diff --git a/BUILD.md b/BUILD.md index e01c514..f317228 100644 --- a/BUILD.md +++ b/BUILD.md @@ -172,6 +172,20 @@ task e2e:setup-env > If changed, you must pass this variable to all subsequent tasks that interact > with the registry to ensure connectivity. +#### Configuring credentials for private registries + +If you need to pull images from a private registry during testing, you can +configure authentication credentials when setting up the environment: + +```bash +REGISTRY_PASSWORD="your-password" task e2e:setup-env \ + REGISTRY_HOST="registry.example.com" \ + REGISTRY_USERNAME="your-username" +``` + +These credentials are configured at the kubelet level, allowing pods to pull +images from the private registry without requiring ImagePullSecrets. + ### Get access to the cluster To interact with the cluster via `kubectl` from your local terminal: @@ -218,6 +232,18 @@ the E2E tests: task e2e:generate-values TARGET="" EXTENSION_IMAGE="" ``` +#### Using private registries + +If your extension image is hosted in a private registry, you can provide authentication +credentials when generating test values: + +```bash +REGISTRY_PASSWORD="your-password" task generate-values \ + TARGET="" \ + EXTENSION_IMAGE="/image:tag" \ + REGISTRY_USERNAME="your-username" +``` + ### Execute End-to-End tests Run the test suite using the internal Kubeconfig. This executes both the @@ -228,6 +254,15 @@ generic tests (global `/test` folder) and extension-specific tests (target task e2e:test TARGET="" KUBECONFIG_PATH="./kubeconfig" ``` +#### Pass arguments to chainsaw test + +It is possible to pass arguments to the [Chainsaw test command](https://kyverno.github.io/chainsaw/latest/reference/commands/chainsaw_test/) by using the `EXTRA_ARGS` +argument, like: + +```bash +task e2e:test TARGET="pgvector" KUBECONFIG_PATH="./kubeconfig" EXTRA_ARGS="--skip-delete,--fail-fast" +``` + --- ### Tear down the local test environment diff --git a/Taskfile.yml b/Taskfile.yml index 79b3dba..41a56e7 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -157,13 +157,17 @@ tasks: prefix: 'generate-values-{{.TARGET}}' vars: EXTENSION_IMAGE: '{{ .EXTENSION_IMAGE| default "" }}' + REGISTRY_USERNAME: '{{ .REGISTRY_USERNAME| default "" }}' env: _EXPERIMENTAL_DAGGER_RUNNER_HOST: '{{ ._EXPERIMENTAL_DAGGER_RUNNER_HOST | default "" }}' + REGISTRY_PASSWORD: '{{ .REGISTRY_PASSWORD | default "" }}' cmds: - echo -e "{{.BLUE}}Generating values for target {{.TARGET}}...{{.NC}}" - > dagger call -sm ./dagger/maintenance/ generate-testing-values - --target {{ .TARGET }} --extension-image="{{ .EXTENSION_IMAGE }}" export --path {{.TARGET}}/values.yaml + --target {{ .TARGET }} --extension-image="{{ .EXTENSION_IMAGE }}" + --registry-username="{{ .REGISTRY_USERNAME }}" --registry-password="env://REGISTRY_PASSWORD" + export --path {{.TARGET}}/values.yaml requires: vars: - name: TARGET @@ -218,6 +222,8 @@ tasks: run: once vars: REGISTRY_DIR: /etc/containerd/certs.d/{{ .REGISTRY_NAME }}:{{ .REGISTRY_INTERNAL_PORT }} + REGISTRY_HOST: '{{ .REGISTRY_HOST | default "" }}' + REGISTRY_USERNAME: '{{ .REGISTRY_USERNAME | default "" }}' DOCKER_SOCKET: sh: docker context inspect -f {{`'{{json .Endpoints.docker.Host}}'`}} $(docker context show) env: @@ -233,6 +239,20 @@ tasks: cat < test "$(dagger call -m github.com/aweris/daggerverse/kind@{{ .DAGGER_KIND_SHA }} @@ -241,7 +261,10 @@ tasks: e2e:install-cnpg: desc: Install CloudNativePG operator in the Kind cluster deps: - - e2e:setup-kind + - task: e2e:setup-kind + vars: + REGISTRY_HOST: '{{ .REGISTRY_HOST }}' + REGISTRY_USERNAME: '{{ .REGISTRY_USERNAME }}' internal: true vars: # renovate: datasource=github-tags depName=cloudnative-pg/cloudnative-pg versioning=semver extractVersion=^v(?\d+\.\d+)\.\d+$ @@ -310,10 +333,16 @@ tasks: e2e:setup-env: desc: Setup E2E environment silent: true + vars: + REGISTRY_HOST: '{{ .REGISTRY_HOST | default "" }}' + REGISTRY_USERNAME: '{{ .REGISTRY_USERNAME | default "" }}' deps: - e2e:start-container-registry - e2e:start-dagger-engine - - e2e:install-cnpg + - task: e2e:install-cnpg + vars: + REGISTRY_HOST: '{{ .REGISTRY_HOST }}' + REGISTRY_USERNAME: '{{ .REGISTRY_USERNAME }}' cmds: - echo -e "{{.GREEN}}--- E2E environment setup complete ---{{.NC}}" @@ -326,11 +355,15 @@ tasks: silent: true vars: EXTENSION_IMAGE: '{{ .EXTENSION_IMAGE| default "" }}' + REGISTRY_USERNAME: '{{ .REGISTRY_USERNAME| default "" }}' + env: + REGISTRY_PASSWORD: '{{ .REGISTRY_PASSWORD | default "" }}' cmds: - task: generate-values vars: _EXPERIMENTAL_DAGGER_RUNNER_HOST: container://{{ .DAGGER_ENGINE_NAME }} TARGET: '{{ .TARGET }}' + REGISTRY_USERNAME: '{{ .REGISTRY_USERNAME }}' EXTENSION_IMAGE: # Rewrite host:port to the internal registry name and port used # within the Docker network. @@ -347,11 +380,12 @@ tasks: silent: true vars: KUBECONFIG_PATH: '{{ .KUBECONFIG_PATH | default "~/.kube/config" }}' + EXTRA_ARGS: '{{ .EXTRA_ARGS | default "" }}' env: _EXPERIMENTAL_DAGGER_RUNNER_HOST: container://{{ .DAGGER_ENGINE_NAME }} cmds: - echo -e "{{ .BLUE }}Testing {{ .TARGET }}...{{ .NC }}" - - dagger call -m ./dagger/maintenance/ test --source . --target {{ .TARGET }} --kubeconfig {{ .KUBECONFIG_PATH }} + - dagger call -m ./dagger/maintenance/ test --source . --target {{ .TARGET }} --kubeconfig {{ .KUBECONFIG_PATH }} --extra-args "{{ .EXTRA_ARGS }}" requires: vars: - name: TARGET @@ -374,8 +408,13 @@ tasks: METADATA_FILE: "{{ .TARGET }}/bake-metadata.json" KUBECONFIG_PATH: "./kubeconfig" DISTRO: '{{ .DISTRO | default "trixie" }}' + REGISTRY_HOST: '{{ .REGISTRY_HOST | default "" }}' + REGISTRY_USERNAME: '{{ .REGISTRY_USERNAME | default "" }}' cmds: - task: e2e:setup-env + vars: + REGISTRY_HOST: '{{ .REGISTRY_HOST }}' + REGISTRY_USERNAME: '{{ .REGISTRY_USERNAME }}' - task: bake vars: PUSH: "true" @@ -385,6 +424,7 @@ tasks: - task: e2e:generate-values vars: TARGET: "{{ .TARGET }}" + REGISTRY_USERNAME: '{{ .REGISTRY_USERNAME }}' EXTENSION_IMAGE: sh: > jq -r --arg distro "{{ .DISTRO }}" diff --git a/dagger/maintenance/image.go b/dagger/maintenance/image.go index 86777e9..26b633a 100644 --- a/dagger/maintenance/image.go +++ b/dagger/maintenance/image.go @@ -2,10 +2,14 @@ package main import ( "bytes" + "context" "fmt" "regexp" "strconv" + "dagger/maintenance/internal/dagger" + + "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/crane" "github.com/google/go-containerregistry/pkg/name" containerregistryv1 "github.com/google/go-containerregistry/pkg/v1" @@ -24,14 +28,31 @@ var SupportedDistributions = []string{ } // getImageAnnotations returns the OCI annotations given an image ref. -func getImageAnnotations(imageRef string) (map[string]string, error) { +// If username and password are provided, they will be used for registry authentication. +func getImageAnnotations(ctx context.Context, imageRef string, username string, password *dagger.Secret) (map[string]string, error) { // Setting Insecure option to allow fetching images from local registries with no TLS ref, err := name.ParseReference(imageRef, name.Insecure) if err != nil { return nil, err } - head, err := remote.Get(ref) + var opts []remote.Option + if password != nil && username != "" { + plainPassword, err := password.Plaintext(ctx) + if err != nil { + return nil, fmt.Errorf("failed to read registry password: %w", err) + } + + if plainPassword != "" { + auth := authn.FromConfig(authn.AuthConfig{ + Username: username, + Password: plainPassword, + }) + opts = append(opts, remote.WithAuth(auth)) + } + } + + head, err := remote.Get(ref, opts...) if err != nil { return nil, err } diff --git a/dagger/maintenance/main.go b/dagger/maintenance/main.go index 6ef5b9b..f56be3c 100644 --- a/dagger/maintenance/main.go +++ b/dagger/maintenance/main.go @@ -147,6 +147,12 @@ func (m *Maintenance) GenerateTestingValues( // URL reference to the extension image to test [REPOSITORY[:TAG]] // +optional extensionImage string, + // Registry username for authentication (optional) + // +optional + registryUsername string, + // Registry password or token for authentication (optional) + // +optional + registryPassword *dagger.Secret, ) (*dagger.File, error) { metadata, err := parseExtensionMetadata(ctx, target) if err != nil { @@ -161,7 +167,7 @@ func (m *Maintenance) GenerateTestingValues( } } - annotations, err := getImageAnnotations(targetExtensionImage) + annotations, err := getImageAnnotations(ctx, targetExtensionImage, registryUsername, registryPassword) if err != nil { return nil, err } @@ -180,7 +186,7 @@ func (m *Maintenance) GenerateTestingValues( targetExtensionImage) } - extensionInfos, err := generateTestingValuesExtensions(ctx, source, metadata, targetExtensionImage, version) + extensionInfos, err := generateTestingValuesExtensions(ctx, source, metadata, targetExtensionImage, version, registryUsername, registryPassword) if err != nil { return nil, err } @@ -348,6 +354,9 @@ func (m *Maintenance) Test( // renovate: datasource=docker depName=kyverno/chainsaw packageName=ghcr.io/kyverno/chainsaw versioning=docker // +default="ghcr.io/kyverno/chainsaw:v0.2.14@sha256:c703e4d4ce7b89c5121fe957ab89b6e2d33f91fd15f8274a9f79ca1b2ba8ecef" chainsawImage string, + // Additional arguments to pass to Chainsaw test command + // +optional + extraArgs []string, ) error { extDir := source if target != "all" { @@ -392,8 +401,15 @@ func (m *Maintenance) Test( WithFile("/etc/kubeconfig/config", kubeconfig). WithEnvVariable("KUBECONFIG", "/etc/kubeconfig/config") + chainsawTestArgs := []string{ + "test", + "./test", + "--values", path.Join(extName, valuesFile), + } + chainsawTestArgs = append(chainsawTestArgs, extraArgs...) + _, err = ctr.WithExec( - []string{"test", "./test", "--values", path.Join(extName, valuesFile)}, + chainsawTestArgs, dagger.ContainerWithExecOpts{ UseEntrypoint: true, }). @@ -410,8 +426,16 @@ func (m *Maintenance) Test( if !hasIndividualTests { continue } + + chainsawTestArgs = []string{ + "test", + path.Join(extName, "test"), + "--values", path.Join(extName, valuesFile), + } + chainsawTestArgs = append(chainsawTestArgs, extraArgs...) + _, err = ctr.WithExec( - []string{"test", path.Join(extName, "test"), "--values", path.Join(extName, valuesFile)}, + chainsawTestArgs, dagger.ContainerWithExecOpts{ UseEntrypoint: true, }). diff --git a/dagger/maintenance/testingvalues.go b/dagger/maintenance/testingvalues.go index 0ff3065..4fa9994 100644 --- a/dagger/maintenance/testingvalues.go +++ b/dagger/maintenance/testingvalues.go @@ -36,7 +36,8 @@ type testingExtensionInfo struct { CreateExtension bool } -func generateTestingValuesExtensions(ctx context.Context, source *dagger.Directory, metadata *extensionMetadata, extensionImage string, version string) ([]*testingExtensionInfo, error) { +func generateTestingValuesExtensions(ctx context.Context, source *dagger.Directory, metadata *extensionMetadata, + extensionImage string, version string, registryUsername string, registryPassword *dagger.Secret) ([]*testingExtensionInfo, error) { var out []*testingExtensionInfo configuration, err := generateExtensionConfiguration(metadata, extensionImage) if err != nil { @@ -67,7 +68,7 @@ func generateTestingValuesExtensions(ctx context.Context, source *dagger.Directo return nil, err } - depAnnotations, err := getImageAnnotations(depConfiguration.ImageVolumeSource.Reference) + depAnnotations, err := getImageAnnotations(ctx, depConfiguration.ImageVolumeSource.Reference, registryUsername, registryPassword) if err != nil { return nil, err }