From 5e77f040ae70c87619e9cc2b36b549356a01e24b Mon Sep 17 00:00:00 2001 From: Dipti Pai Date: Fri, 13 Sep 2024 14:09:50 +0000 Subject: [PATCH] Add new integration tests for Azure OIDC for git repositories Signed-off-by: Dipti Pai Add new integration tests for Azure OIDC for git repositories Signed-off-by: Dipti Pai Signed-off-by: Sunny --- oci/tests/integration/.env.sample | 8 +- oci/tests/integration/Makefile | 9 +- oci/tests/integration/README.md | 53 +++- oci/tests/integration/aws_test.go | 15 ++ oci/tests/integration/azure_test.go | 153 +++++++++++- oci/tests/integration/gcp_test.go | 15 ++ oci/tests/integration/git_test.go | 48 ++++ oci/tests/integration/go.mod | 29 ++- oci/tests/integration/go.sum | 48 +++- .../{repo_list_test.go => job_test.go} | 51 +--- oci/tests/integration/oci_test.go | 100 ++++++++ oci/tests/integration/suite_test.go | 227 ++++++++++++++---- oci/tests/integration/terraform/azure/main.tf | 20 +- .../integration/terraform/azure/outputs.tf | 12 + .../integration/terraform/azure/variables.tf | 18 ++ .../integration/terraform/azure/version.tf | 7 + oci/tests/integration/testapp/main.go | 76 +++++- oci/tests/integration/util_test.go | 119 +++++++++ 18 files changed, 886 insertions(+), 122 deletions(-) create mode 100644 oci/tests/integration/git_test.go rename oci/tests/integration/{repo_list_test.go => job_test.go} (64%) create mode 100644 oci/tests/integration/oci_test.go create mode 100644 oci/tests/integration/terraform/azure/version.tf create mode 100644 oci/tests/integration/util_test.go diff --git a/oci/tests/integration/.env.sample b/oci/tests/integration/.env.sample index e50eb0c1..12357f86 100644 --- a/oci/tests/integration/.env.sample +++ b/oci/tests/integration/.env.sample @@ -11,12 +11,14 @@ # export TF_VAR_rand=${RANDOM} ## Azure +# export ARM_SUBSCRIPTION_ID= +# export TF_VAR_azuredevops_org= +# export TF_VAR_azuredevops_pat= # export TF_VAR_azure_location=eastus ## Set the following only when authenticating using Service Principal (suited ## for CI environment). # export ARM_CLIENT_ID= # export ARM_CLIENT_SECRET= -# export ARM_SUBSCRIPTION_ID= # export ARM_TENANT_ID= ## GCP @@ -48,3 +50,7 @@ # export TF_VAR_wi_k8s_sa_name=test-workload-id # export TF_VAR_wi_k8s_sa_ns=default # export TF_VAR_enable_wi=true + +## Test Configuration variables +# export TF_VAR_enable_git=true +# export TF_VAR_enable_oci=true diff --git a/oci/tests/integration/Makefile b/oci/tests/integration/Makefile index 2183bfd6..dfe5954f 100644 --- a/oci/tests/integration/Makefile +++ b/oci/tests/integration/Makefile @@ -1,4 +1,5 @@ GO_TEST_ARGS ?= +GO_TEST_PREFIX ?= PROVIDER_ARG ?= TEST_TIMEOUT ?= 30m GOARCH ?= amd64 @@ -15,7 +16,7 @@ docker-build: app test: docker image inspect $(TEST_IMG) >/dev/null - TEST_IMG=$(TEST_IMG) go test -timeout $(TEST_TIMEOUT) -v ./ $(GO_TEST_ARGS) $(PROVIDER_ARG) --tags=integration + TEST_IMG=$(TEST_IMG) go test -timeout $(TEST_TIMEOUT) -v ./ -run "^$(GO_TEST_PREFIX).*" $(GO_TEST_ARGS) $(PROVIDER_ARG) --tags=integration test-aws: $(MAKE) test PROVIDER_ARG="-provider aws" @@ -23,6 +24,12 @@ test-aws: test-azure: $(MAKE) test PROVIDER_ARG="-provider azure" +test-azure-git: + $(MAKE) test PROVIDER_ARG="-provider azure" GO_TEST_PREFIX="TestGit" + +test-azure-oci: + $(MAKE) test PROVIDER_ARG="-provider azure" GO_TEST_PREFIX="TestOci" + test-gcp: $(MAKE) test PROVIDER_ARG="-provider gcp" diff --git a/oci/tests/integration/README.md b/oci/tests/integration/README.md index 9e521945..105d3da2 100644 --- a/oci/tests/integration/README.md +++ b/oci/tests/integration/README.md @@ -1,7 +1,7 @@ -# OCI integration test +# Integration tests -OCI integration test uses a test application(`testapp/`) to test the -oci package against each of the supported cloud providers. +Integration tests uses a test application(`testapp/`) to test the +oci and git package against each of the supported cloud providers. **NOTE:** Tests in this package aren't run automatically by the `test-*` make target at the root of `fluxcd/pkg` repo. These tests are more complicated than @@ -16,7 +16,7 @@ runs the test app as a batch job which tries to log in and list tags from the test registry repository. A successful job indicates successful test. If the job fails, the test fails. -Logs of a successful job run: +Logs of a successful job run for oci: ```console $ kubectl logs test-job-93tbl-4jp2r 2022/07/28 21:59:06 repo: xxx.dkr.ecr.us-east-2.amazonaws.com/test-repo-flux-test-heroic-ram @@ -25,6 +25,25 @@ $ kubectl logs test-job-93tbl-4jp2r 2022/07/28 21:59:06 tags: [v0.1.4 v0.1.3 v0.1.0 v0.1.2] ``` +Logs of a successful job run for git: +```console +$ kubectl logs test-job-dzful-jrcqw +2024/08/27 22:28:22 Successfully cloned repository +2024/08/27 22:28:22 apiVersion: v1 +kind: ConfigMap +metadata: + name: foobar +2024/08/27 22:28:22 Keys in cache 0 [https://dev.azure.com/xxx/fluxProjpopularosheepdog/_git/fluxRepopopularosheepdog] +2024/08/27 22:28:22 Cache entry expiration 2024-08-28 22:28:21.335223377 +0000 UTC +2024/08/27 22:28:22 Successfully cloned repository +2024/08/27 22:28:22 apiVersion: v1 +kind: ConfigMap +metadata: + name: foobar +2024/08/27 22:28:22 Keys in cache 1 [https://dev.azure.com/xxx/fluxProjpopularosheepdog/_git/fluxRepopopularosheepdog] +2024/08/27 22:28:22 Cache entry expiration 2024-08-28 22:28:21.335223377 +0000 UTC +``` + ## Requirements ### Amazon Web Services @@ -316,6 +335,18 @@ module "aws_gh_actions" { workloads to access ACR. - Azure CLI, need to be logged in using `az login` as a User (not a Service Principal). +- An Azure DevOps organization [connected to Microsoft + Entra](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/connect-organization-to-azure-ad?view=azure-devops), + personal access token for accessing repositories within the organization. The + scope required for the personal access token is: + - Project and Team - read, write and manage access + - Member Entitlement Management (Read & Write) + - Code - Full + - Please take a look at the [terraform + provider](https://registry.terraform.io/providers/microsoft/azuredevops/latest/docs/guides/authenticating_using_the_personal_access_token#create-a-personal-access-token) + for more explanation. + - A valid Azure devops configuration is needed even if git is not being + tested. **NOTE:** To use Service Principal (for example in CI environment), set the `ARM-*` variables in `.env`, source it and authenticate Azure CLI with: @@ -520,9 +551,10 @@ Run the test with `make test-*`, setting the test app image with variable $ make test-azure make test PROVIDER_ARG="-provider azure" docker image inspect fluxcd/testapp:test >/dev/null -TEST_IMG=fluxcd/testapp:test go test -timeout 30m -v ./ -verbose -retain -provider azure --tags=integration -2022/07/29 02:06:51 Terraform binary: /usr/bin/terraform -2022/07/29 02:06:51 Init Terraform +TEST_IMG=fluxcd/testapp:test go test -timeout 30m -v ./ -run "^.*" -provider azure --tags=integration +2024/08/26 23:39:13 Terraform binary: /snap/bin/terraform +2024/08/26 23:39:13 Init Terraform +2024/08/26 23:39:15 Applying Terraform ... ``` @@ -532,7 +564,10 @@ the resources don't get deleted, the `make destroy-*` commands can be run for the respective provider. This will run terraform destroy in the respective provider's terraform configuration directory. This can be used to quickly destroy the infrastructure without going through the provision-test-destroy -steps. +steps. There is a known issue with Azure user not getting cleaned up if the +infrastructure is retained and destroy is used for cleanup. The workaround is to +manually delete the user from Azure DevOps Organization +Settings->Users page. ## Workload Identity @@ -547,6 +582,8 @@ export TF_VAR_enable_wi= They have been included in the `.env.sample` and you can simply uncomment it. +The git integration tests require workload identity to be enabled. + ## Debugging the tests For debugging environment provisioning, enable verbose output with `-verbose` diff --git a/oci/tests/integration/aws_test.go b/oci/tests/integration/aws_test.go index 640a514b..d3e6219c 100644 --- a/oci/tests/integration/aws_test.go +++ b/oci/tests/integration/aws_test.go @@ -98,3 +98,18 @@ func getWISAAnnotationsAWS(output map[string]*tfjson.StateOutput) (map[string]st eksRoleArnAnnotation: iamARN, }, nil } + +// When implemented, getGitTestConfigAws would return the git-specific test config for AWS +func getGitTestConfigAWS(outputs map[string]*tfjson.StateOutput) (*gitTestConfig, error) { + return nil, fmt.Errorf("NotImplemented for AWS") +} + +// When implemented, grantPermissionsToGitRepositoryAWS would grant the required permissions to AWS CodeCommit repository +func grantPermissionsToGitRepositoryAWS(ctx context.Context, cfg *gitTestConfig, output map[string]*tfjson.StateOutput) error { + return fmt.Errorf("NotImplemented for AWS") +} + +// When implemented, revokePermissionsToGitRepositoryAWS would revoke the permissions granted to AWS CodeCommit repository +func revokePermissionsToGitRepositoryAWS(ctx context.Context, cfg *gitTestConfig, outputs map[string]*tfjson.StateOutput) error { + return fmt.Errorf("NotImplemented for AWS") +} diff --git a/oci/tests/integration/azure_test.go b/oci/tests/integration/azure_test.go index 104ca721..e445944c 100644 --- a/oci/tests/integration/azure_test.go +++ b/oci/tests/integration/azure_test.go @@ -22,10 +22,19 @@ package integration import ( "context" "fmt" + "log" + "os" + "strings" + "time" - tfjson "github.com/hashicorp/terraform-json" - + "github.com/fluxcd/pkg/git" "github.com/fluxcd/test-infra/tftestenv" + "github.com/google/uuid" + tfjson "github.com/hashicorp/terraform-json" + "github.com/microsoft/azure-devops-go-api/azuredevops/v7" + "github.com/microsoft/azure-devops-go-api/azuredevops/v7/graph" + "github.com/microsoft/azure-devops-go-api/azuredevops/v7/licensing" + "github.com/microsoft/azure-devops-go-api/azuredevops/v7/memberentitlementmanagement" ) const ( @@ -81,3 +90,143 @@ func getWISAAnnotationsAzure(output map[string]*tfjson.StateOutput) (map[string] azureWIClientIdAnnotation: clientID, }, nil } + +// Give managed identity permissions on the azure devops project. Refer +// https://learn.microsoft.com/en-us/rest/api/azure/devops/memberentitlementmanagement/service-principal-entitlements/add?view=azure-devops-rest-7.1&tabs=HTTP. +// This can be moved to terraform if/when this PR completes - +// https://github.com/microsoft/terraform-provider-azuredevops/pull/1028 +// Returns a string representing the uuid of the entity that was granted permissions +func grantPermissionsToGitRepositoryAzure(ctx context.Context, cfg *gitTestConfig, outputs map[string]*tfjson.StateOutput) error { + projectId := outputs["azure_devops_project_id"].Value.(string) + wiObjectId := outputs["workload_identity_object_id"].Value.(string) + var servicePrincipalID string + + // Create a connection to the organization and create a new client + connection := azuredevops.NewPatConnection(fmt.Sprintf("https://dev.azure.com/%s", cfg.organization), cfg.gitPat) + client, err := memberentitlementmanagement.NewClient(ctx, connection) + if err != nil { + return err + } + + uuid, err := uuid.Parse(projectId) + if err != nil { + return err + } + origin := "AAD" + kind := "servicePrincipal" + servicePrincipal := memberentitlementmanagement.ServicePrincipalEntitlement{ + AccessLevel: &licensing.AccessLevel{ + AccountLicenseType: &licensing.AccountLicenseTypeValues.Express, + }, + ProjectEntitlements: &[]memberentitlementmanagement.ProjectEntitlement{ + { + ProjectRef: &memberentitlementmanagement.ProjectRef{ + Id: &uuid, + }, + Group: &memberentitlementmanagement.Group{ + GroupType: &memberentitlementmanagement.GroupTypeValues.ProjectContributor, + }, + }, + }, + ServicePrincipal: &graph.GraphServicePrincipal{ + Origin: &origin, + OriginId: &wiObjectId, + SubjectKind: &kind, + }, + } + + // First request to add new user fails, second request succeeds, add a retry + retryAttempts := 2 + retryDelay := 1 * time.Second // 1 seconds delay + attempts := 0 + for attempts < retryAttempts { + attempts++ + responseValue, err := client.AddServicePrincipalEntitlement(ctx, memberentitlementmanagement.AddServicePrincipalEntitlementArgs{ServicePrincipalEntitlement: &servicePrincipal}) + if err != nil { + return err + } + + if !*responseValue.OperationResult.IsSuccess { + errMsg := getServicePrincipalEntitlementAPIErrorMessage(*responseValue.OperationResult) + if strings.Contains(errMsg, "VS403283: Could not add user") { + log.Println("Retryable error encountered", errMsg) + time.Sleep(retryDelay) + continue + } else { + return fmt.Errorf(errMsg) + } + } + uuid := responseValue.OperationResult.ServicePrincipalId + servicePrincipalID = uuid.String() + break + } + + cfg.permissionID = servicePrincipalID + log.Println("Added service principal entitlement!") + + return nil +} + +func getServicePrincipalEntitlementAPIErrorMessage(operationResult memberentitlementmanagement.ServicePrincipalEntitlementOperationResult) string { + errMsg := "Unknown API error" + if operationResult.Errors != nil && len(*operationResult.Errors) > 0 { + var errorMessages []string + for _, err := range *operationResult.Errors { + errorMessages = append(errorMessages, fmt.Sprintf("(%v) %s", *err.Key, *err.Value)) + } + errMsg = strings.Join(errorMessages, "\n") + } + return errMsg +} + +// revokePermissionsToGitRepositoryAzure deletes the managed identity from users list in the organization. +func revokePermissionsToGitRepositoryAzure(ctx context.Context, cfg *gitTestConfig, outputs map[string]*tfjson.StateOutput) error { + uuid, err := uuid.Parse(cfg.permissionID) + if err != nil { + return err + } + + // Create a connection to the organization and create a new client + connection := azuredevops.NewPatConnection(fmt.Sprintf("https://dev.azure.com/%s", cfg.organization), cfg.gitPat) + client, err := memberentitlementmanagement.NewClient(ctx, connection) + if err != nil { + return err + } + + err = client.DeleteServicePrincipalEntitlement(ctx, memberentitlementmanagement.DeleteServicePrincipalEntitlementArgs{ServicePrincipalId: &uuid}) + if err != nil { + log.Fatal(err) + } + cfg.permissionID = "" + + return nil +} + +// getGitTestConfigAzure returns the test config used to setup the git repository +func getGitTestConfigAzure(outputs map[string]*tfjson.StateOutput) (*gitTestConfig, error) { + config := &gitTestConfig{ + defaultGitTransport: git.HTTP, + gitUsername: git.DefaultPublicKeyAuthUser, + organization: os.Getenv(envVarAzureDevOpsOrg), + gitPat: os.Getenv(envVarAzureDevOpsPAT), + applicationRepository: outputs["git_repo_url"].Value.(string), + } + + opts, err := getAuthOpts(config.applicationRepository, map[string][]byte{ + "password": []byte(config.gitPat), + "username": []byte(git.DefaultPublicKeyAuthUser), + }) + if err != nil { + return nil, err + } + config.defaultAuthOpts = opts + + parts := strings.Split(config.applicationRepository, "@") + // Check if the URL contains the "@" symbol + if len(parts) > 1 { + // Reconstruct the URL without the username + config.applicationRepositoryWithoutUser = "https://" + parts[1] + } + + return config, nil +} diff --git a/oci/tests/integration/gcp_test.go b/oci/tests/integration/gcp_test.go index a92b5791..77d2b213 100644 --- a/oci/tests/integration/gcp_test.go +++ b/oci/tests/integration/gcp_test.go @@ -90,3 +90,18 @@ func getWISAAnnotationsGCP(output map[string]*tfjson.StateOutput) (map[string]st gcpIAMAnnotation: saEmail, }, nil } + +// When implemented, getGitTestConfigGCP would return the git-specific test config for GCP +func getGitTestConfigGCP(outputs map[string]*tfjson.StateOutput) (*gitTestConfig, error) { + return nil, fmt.Errorf("NotImplemented for GCP") +} + +// When implemented, grantPermissionsToGitRepositoryGCP would grant the required permissions to Google cloud source repositories +func grantPermissionsToGitRepositoryGCP(ctx context.Context, cfg *gitTestConfig, output map[string]*tfjson.StateOutput) error { + return fmt.Errorf("NotImplemented for GCP") +} + +// When implemented, revokePermissionsToGitRepositoryGCP would revoke the permissions granted to Google cloud source repositories +func revokePermissionsToGitRepositoryGCP(ctx context.Context, cfg *gitTestConfig, outputs map[string]*tfjson.StateOutput) error { + return fmt.Errorf("NotImplemented for GCP") +} diff --git a/oci/tests/integration/git_test.go b/oci/tests/integration/git_test.go new file mode 100644 index 00000000..846a3dcf --- /dev/null +++ b/oci/tests/integration/git_test.go @@ -0,0 +1,48 @@ +//go:build integration +// +build integration + +/* +Copyright 2022 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration + +import ( + "context" + "fmt" + "testing" +) + +func TestGitCloneUsingProvider(t *testing.T) { + if !enableGit { + t.Skip("Skipping test, enable git in env, supported providers ", supportedGitProviders) + } + + ctx := context.TODO() + tmpDir := t.TempDir() + + if err := setUpGitRepository(ctx, tmpDir); err != nil { + t.Fatalf("failed setting up GitRepository: %v", err) + } + t.Run("Git oidc credential test", func(t *testing.T) { + args := []string{ + "-category=git", + "-oidc-login=true", + fmt.Sprintf("-provider=%s", *targetProvider), + fmt.Sprintf("-repo=%s", testGitCfg.applicationRepositoryWithoutUser), + } + testjobExecutionWithArgs(t, args) + }) +} diff --git a/oci/tests/integration/go.mod b/oci/tests/integration/go.mod index 242bad54..18df1864 100644 --- a/oci/tests/integration/go.mod +++ b/oci/tests/integration/go.mod @@ -3,17 +3,26 @@ module github.com/fluxcd/pkg/oci/tests/integration go 1.22.4 replace ( + github.com/fluxcd/pkg/auth => ../../../auth github.com/fluxcd/pkg/cache => ../../../cache + github.com/fluxcd/pkg/git => ../../../git + github.com/fluxcd/pkg/git/gogit => ../../../git/gogit github.com/fluxcd/pkg/oci => ../../ ) require ( + github.com/fluxcd/pkg/auth v0.0.0-00010101000000-000000000000 github.com/fluxcd/pkg/cache v0.0.3 + github.com/fluxcd/pkg/git v0.20.0 + github.com/fluxcd/pkg/git/gogit v0.19.0 github.com/fluxcd/pkg/oci v0.40.0 github.com/fluxcd/test-infra/tftestenv v0.0.0-20240805120810-5b91964f964f + github.com/go-git/go-git/v5 v5.12.0 github.com/google/go-containerregistry v0.20.2 + github.com/google/uuid v1.6.0 github.com/hashicorp/terraform-exec v0.21.0 github.com/hashicorp/terraform-json v0.22.1 + github.com/microsoft/azure-devops-go-api/azuredevops/v7 v7.1.0 github.com/onsi/gomega v1.34.1 k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 @@ -21,10 +30,13 @@ require ( ) require ( + dario.cat/mergo v1.0.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/aws/aws-sdk-go-v2 v1.30.4 // indirect @@ -46,16 +58,22 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect + github.com/cyphar/filepath-securejoin v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/cli v27.1.2+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fluxcd/cli-utils v0.36.0-flux.9 // indirect + github.com/fluxcd/pkg/ssh v0.14.0 // indirect + github.com/fluxcd/pkg/version v0.4.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-errors/errors v1.5.1 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect @@ -69,16 +87,17 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/hc-install v0.6.4 // indirect github.com/imdario/mergo v0.3.15 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -89,16 +108,20 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.20.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/skeema/knownhosts v1.3.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/vbatts/tar-split v0.11.3 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/zclconf/go-cty v1.14.4 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect @@ -110,14 +133,16 @@ require ( golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect + golang.org/x/sys v0.25.0 // indirect golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.6.0 // indirect + golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect diff --git a/oci/tests/integration/go.sum b/oci/tests/integration/go.sum index a74c27a5..381141e8 100644 --- a/oci/tests/integration/go.sum +++ b/oci/tests/integration/go.sum @@ -15,12 +15,19 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8= github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= github.com/aws/aws-sdk-go-v2/config v1.27.29 h1:+ZPKb3u9Up4KZWLGTtpTmC5T3XmRD1ZQ8XQjRCHUvJw= @@ -80,6 +87,8 @@ github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBi github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= +github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380 h1:1NyRx2f4W4WBRyg0Kys0ZbaNmDDzZ2R/C7DTi+bbsJ0= +github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380/go.mod h1:thX175TtLTzLj3p7N/Q9IiKZ7NF+p72cvL91emV0hzo= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -94,18 +103,30 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fluxcd/cli-utils v0.36.0-flux.9 h1:RITKdwIAqT3EFKXl7B91mj6usVjxcy7W8PJZlxqUa84= github.com/fluxcd/cli-utils v0.36.0-flux.9/go.mod h1:q6lXQpbAlrZmTB4Qe5oAENkv0y2kwMWcqTMDHrRo2Is= +github.com/fluxcd/gitkit v0.6.0 h1:iNg5LTx6ePo+Pl0ZwqHTAkhbUHxGVSY3YCxCdw7VIFg= +github.com/fluxcd/gitkit v0.6.0/go.mod h1:svOHuKi0fO9HoawdK4HfHAJJseZDHHjk7I3ihnCIqNo= +github.com/fluxcd/pkg/gittestserver v0.13.0 h1:6rvD9Z7+4zBcNT+LK0z4H0z6mDaw1Zd8ZaLh/dw8dzI= +github.com/fluxcd/pkg/gittestserver v0.13.0/go.mod h1:LDw32Wo9mTmKNmJq4g7LRVBqPXlpMIWFBDOrRRh/+As= +github.com/fluxcd/pkg/ssh v0.14.0 h1:rkcUwEZiwNoHq8oGOf/THV5sf9LBbXOoJgOt+6+bU34= +github.com/fluxcd/pkg/ssh v0.14.0/go.mod h1:1USgRvaaayJfzybQaCIAUn2e8LPsLe601Rec7Y8KQQE= +github.com/fluxcd/pkg/version v0.4.0 h1:3F6oeIZ+ug/f7pALIBhcUhfURel37EPPOn7nsGfsnOg= +github.com/fluxcd/pkg/version v0.4.0/go.mod h1:izVsSDxac81qWRmpOL9qcxZYx+zAN1ajoP5SidGP6PA= github.com/fluxcd/test-infra/tftestenv v0.0.0-20240805120810-5b91964f964f h1:P2bWQKTeotAzOeLLXitUHy1RrKn2Zl8tk4IWr2XY/10= github.com/fluxcd/test-infra/tftestenv v0.0.0-20240805120810-5b91964f964f/go.mod h1:liFlLEXgambGVdWSJ4JzbIHf1Vjpp1HwUyPazPIVZug= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= +github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= @@ -121,6 +142,8 @@ github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogB github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= @@ -161,6 +184,7 @@ github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2 github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -201,6 +225,7 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -214,6 +239,8 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/microsoft/azure-devops-go-api/azuredevops/v7 v7.1.0 h1:mmJCWLe63QvybxhW1iBmQWEaCKdc4SKgALfTNZ+OphU= +github.com/microsoft/azure-devops-go-api/azuredevops/v7 v7.1.0/go.mod h1:mDunUZ1IUJdJIRHvFb+LPBUtxe3AYB5MI6BMXNg8194= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -268,11 +295,12 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= -github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= +github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -282,7 +310,9 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -313,6 +343,7 @@ go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -333,6 +364,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -348,18 +380,24 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= @@ -399,6 +437,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= @@ -407,6 +446,7 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/oci/tests/integration/repo_list_test.go b/oci/tests/integration/job_test.go similarity index 64% rename from oci/tests/integration/repo_list_test.go rename to oci/tests/integration/job_test.go index 459cf566..dbb76b06 100644 --- a/oci/tests/integration/repo_list_test.go +++ b/oci/tests/integration/job_test.go @@ -21,8 +21,6 @@ package integration import ( "context" - "fmt" - "strings" "testing" . "github.com/onsi/gomega" @@ -32,51 +30,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func TestImageRepositoryListTags(t *testing.T) { - for name, repo := range testRepos { - t.Run(name, func(t *testing.T) { - args := []string{fmt.Sprintf("-repo=%s", repo)} - testImageRepositoryListTags(t, args) - }) - } -} - -func TestRepositoryRootLoginListTags(t *testing.T) { - for name, repo := range testRepos { - t.Run(name, func(t *testing.T) { - parts := strings.SplitN(repo, "/", 2) - args := []string{ - fmt.Sprintf("-registry=%s", parts[0]), - fmt.Sprintf("-repo=%s", parts[1]), - } - testImageRepositoryListTags(t, args) - }) - } -} - -func TestOIDCLoginListTags(t *testing.T) { - for name, repo := range testRepos { - t.Run(name, func(t *testing.T) { - // Registry only. - parts := strings.SplitN(repo, "/", 2) - args := []string{ - "-oidc-login=true", - fmt.Sprintf("-registry=%s", parts[0]), - fmt.Sprintf("-repo=%s", parts[1]), - } - testImageRepositoryListTags(t, args) - - // Registry + repo. - args = []string{ - "-oidc-login=true", - fmt.Sprintf("-repo=%s", repo), - } - testImageRepositoryListTags(t, args) - }) - } -} - -func testImageRepositoryListTags(t *testing.T, args []string) { +func testjobExecutionWithArgs(t *testing.T, args []string) { + t.Helper() g := NewWithT(t) ctx := context.TODO() @@ -107,10 +62,12 @@ func testImageRepositoryListTags(t *testing.T, args []string) { key := client.ObjectKeyFromObject(job) g.Expect(testEnv.Client.Create(ctx, job)).To(Succeed()) + defer func() { background := metav1.DeletePropagationBackground g.Expect(testEnv.Client.Delete(ctx, job, &client.DeleteOptions{PropagationPolicy: &background})).To(Succeed()) }() + g.Eventually(func() bool { if err := testEnv.Client.Get(ctx, key, job); err != nil { return false diff --git a/oci/tests/integration/oci_test.go b/oci/tests/integration/oci_test.go new file mode 100644 index 00000000..49c59840 --- /dev/null +++ b/oci/tests/integration/oci_test.go @@ -0,0 +1,100 @@ +//go:build integration +// +build integration + +/* +Copyright 2022 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration + +import ( + "fmt" + "strings" + "testing" +) + +func TestOciImageRepositoryListTags(t *testing.T) { + if !enableOci { + t.Skip("Skipping test as oci is not enabled in env") + } + + if len(testRepos) == 0 { + t.Fatalf("expected testRepos to be set") + } + + for name, repo := range testRepos { + t.Run(name, func(t *testing.T) { + args := []string{ + "-category=oci", + fmt.Sprintf("-repo=%s", repo), + } + testjobExecutionWithArgs(t, args) + }) + } +} + +func TestOciRepositoryRootLoginListTags(t *testing.T) { + if !enableOci { + t.Skip("Skipping test as oci is not enabled in env") + } + + if len(testRepos) == 0 { + t.Fatalf("expected testRepos to be set") + } + + for name, repo := range testRepos { + t.Run(name, func(t *testing.T) { + parts := strings.SplitN(repo, "/", 2) + args := []string{ + "-category=oci", + fmt.Sprintf("-registry=%s", parts[0]), + fmt.Sprintf("-repo=%s", parts[1]), + } + testjobExecutionWithArgs(t, args) + }) + } +} + +func TestOciOIDCLoginListTags(t *testing.T) { + if !enableOci { + t.Skip("Skipping test as oci is not enabled in env") + } + + if len(testRepos) == 0 { + t.Fatalf("expected testRepos to be set") + } + + for name, repo := range testRepos { + t.Run(name, func(t *testing.T) { + // Registry only. + parts := strings.SplitN(repo, "/", 2) + args := []string{ + "-category=oci", + "-oidc-login=true", + fmt.Sprintf("-registry=%s", parts[0]), + fmt.Sprintf("-repo=%s", parts[1]), + } + testjobExecutionWithArgs(t, args) + + // Registry + repo. + args = []string{ + "-category=oci", + "-oidc-login=true", + fmt.Sprintf("-repo=%s", repo), + } + testjobExecutionWithArgs(t, args) + }) + } +} diff --git a/oci/tests/integration/suite_test.go b/oci/tests/integration/suite_test.go index 2ddc566e..60166fba 100644 --- a/oci/tests/integration/suite_test.go +++ b/oci/tests/integration/suite_test.go @@ -37,6 +37,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "github.com/fluxcd/pkg/git" "github.com/fluxcd/test-infra/tftestenv" ) @@ -63,14 +64,31 @@ const ( // envVarWISANamespace is the name of the terraform environment variable containing // the service account namespace used for workload identity. envVarWISANamespace = "TF_VAR_wi_k8s_sa_ns" + + // envVarAzureDevOpsOrg is the name of the terraform environment variable + // containing the Azure DevOps organization name. + envVarAzureDevOpsOrg = "TF_VAR_azuredevops_org" + + // envVarAzureDevOpsPAT is the name of the terraform environment variable + // containing the Azure DevOps personal access token. + envVarAzureDevOpsPAT = "TF_VAR_azuredevops_pat" ) var ( + // supportedOciProviders are the providers supported by the test. + supportedOciProviders = []string{"aws", "azure", "gcp"} + // supportedProviders are the providers supported by the test. - supportedProviders = []string{"aws", "azure", "gcp"} + supportedGitProviders = []string{"azure"} // targetProvider is the name of the kubernetes provider to test against. - targetProvider = flag.String("provider", "", fmt.Sprintf("name of the provider %v", supportedProviders)) + targetProvider = flag.String("provider", "", fmt.Sprintf("name of the provider %v for oci, %v for git", supportedOciProviders, supportedGitProviders)) + + // enableOci is set to true when oci is enabled in env and a supported provider is specified. + enableOci bool + + // enableGit is set to true when oci is enabled in env and a supported provider is specified. + enableGit bool // retain flag to prevent destroy and retaining the created infrastructure. retain = flag.Bool("retain", false, "retain the infrastructure for debugging purposes") @@ -103,11 +121,14 @@ var ( testAppImage string // wiServiceAccount is the name of the service account that will be created and annotated for workload - // identity. It is set from the terraform variable (`TF_VAR_k8s_serviceaccount_name`) + // identity. It is set from the terraform variable (`TF_VAR_wi_k8s_sa_name`) wiServiceAccount string - // enableWI is set to true when the TF_vAR_enable_wi is set to "true", so the tests run for Workload Identtty + // enableWI is set to true when the TF_VAR_enable_wi is set to "true", so the tests run for Workload Identtty enableWI bool + + // testGitCfg is a struct containing different variables needed for running git tests. + testGitCfg *gitTestConfig ) // registryLoginFunc is used to perform registry login against a provider based @@ -126,6 +147,30 @@ type pushTestImages func(ctx context.Context, localImgs map[string]string, outpu // service account when workload identity is used on the cluster. type getWISAAnnotations func(output map[string]*tfjson.StateOutput) (map[string]string, error) +// grantPermissionsToGitRepository calls provider specific API to add additional permissions to the git repository/project +type grantPermissionsToGitRepository func(ctx context.Context, cfg *gitTestConfig, output map[string]*tfjson.StateOutput) error + +// revokePermissionsToGitRepository calls provider specific API to revoke permissions to the git repository/project +type revokePermissionsToGitRepository func(ctx context.Context, cfg *gitTestConfig, output map[string]*tfjson.StateOutput) error + +// getGitTestConfig gets the configuration for the tests +type getGitTestConfig func(output map[string]*tfjson.StateOutput) (*gitTestConfig, error) + +// gitTestConfig hold different variable that will be needed by the different test functions. +type gitTestConfig struct { + // authentication info for git repositories + gitPat string + gitUsername string + defaultGitTransport git.TransportType + defaultAuthOpts *git.AuthOptions + applicationRepository string + applicationRepositoryWithoutUser string + organization string + // permissionID is a string that represents the entity that was granted + // permissions on the git repository + permissionID string +} + // ProviderConfig is the test configuration of a supported cloud provider to run // the tests against. type ProviderConfig struct { @@ -141,6 +186,12 @@ type ProviderConfig struct { // getWISAAnnotations is used to return the provider specific annotations // for the service account when using workload identity. getWISAAnnotations getWISAAnnotations + // grantPermissionsToGitRepository is used to give the identity access to the Git repository + grantPermissionsToGitRepository grantPermissionsToGitRepository + // revokePermissionsToGitRepository is used to revoke the identity access to the Git repository + revokePermissionsToGitRepository revokePermissionsToGitRepository + // getGitTestConfig is used to return provider specific test configuration + getGitTestConfig getGitTestConfig } var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890") @@ -159,16 +210,20 @@ func TestMain(m *testing.M) { // Validate the provider. if *targetProvider == "" { - log.Fatalf("-provider flag must be set to one of %v", supportedProviders) + log.Fatalf("-provider flag must be set to one of %v for git or %v for oci", supportedGitProviders, supportedOciProviders) } - var supported bool - for _, p := range supportedProviders { - if p == *targetProvider { - supported = true - } + + if os.Getenv("TF_VAR_enable_git") == "true" && supportedProvider(*targetProvider, supportedGitProviders) { + enableGit = true + } + + if os.Getenv("TF_VAR_enable_oci") == "true" && supportedProvider(*targetProvider, supportedOciProviders) { + enableOci = true } - if !supported { - log.Fatalf("Unsupported provider %q, must be one of %v", *targetProvider, supportedProviders) + + enableWI = os.Getenv("TF_VAR_enable_wi") == "true" + if enableGit && !enableWI { + log.Fatalf("Workload identity must be enabled to run git tests") } providerCfg := getProviderConfig(*targetProvider) @@ -249,12 +304,89 @@ func TestMain(m *testing.M) { panic(fmt.Sprintf("Failed to get the terraform state output: %v", err)) } - testRepos, err = providerCfg.registryLogin(ctx, output) + // Cleanup infra that depends on terraform output before exit + defer func() { + if !*retain { + if testGitCfg != nil && testGitCfg.permissionID != "" { + err := providerCfg.revokePermissionsToGitRepository(ctx, testGitCfg, output) + if err != nil { + log.Printf("Failed to revoke permissions to git repository: %s", err) + exitCode = 1 + } + } + } + }() + + if enableGit { + // Populate the global git config. + testGitCfg, err = providerCfg.getGitTestConfig(output) + if err != nil { + panic(fmt.Sprintf("Failed to get git test config: %v", err)) + } + } + + pushAppImage(ctx, providerCfg, output, localImgs) + configureAdditionalInfra(ctx, providerCfg, output) + + exitCode = m.Run() +} + +func supportedProvider(targetProvider string, supportedProviders []string) bool { + for _, p := range supportedProviders { + if p == targetProvider { + return true + } + } + return false +} + +// getProviderConfig returns the test configuration of supported providers. +func getProviderConfig(provider string) *ProviderConfig { + switch provider { + case "aws": + return &ProviderConfig{ + terraformPath: terraformPathAWS, + registryLogin: registryLoginECR, + pushAppTestImages: pushAppTestImagesECR, + createKubeconfig: createKubeconfigEKS, + getWISAAnnotations: getWISAAnnotationsAWS, + grantPermissionsToGitRepository: grantPermissionsToGitRepositoryAWS, + revokePermissionsToGitRepository: revokePermissionsToGitRepositoryAWS, + getGitTestConfig: getGitTestConfigAWS, + } + case "azure": + providerCfg := &ProviderConfig{ + terraformPath: terraformPathAzure, + registryLogin: registryLoginACR, + pushAppTestImages: pushAppTestImagesACR, + createKubeconfig: createKubeConfigAKS, + getWISAAnnotations: getWISAAnnotationsAzure, + grantPermissionsToGitRepository: grantPermissionsToGitRepositoryAzure, + revokePermissionsToGitRepository: revokePermissionsToGitRepositoryAzure, + getGitTestConfig: getGitTestConfigAzure, + } + return providerCfg + case "gcp": + return &ProviderConfig{ + terraformPath: terraformPathGCP, + registryLogin: registryLoginGCR, + pushAppTestImages: pushAppTestImagesGCR, + createKubeconfig: createKubeconfigGKE, + getWISAAnnotations: getWISAAnnotationsGCP, + grantPermissionsToGitRepository: grantPermissionsToGitRepositoryGCP, + revokePermissionsToGitRepository: revokePermissionsToGitRepositoryGCP, + getGitTestConfig: getGitTestConfigGCP, + } + } + return nil +} + +func pushAppImage(ctx context.Context, providerCfg *ProviderConfig, tfOutput map[string]*tfjson.StateOutput, localImgs map[string]string) { + _, err := providerCfg.registryLogin(ctx, tfOutput) if err != nil { panic(fmt.Sprintf("Failed to log into registry: %v", err)) } - - pushedImages, err := providerCfg.pushAppTestImages(ctx, localImgs, output) + pushedImages, err := providerCfg.pushAppTestImages(ctx, localImgs, tfOutput) if err != nil { panic(fmt.Sprintf("Failed to push test images: %v", err)) } @@ -268,64 +400,53 @@ func TestMain(m *testing.M) { } else { testAppImage = appImg } +} + +func configureAdditionalInfra(ctx context.Context, providerCfg *ProviderConfig, tfOutput map[string]*tfjson.StateOutput) { + if enableOci { + log.Println("OCI is enabled, push oci test images") + pushOciTestImages(ctx, providerCfg, tfOutput) + } - // Create and push test images. - if err := tftestenv.CreateAndPushImages(testRepos, testImageTags); err != nil { - panic(fmt.Sprintf("Failed to create and push images: %v", err)) + if enableGit && testGitCfg != nil { + // Call provider specific API to configure permisions for the git repository + log.Println("Git is enabled, granting permissions to workload identity to access repository") + if err := providerCfg.grantPermissionsToGitRepository(ctx, testGitCfg, tfOutput); err != nil { + panic(fmt.Sprintf("Failed to grant permissions to repository: %v", err)) + } } - enableWI = os.Getenv("TF_VAR_enable_wi") == "true" if enableWI { - log.Println("Running tests with workload identity enabled") - annotations, err := providerCfg.getWISAAnnotations(output) + log.Println("Workload identity is enabled, initializing service account with annotations") + annotations, err := providerCfg.getWISAAnnotations(tfOutput) if err != nil { panic(fmt.Sprintf("Failed to get service account func for workload identity: %v", err)) } - if err := creatWorkloadIDServiceAccount(ctx, annotations); err != nil { + if err := createWorkloadIDServiceAccount(ctx, annotations); err != nil { panic(err) } } - - exitCode = m.Run() } -// getProviderConfig returns the test configuration of supported providers. -func getProviderConfig(provider string) *ProviderConfig { - switch provider { - case "aws": - return &ProviderConfig{ - terraformPath: terraformPathAWS, - registryLogin: registryLoginECR, - pushAppTestImages: pushAppTestImagesECR, - createKubeconfig: createKubeconfigEKS, - getWISAAnnotations: getWISAAnnotationsAWS, - } - case "azure": - return &ProviderConfig{ - terraformPath: terraformPathAzure, - registryLogin: registryLoginACR, - pushAppTestImages: pushAppTestImagesACR, - createKubeconfig: createKubeConfigAKS, - getWISAAnnotations: getWISAAnnotationsAzure, - } - case "gcp": - return &ProviderConfig{ - terraformPath: terraformPathGCP, - registryLogin: registryLoginGCR, - pushAppTestImages: pushAppTestImagesGCR, - createKubeconfig: createKubeconfigGKE, - getWISAAnnotations: getWISAAnnotationsGCP, - } +func pushOciTestImages(ctx context.Context, providerCfg *ProviderConfig, tfOutput map[string]*tfjson.StateOutput) { + var err error + testRepos, err = providerCfg.registryLogin(ctx, tfOutput) + if err != nil { + panic(fmt.Sprintf("Failed to log into registry: %v", err)) + } + + // Create and push test images. + if err := tftestenv.CreateAndPushImages(testRepos, testImageTags); err != nil { + panic(fmt.Sprintf("Failed to create and push images: %v", err)) } - return nil } // creatWorkloadIDServiceAccount creates the service account (name and namespace specified in the terraform // variables) with the annotations passed into the function. // // TODO: move creation of serviceaccount to terraform -func creatWorkloadIDServiceAccount(ctx context.Context, annotations map[string]string) error { +func createWorkloadIDServiceAccount(ctx context.Context, annotations map[string]string) error { wiServiceAccount = os.Getenv(envVarWISAName) wiSANamespace := os.Getenv(envVarWISANamespace) if wiServiceAccount == "" || wiSANamespace == "" { diff --git a/oci/tests/integration/terraform/azure/main.tf b/oci/tests/integration/terraform/azure/main.tf index fba04c6d..ee208b5a 100644 --- a/oci/tests/integration/terraform/azure/main.tf +++ b/oci/tests/integration/terraform/azure/main.tf @@ -9,7 +9,9 @@ resource "random_pet" "suffix" { } locals { - name = "fluxTest${random_pet.suffix.id}" + name = "fluxTest${random_pet.suffix.id}" + project_name = "fluxProj${random_pet.suffix.id}" + repo_name = "fluxRepo${random_pet.suffix.id}" } module "aks" { @@ -55,3 +57,19 @@ resource "azurerm_federated_identity_credential" "federated-identity2" { depends_on = [module.aks] } + +provider "azuredevops" { + org_service_url = "https://dev.azure.com/${var.azuredevops_org}" + personal_access_token = var.azuredevops_pat +} + +module "devops" { + count = var.enable_git ? 1 : 0 + source = "git::https://github.com/fluxcd/test-infra.git//tf-modules/azure/devops" + providers = { + azuredevops = azuredevops + } + + project_name = local.project_name + repository_name = local.repo_name +} diff --git a/oci/tests/integration/terraform/azure/outputs.tf b/oci/tests/integration/terraform/azure/outputs.tf index 02822208..a64e6903 100644 --- a/oci/tests/integration/terraform/azure/outputs.tf +++ b/oci/tests/integration/terraform/azure/outputs.tf @@ -14,3 +14,15 @@ output "acr_registry_id" { output "workload_identity_client_id" { value = var.enable_wi ? azurerm_user_assigned_identity.wi-id[0].client_id : "" } + +output "workload_identity_object_id" { + value = var.enable_wi ? azurerm_user_assigned_identity.wi-id[0].principal_id : "" +} + +output "git_repo_url" { + value = var.enable_git ? module.devops[0].repo_url : "" +} + +output "azure_devops_project_id" { + value = var.enable_git ? module.devops[0].project_id : "" +} diff --git a/oci/tests/integration/terraform/azure/variables.tf b/oci/tests/integration/terraform/azure/variables.tf index e2a1f205..369be926 100644 --- a/oci/tests/integration/terraform/azure/variables.tf +++ b/oci/tests/integration/terraform/azure/variables.tf @@ -25,3 +25,21 @@ variable "enable_wi" { default = false description = "Enable workload identity on cluster and create federated identity" } + +variable "enable_git" { + type = bool + default = false + description = "Enable git repository creation" +} + +variable "azuredevops_org" { + type = string + description = "Azure Devops organization to create project and git repository" + default = "" +} + +variable "azuredevops_pat" { + type = string + description = "Personal access token to create project and repository in azure devops" + default = "" +} diff --git a/oci/tests/integration/terraform/azure/version.tf b/oci/tests/integration/terraform/azure/version.tf new file mode 100644 index 00000000..cb03d9ce --- /dev/null +++ b/oci/tests/integration/terraform/azure/version.tf @@ -0,0 +1,7 @@ +terraform { + required_providers { + azuredevops = { + source = "microsoft/azuredevops" + } + } +} diff --git a/oci/tests/integration/testapp/main.go b/oci/tests/integration/testapp/main.go index 4ca6eb9e..bf92cf9a 100644 --- a/oci/tests/integration/testapp/main.go +++ b/oci/tests/integration/testapp/main.go @@ -21,6 +21,9 @@ import ( "flag" "fmt" "log" + "net/url" + "os" + "path/filepath" "strings" "time" @@ -30,7 +33,11 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log/zap" + "github.com/fluxcd/pkg/auth/azure" "github.com/fluxcd/pkg/cache" + "github.com/fluxcd/pkg/git" + "github.com/fluxcd/pkg/git/gogit" + "github.com/fluxcd/pkg/git/repository" "github.com/fluxcd/pkg/oci/auth/login" ) @@ -41,13 +48,27 @@ import ( // is provided separately, e.g. registry: foo.azurecr.io, repo: bar. var ( registry = flag.String("registry", "", "registry of the repository") - repo = flag.String("repo", "", "repository to list") + repo = flag.String("repo", "", "git/oci repository to list") oidcLogin = flag.Bool("oidc-login", false, "login with OIDCLogin function") + category = flag.String("category", "", "Test category to run - oci/git") + provider = flag.String("provider", "", "Supported git oidc provider - azure") ) func main() { flag.Parse() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() ctrl.SetLogger(zap.New(zap.UseDevMode(true))) + if *category == "oci" { + checkOci(ctx) + } else if *category == "git" { + checkGit(ctx) + } else { + panic("unsupported category") + } +} + +func checkOci(ctx context.Context) { cache, err := cache.New(5, cache.StoreObjectKeyFunc, cache.WithCleanupInterval[cache.StoreObject[authn.Authenticator]](1*time.Second)) if err != nil { @@ -59,8 +80,6 @@ func main() { AzureAutoLogin: true, Cache: cache, } - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() if *repo == "" { panic("must provide -repo value") @@ -106,3 +125,54 @@ func main() { } log.Println("tags:", tags) } + +func checkGit(ctx context.Context) { + u, err := url.Parse(*repo) + if err != nil { + panic(err) + } + + var authData map[string][]byte + authOpts, err := git.NewAuthOptions(*u, authData) + if err != nil { + panic(err) + } + authOpts.ProviderOpts = &git.ProviderOptions{ + Name: *provider, + AzureOpts: []azure.OptFunc{ + azure.WithAzureDevOpsScope(), + }, + } + cloneDir, err := os.MkdirTemp("", fmt.Sprint("test-clone")) + if err != nil { + panic(err) + } + defer os.RemoveAll(cloneDir) + c, err := gogit.NewClient(cloneDir, authOpts, gogit.WithSingleBranch(false), gogit.WithDiskStorage()) + if err != nil { + panic(err) + } + + _, err = c.Clone(ctx, *repo, repository.CloneConfig{ + CheckoutStrategy: repository.CheckoutStrategy{ + Branch: "main", + }, + }) + if err != nil { + panic(err) + } + + log.Println("Successfully cloned repository ") + // Check file from clone. + fPath := filepath.Join(cloneDir, "configmap.yaml") + if _, err := os.Stat(fPath); os.IsNotExist(err) { + panic("expected artifact configmap.yaml to exist in clone dir") + } + + // read the whole file at once + contents, err := os.ReadFile(fPath) + if err != nil { + panic(err) + } + log.Println(string(contents)) +} diff --git a/oci/tests/integration/util_test.go b/oci/tests/integration/util_test.go new file mode 100644 index 00000000..811ba25e --- /dev/null +++ b/oci/tests/integration/util_test.go @@ -0,0 +1,119 @@ +//go:build integration +// +build integration + +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration + +import ( + "context" + "errors" + "fmt" + "io" + "net/url" + "strings" + "time" + + "github.com/fluxcd/pkg/git" + "github.com/fluxcd/pkg/git/gogit" + "github.com/fluxcd/pkg/git/repository" + "github.com/go-git/go-git/v5/plumbing" +) + +const ( + // default branch to be used when cloning git repositories + defaultBranch = "main" +) + +// Clones the git repository specified in the test config and commits a config +// map yaml into the repository +func setUpGitRepository(ctx context.Context, tmpDir string) error { + c, err := getRepository(ctx, tmpDir, testGitCfg.applicationRepository, defaultBranch, testGitCfg.defaultAuthOpts) + + if err != nil { + return err + } + + manifest := `apiVersion: v1 +kind: ConfigMap +metadata: + name: foobar` + branchName := defaultBranch + + files := make(map[string]io.Reader) + files["configmap.yaml"] = strings.NewReader(manifest) + return commitAndPushAll(ctx, c, files, branchName) +} + +// Uses git package to get auth options +func getAuthOpts(repoURL string, authData map[string][]byte) (*git.AuthOptions, error) { + u, err := url.Parse(repoURL) + if err != nil { + return nil, err + } + + return git.NewAuthOptions(*u, authData) +} + +// getRepository clones the specified branch of the git repository +func getRepository(ctx context.Context, dir, repoURL, branchName string, authOpts *git.AuthOptions) (*gogit.Client, error) { + c, err := gogit.NewClient(dir, authOpts, gogit.WithSingleBranch(false), gogit.WithDiskStorage()) + if err != nil { + return nil, err + } + + _, err = c.Clone(ctx, repoURL, repository.CloneConfig{ + CheckoutStrategy: repository.CheckoutStrategy{ + Branch: branchName, + }, + }) + if err != nil { + return nil, err + } + + return c, nil +} + +// commitAndPushAll creates a commit and pushes the changes using gogit client +func commitAndPushAll(ctx context.Context, client *gogit.Client, files map[string]io.Reader, branchName string) error { + err := client.SwitchBranch(ctx, branchName) + if err != nil && !errors.Is(err, plumbing.ErrReferenceNotFound) { + return err + } + + _, err = client.Commit(git.Commit{ + Author: git.Signature{ + Name: git.DefaultPublicKeyAuthUser, + Email: "test@example.com", + When: time.Now(), + }, + }, repository.WithFiles(files)) + if err != nil { + if errors.Is(err, git.ErrNoStagedFiles) { + return nil + } + + return err + } + + err = client.Push(ctx, repository.PushConfig{}) + if err != nil { + return fmt.Errorf("unable to push: %s", err) + } + + return nil +}