mirror of https://github.com/fluxcd/flux2.git
				
				
				
			Merge pull request #4092 from fluxcd/azure-e2e-refactor
Add new Azure and GCP e2e test setuppull/4212/head
						commit
						ffe5657367
					
				| @ -0,0 +1,92 @@ | ||||
| name: e2e-gcp | ||||
| 
 | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|   schedule: | ||||
|     - cron: '0 6 * * *' | ||||
|   push: | ||||
|     branches: | ||||
|       - main | ||||
|     paths: | ||||
|       - 'tests/**' | ||||
|       - '.github/workflows/e2e-gcp.yaml' | ||||
|   pull_request: | ||||
|     branches: | ||||
|       - main | ||||
|     paths: | ||||
|       - 'tests/**' | ||||
|       - '.github/workflows/e2e-gcp.yaml' | ||||
| 
 | ||||
| permissions: | ||||
|   contents: read | ||||
| 
 | ||||
| jobs: | ||||
|   e2e-gcp: | ||||
|     runs-on: ubuntu-22.04 | ||||
|     defaults: | ||||
|       run: | ||||
|         working-directory: ./tests/integration | ||||
|     if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0 | ||||
|       - name: Setup Go | ||||
|         uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 | ||||
|         with: | ||||
|           go-version: 1.20.x | ||||
|           cache-dependency-path: tests/integration/go.sum | ||||
|       - name: Setup Flux CLI | ||||
|         run: make build | ||||
|         working-directory: ./ | ||||
|       - name: Setup SOPS | ||||
|         run: | | ||||
|           mkdir -p $HOME/.local/bin | ||||
|           wget -O $HOME/.local/bin/sops https://github.com/mozilla/sops/releases/download/v$SOPS_VER/sops-v$SOPS_VER.linux | ||||
|           chmod +x $HOME/.local/bin/sops | ||||
|         env: | ||||
|           SOPS_VER: 3.7.1 | ||||
|       - name: Authenticate to Google Cloud | ||||
|         uses: google-github-actions/auth@35b0e87d162680511bf346c299f71c9c5c379033 # v1.1.1 | ||||
|         id: 'auth' | ||||
|         with: | ||||
|           credentials_json: '${{ secrets.FLUX2_E2E_GOOGLE_CREDENTIALS }}' | ||||
|           token_format: 'access_token' | ||||
|       - name: Setup gcloud | ||||
|         uses: google-github-actions/setup-gcloud@e30db14379863a8c79331b04a9969f4c1e225e0b # v1.1.1 | ||||
|       - name: Setup QEMU | ||||
|         uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2.2.0 | ||||
|       - name: Setup Docker Buildx | ||||
|         uses: docker/setup-buildx-action@4c0219f9ac95b02789c1075625400b2acbff50b1 # v2.9.1 | ||||
|       - name: Log into us-central1-docker.pkg.dev | ||||
|         uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0 | ||||
|         with: | ||||
|           registry: us-central1-docker.pkg.dev | ||||
|           username: oauth2accesstoken | ||||
|           password: ${{ steps.auth.outputs.access_token }} | ||||
|       - name: Set dynamic variables in .env | ||||
|         run: | | ||||
|           cat > .env <<EOF | ||||
|           export TF_VAR_tags='{ "environment"="github", "ci"="true", "repo"="flux2", "createdat"="$(date -u +x%Y-%m-%d_%Hh%Mm%Ss)" }' | ||||
|           EOF | ||||
|       - name: Print .env for dynamic tag value reference | ||||
|         run: cat .env | ||||
|       - name: Run GCP e2e tests | ||||
|         env: | ||||
|           TF_VAR_gcp_project_id: ${{ vars.TF_VAR_gcp_project_id }} | ||||
|           TF_VAR_gcp_region: ${{ vars.TF_VAR_gcp_region }} | ||||
|           TF_VAR_gcp_zone: ${{ vars.TF_VAR_gcp_zone }} | ||||
|           TF_VAR_gcp_email: ${{ secrets.TF_VAR_gcp_email }} | ||||
|           TF_VAR_gcp_keyring: ${{ secrets.TF_VAR_gcp_keyring }} | ||||
|           TF_VAR_gcp_crypto_key: ${{ secrets.TF_VAR_gcp_crypto_key }} | ||||
|           GITREPO_SSH_CONTENTS: ${{ secrets.GCP_GITREPO_SSH_CONTENTS }} | ||||
|           GITREPO_SSH_PUB_CONTENTS: ${{ secrets.GCP_GITREPO_SSH_PUB_CONTENTS }} | ||||
|         run: | | ||||
|           source .env | ||||
|           mkdir -p ./build/ssh | ||||
|           touch ./build/ssh/key | ||||
|           echo $GITREPO_SSH_CONTENTS | base64 -d > build/ssh/key | ||||
|           export GITREPO_SSH_PATH=build/ssh/key | ||||
|           touch ./build/ssh/key.pub | ||||
|           echo $GITREPO_SSH_PUB_CONTENTS | base64 -d > ./build/ssh/key.pub | ||||
|           export GITREPO_SSH_PUB_PATH=build/ssh/key.pub | ||||
|           make test-gcp | ||||
| @ -0,0 +1,29 @@ | ||||
| ## Azure | ||||
| # export TF_VAR_azuredevops_org= | ||||
| # export TF_VAR_azuredevops_pat= | ||||
| # export TF_VAR_azure_location= | ||||
| ## 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 | ||||
| # export TF_VAR_gcp_project_id= | ||||
| # export TF_VAR_gcp_zone= | ||||
| # export TF_VAR_gcp_region= | ||||
| # export TF_VAR_gcp_keyring= | ||||
| # export TF_VAR_gcp_crypto_key= | ||||
| ## Email address of a GCP user used for git repository cloning over ssh. | ||||
| # export TF_VAR_gcp_email= | ||||
| ## Set the following only when using service account. | ||||
| ## Provide absolute path to the service account JSON key file. | ||||
| # export GOOGLE_APPLICATION_CREDENTIALS= | ||||
| 
 | ||||
| ## Common variables | ||||
| # export TF_VAR_tags='{"environment"="dev", "createdat"='"\"$(date -u +x%Y-%m-%d_%Hh%Mm%Ss)\""'}' | ||||
| ## These are not terraform variables | ||||
| ## but they are needed for the bootstrap tests | ||||
| # export GITREPO_SSH_PATH= | ||||
| # export GITREPO_SSH_PUB_PATH= | ||||
| @ -0,0 +1,24 @@ | ||||
| GO_TEST_ARGS ?= | ||||
| PROVIDER_ARG ?= | ||||
| TEST_TIMEOUT ?= 60m | ||||
| FLUX_BINARY ?= ../../bin/flux | ||||
| 
 | ||||
| test: sops-check | ||||
| 	mkdir -p build | ||||
| 	cp $(FLUX_BINARY) build/flux | ||||
| 	# These two versions of podinfo are pushed to the cloud registry and used in tests for ImageUpdateAutomation | ||||
| 	docker pull ghcr.io/stefanprodan/podinfo:6.0.0 | ||||
| 	docker pull ghcr.io/stefanprodan/podinfo:6.0.1 | ||||
| 	go test -timeout $(TEST_TIMEOUT) -v ./ $(GO_TEST_ARGS) $(PROVIDER_ARG) | ||||
| 
 | ||||
| test-azure: | ||||
| 	$(MAKE) test PROVIDER_ARG="-provider azure" GO_TEST_ARGS="--tags azure $(GO_TEST_ARGS)" | ||||
| 
 | ||||
| test-gcp: | ||||
| 	$(MAKE) test PROVIDER_ARG="-provider gcp" | ||||
| 
 | ||||
| 
 | ||||
| sops-check: | ||||
| ifeq ($(shell which sops),) | ||||
| 	$(error "no sops in PATH, consider installing") | ||||
| endif | ||||
| @ -0,0 +1,381 @@ | ||||
| # E2E Tests | ||||
| 
 | ||||
| The goal is to verify that Flux integration with cloud providers are actually working now and in the future. | ||||
| Currently, we only have tests for Azure and GCP. | ||||
| 
 | ||||
| ## General requirements | ||||
| 
 | ||||
| These CLI tools need to be installed for each of the tests to run successfully. | ||||
| 
 | ||||
| - Docker CLI for registry login. | ||||
| - [SOPS CLI](https://github.com/mozilla/sops) for encrypting files | ||||
| - kubectl for applying certain install manifests. | ||||
| 
 | ||||
| ## Azure | ||||
| 
 | ||||
| ### Architecture | ||||
| 
 | ||||
| The [azure](./terraform/azure) Terraform creates the AKS cluster and related resources to run the tests. It creates: | ||||
| - An Azure Container Registry | ||||
| - An Azure Kubernetes Cluster | ||||
| - Two Azure DevOps repositories | ||||
| - Azure EventHub for sending notifications | ||||
| - An Azure Key Vault | ||||
| 
 | ||||
| ### Requirements | ||||
| 
 | ||||
| - Azure account with an active subscription to be able to create AKS and ACR, | ||||
|   and permission to assign roles. Role assignment is required for allowing AKS workloads to access ACR. | ||||
| - Azure CLI, need to be logged in using `az login` as a User or as a Service Principal | ||||
| - An Azure DevOps organization, personal access token and ssh keys for accessing repositories | ||||
|   within the organization. The scope required for the personal access token is: | ||||
|   - `Project and Team` - read, write and manage access | ||||
|   - `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. | ||||
|   - Azure DevOps only supports RSA keys. Please see | ||||
|     [documentation](https://learn.microsoft.com/en-us/azure/devops/repos/git/use-ssh-keys-to-authenticate?view=azure-devops#set-up-ssh-key-authentication) | ||||
|     for how to set up SSH key authentication. | ||||
|   - When using in CI, create a test user and use the test user's PAT and SSH key | ||||
|     for all Azure DevOps interactions. To grant the test user access in Azure | ||||
|     DevOps: | ||||
|     - Go to `Organization Settings` on the sidebar of the organization page. | ||||
|     - Under `General` > `Users`, click on `Add User` and input the user's email, | ||||
|       select `Access Level` of `Basic`. | ||||
|     - Go to `Security` > `Permissions`, click on the `User` tab. | ||||
|     - For the invited user, set the following permissions to `Allow`: | ||||
|       - `General: Create new project`. | ||||
|     - The user will get an email invitation and would need to create a Microsoft | ||||
|       account if they don't have one yet. | ||||
| 
 | ||||
| **NOTE:** To use Service Principal (for example in CI environment), set the | ||||
| `ARM-*` variables in `.env`, source it and authenticate Azure CLI with: | ||||
| ```console | ||||
| $ az login --service-principal -u $ARM_CLIENT_ID -p $ARM_CLIENT_SECRET --tenant $ARM_TENANT_ID | ||||
| ``` | ||||
| 
 | ||||
| ### Permissions | ||||
| 
 | ||||
| Following permissions are needed for provisioning the infrastructure and running | ||||
| the tests: | ||||
| - `Microsoft.Kubernetes/*` | ||||
| - `Microsoft.Resources/*` | ||||
| - `Microsoft.Authorization/roleAssignments/{Read,Write,Delete}` | ||||
| - `Microsoft.ContainerRegistry/*` | ||||
| - `Microsoft.ContainerService/*` | ||||
| - `Microsoft.KeyVault/*` | ||||
| - `Microsoft.EventHub/*` | ||||
| 
 | ||||
| ### IAM and CI setup | ||||
| 
 | ||||
| To create the necessary IAM role with all the permissions, set up CI secrets and | ||||
| variables using | ||||
| [azure-gh-actions](https://github.com/fluxcd/test-infra/tree/main/tf-modules/azure/github-actions) | ||||
| use the terraform configuration below. Please make sure all the requirements of | ||||
| azure-gh-actions are followed before running it. | ||||
| 
 | ||||
| ```hcl | ||||
| provider "github" { | ||||
|   owner = "fluxcd" | ||||
| } | ||||
| 
 | ||||
| resource "tls_private_key" "privatekey" { | ||||
|   algorithm = "RSA" | ||||
|   rsa_bits  = 4096 | ||||
| } | ||||
| 
 | ||||
| module "azure_gh_actions" { | ||||
|   source = "git::https://github.com/fluxcd/test-infra.git//tf-modules/azure/github-actions" | ||||
| 
 | ||||
|   azure_owners          = ["owner-id-1", "owner-id-2"] | ||||
|   azure_app_name        = "flux2-e2e" | ||||
|   azure_app_description = "flux2 e2e" | ||||
|   azure_app_secret_name = "flux2-e2e" | ||||
|   azure_permissions = [ | ||||
|     "Microsoft.Kubernetes/*", | ||||
|     "Microsoft.Resources/*", | ||||
|     "Microsoft.Authorization/roleAssignments/Read", | ||||
|     "Microsoft.Authorization/roleAssignments/Write", | ||||
|     "Microsoft.Authorization/roleAssignments/Delete", | ||||
|     "Microsoft.ContainerRegistry/*", | ||||
|     "Microsoft.ContainerService/*", | ||||
|     "Microsoft.KeyVault/*", | ||||
|     "Microsoft.EventHub/*" | ||||
|   ] | ||||
|   azure_location = "eastus" | ||||
| 
 | ||||
|   github_project = "flux2" | ||||
| 
 | ||||
|   github_secret_client_id_name       = "AZ_ARM_CLIENT_ID" | ||||
|   github_secret_client_secret_name   = "AZ_ARM_CLIENT_SECRET" | ||||
|   github_secret_subscription_id_name = "AZ_ARM_SUBSCRIPTION_ID" | ||||
|   github_secret_tenant_id_name       = "AZ_ARM_TENANT_ID" | ||||
| 
 | ||||
|   github_secret_custom = { | ||||
|     "TF_VAR_azuredevops_org"         = "<azuredevops-org-name>", | ||||
|     "TF_VAR_azuredevops_pat"         = "<azuredevops-pat>", | ||||
|     "AZURE_GITREPO_SSH_CONTENTS"     = base64encode(tls_private_key.privatekey.private_key_openssh), | ||||
|     "AZURE_GITREPO_SSH_PUB_CONTENTS" = base64encode(tls_private_key.privatekey.public_key_openssh) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| output "publickey" { | ||||
|   value = tls_private_key.privatekey.public_key_openssh | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Copy the `publickey` output printed after applying, or run `terraform output` to | ||||
| print it again, and add it in the Azure DevOps SSH public keys under the user | ||||
| account that'll be used by flux in the tests. | ||||
| 
 | ||||
| **NOTE:** The environment variables used above are for the GitHub workflow that | ||||
| runs the tests. Change the variable names if needed accordingly. | ||||
| 
 | ||||
| ## GCP | ||||
| 
 | ||||
| ### Architecture | ||||
| 
 | ||||
| The [gcp](./terraform/gcp) terraform files create the GKE cluster and related resources to run the tests. It creates: | ||||
| - A Google Container Registry and Artifact Registry | ||||
| - A Google Kubernetes Cluster | ||||
| - Two Google Cloud Source Repositories | ||||
| - A Google Pub/Sub Topic and a subscription to the service that would be used in the tests | ||||
| 
 | ||||
| Note: It doesn't create Google KMS keyrings and crypto keys because these cannot be destroyed. Instead, you have | ||||
| to pass in the crypto key and keyring that would be used to test the sops encryption in Flux. Please see `.env.sample` | ||||
| for the terraform variables | ||||
| 
 | ||||
| ### Requirements | ||||
| 
 | ||||
| - GCP account with an active project to be able to create GKE and GCR, and permission to assign roles. | ||||
| - Existing GCP KMS keyring and crypto key. | ||||
|   - [Create a Keyring](https://cloud.google.com/kms/docs/create-key-ring) in | ||||
|     `global` location. | ||||
|   - [Create a Crypto Key](https://cloud.google.com/kms/docs/create-key) with | ||||
|     symmetric algorithm for encryption and decryption, and software based | ||||
|     protection level. | ||||
| - gcloud CLI, need to be logged in using `gcloud auth login` as a User (not a | ||||
|   Service Account), configure application default credentials with `gcloud auth | ||||
|   application-default login` and docker credential helper with `gcloud auth configure-docker`. | ||||
| 
 | ||||
|   **NOTE:** To use Service Account (for example in CI environment), set | ||||
|   `GOOGLE_APPLICATION_CREDENTIALS` variable in `.env` with the path to the JSON | ||||
|   key file, source it and authenticate gcloud CLI with: | ||||
|   ```console | ||||
|   $ gcloud auth activate-service-account --key-file=$GOOGLE_APPLICATION_CREDENTIALS | ||||
|   ``` | ||||
|   Depending on the Container/Artifact Registry host used in the test, authenticate | ||||
|   docker accordingly | ||||
|   ```console | ||||
|   $ gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin https://us-central1-docker.pkg.dev | ||||
|   ``` | ||||
|   In this case, the GCP client in terraform uses the Service Account to | ||||
|   authenticate and the gcloud CLI is used only to authenticate with Google | ||||
|   Container Registry and Google Artifact Registry. | ||||
| 
 | ||||
|   **NOTE FOR CI USAGE:** When saving the JSON key file as a CI secret, compress | ||||
|   the file content with | ||||
|   ```console | ||||
|   $ cat key.json | jq -r tostring | ||||
|   ``` | ||||
|   to prevent aggressive masking in the logs. Refer | ||||
|   [aggressive replacement in logs](https://github.com/google-github-actions/auth/blob/v1.1.0/docs/TROUBLESHOOTING.md#aggressive--replacement-in-logs) | ||||
|   for more details. | ||||
| - Register [SSH Keys with Google Cloud](https://cloud.google.com/source-repositories/docs/authentication#ssh) | ||||
|   - Google Cloud supports these three SSH key types: RSA (only for keys with | ||||
|     more than 2048 bits), ECDSA and ED25519. | ||||
|   - The SSH user doesn't have to be a member of the GCP project. The terraform | ||||
|     setup will grant the user permissions to the repository. Visit | ||||
|     https://source.cloud.google.com, login or create a GCP account with the SSH | ||||
|     user's email address and add SSH keys in the account. Set this email as the | ||||
|     value for the environment variable `TF_VAR_gcp_email` in `.env` file to be | ||||
|     used as a terraform variable. | ||||
| 
 | ||||
|   **Note:** Google doesn't allow a SSH key to be associated with a service | ||||
|   account email address. Therefore, there has to be an actual user that the SSH | ||||
|   key is registered to. | ||||
| 
 | ||||
| ### Permissions | ||||
| 
 | ||||
| Following roles are needed for provisioning the infrastructure and running the tests: | ||||
| 
 | ||||
| - Compute Instance Admin (v1) - `roles/compute.instanceAdmin.v1` | ||||
| - Kubernetes Engine Admin - `roles/container.admin` | ||||
| - Service Account User - `roles/iam.serviceAccountUser` | ||||
| - Service Account Token Creator - `roles/iam.serviceAccountTokenCreator` | ||||
| - Artifact Registry Administrator - `roles/artifactregistry.admin` | ||||
| - Artifact Registry Repository Administrator - `roles/artifactregistry.repoAdmin` | ||||
| - Cloud KMS Admin - `roles/cloudkms.admin` | ||||
| - Cloud KMS CryptoKey Encrypter - `roles/cloudkms.cryptoKeyEncrypter` | ||||
| - Source Repository Administrator - `roles/source.admin` | ||||
| - Pub/Sub Admin - `roles/pubsub.admin` | ||||
| 
 | ||||
| ### IAM and CI setup | ||||
| 
 | ||||
| To create the necessary IAM role with all the permissions, set up CI secrets and | ||||
| variables using | ||||
| [gcp-gh-actions](https://github.com/fluxcd/test-infra/tree/main/tf-modules/gcp/github-actions) | ||||
| use the terraform configuration below. Please make sure all the requirements of | ||||
| gcp-gh-actions are followed before running it. | ||||
| 
 | ||||
| **NOTE:** When running the following for a repo under and organization, set the | ||||
| environment variable `GITHUB_ORGANIZATION` if setting the `owner` in the | ||||
| `github` provider doesn't work. | ||||
| 
 | ||||
| ```hcl | ||||
| provider "google" {} | ||||
| 
 | ||||
| provider "github" { | ||||
|   owner = "fluxcd" | ||||
| } | ||||
| 
 | ||||
| resource "tls_private_key" "privatekey" { | ||||
|   algorithm = "RSA" | ||||
|   rsa_bits  = 4096 | ||||
| } | ||||
| 
 | ||||
| module "gcp_gh_actions" { | ||||
|   source = "git::https://github.com/fluxcd/test-infra.git//tf-modules/gcp/github-actions" | ||||
| 
 | ||||
|   gcp_service_account_id          = "flux2-e2e-test" | ||||
|   gcp_service_account_name        = "flux2-e2e-test" | ||||
|   gcp_service_account_description = "For running fluxcd/flux2 e2e tests." | ||||
|   gcp_roles = [ | ||||
|     "roles/compute.instanceAdmin.v1", | ||||
|     "roles/container.admin", | ||||
|     "roles/iam.serviceAccountUser", | ||||
|     "roles/iam.serviceAccountTokenCreator", | ||||
|     "roles/artifactregistry.admin", | ||||
|     "roles/artifactregistry.repoAdmin", | ||||
|     "roles/cloudkms.admin", | ||||
|     "roles/cloudkms.cryptoKeyEncrypter", | ||||
|     "roles/source.admin", | ||||
|     "roles/pubsub.admin" | ||||
|   ] | ||||
| 
 | ||||
|   github_project = "flux2" | ||||
| 
 | ||||
|   github_secret_credentials_name = "FLUX2_E2E_GOOGLE_CREDENTIALS" | ||||
| 
 | ||||
|   github_secret_custom = { | ||||
|     "TF_VAR_gcp_keyring"           = "<keyring-name>", | ||||
|     "TF_VAR_gcp_crypto_key"        = "<key-name>", | ||||
|     "TF_VAR_gcp_email"             = "<email>", | ||||
|     "GCP_GITREPO_SSH_CONTENTS"     = base64encode(tls_private_key.privatekey.private_key_openssh), | ||||
|     "GCP_GITREPO_SSH_PUB_CONTENTS" = base64encode(tls_private_key.privatekey.public_key_openssh) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| output "publickey" { | ||||
|   value = tls_private_key.privatekey.public_key_openssh | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Copy the `publickey` output printed after applying, or run `terraform output` to | ||||
| print it again, and add it in the Google Source Repository SSH public keys under | ||||
| the user account with email address referred in `TF_VAR_gcp_email` above. | ||||
| 
 | ||||
| **NOTE:** The environment variables used above are for the GitHub workflow that | ||||
| runs the tests. Change the variable names if needed accordingly. | ||||
| 
 | ||||
| ## Tests | ||||
| 
 | ||||
| Each test run is initiated by running `terraform apply` in the provider's terraform directory e.g terraform apply, | ||||
| it does this by using the [tftestenv package](https://github.com/fluxcd/test-infra/blob/main/tftestenv/testenv.go) | ||||
| within the `fluxcd/test-infra` repository. It then reads the output of the Terraform to get information needed | ||||
| for the tests like the kubernetes client ID, the cloud repository urls, the key vault ID etc. This means that | ||||
| a lot of the communication with the cloud provider API is offset to Terraform instead of requiring it to be implemented in the test. | ||||
| 
 | ||||
| The following tests are currently implemented: | ||||
| 
 | ||||
| - Flux can be successfully installed on the cluster using the Flux CLI | ||||
| - source-controller can clone cloud provider repositories (Azure DevOps, Google Cloud Source Repositories) (https+ssh) | ||||
| - image-reflector-controller can list tags from provider container Registry image repositories | ||||
| - kustomize-controller can decrypt secrets using SOPS and provider key vault | ||||
| - image-automation-controller can create branches and push to cloud repositories (https+ssh) | ||||
| - source-controller can pull charts from cloud provider container registry Helm repositories | ||||
| - notification-controller can forward events to cloud Events Service(EventHub for Azure and Google Pub/Sub) | ||||
| 
 | ||||
| The following tests are run only for Azure since it is supported in the notification-controller: | ||||
| 
 | ||||
| - notification-controller can send commit status to Azure DevOps | ||||
| 
 | ||||
| ### Running tests locally | ||||
| 
 | ||||
| 1. Ensure that you have the Flux CLI binary that is to be tested built and ready. You can build it by running | ||||
|    `make build` at the root of this repository. The binary is located at `./bin` directory at the root and by default | ||||
|    this is where the Makefile copies the binary for the tests from. If you have it in a different location, you can set it | ||||
|    with the `FLUX_BINARY` variable | ||||
| 2. Copy `.env.sample` to `.env` and add the values for the different variables for the provider that you are running the tests for.  | ||||
| 3. Run `make test-<provider>`, setting the location of the flux binary with `FLUX_BINARY` variable | ||||
| 
 | ||||
| ```console | ||||
| $ make test-azure | ||||
| make test PROVIDER_ARG="-provider azure" | ||||
| # These two versions of podinfo are pushed to the cloud registry and used in tests for ImageUpdateAutomation | ||||
| mkdir -p build | ||||
| cp ../../bin/flux build/flux | ||||
| docker pull ghcr.io/stefanprodan/podinfo:6.0.0 | ||||
| 6.0.0: Pulling from stefanprodan/podinfo | ||||
| Digest: sha256:e7eeab287181791d36c82c904206a845e30557c3a4a66a8143fa1a15655dae97 | ||||
| Status: Image is up to date for ghcr.io/stefanprodan/podinfo:6.0.0 | ||||
| ghcr.io/stefanprodan/podinfo:6.0.0 | ||||
| docker pull ghcr.io/stefanprodan/podinfo:6.0.1 | ||||
| 6.0.1: Pulling from stefanprodan/podinfo | ||||
| Digest: sha256:1169f220a670cf640e45e1a7ac42dc381a441e9d4b7396432cadb75beb5b5d68 | ||||
| Status: Image is up to date for ghcr.io/stefanprodan/podinfo:6.0.1 | ||||
| ghcr.io/stefanprodan/podinfo:6.0.1 | ||||
| go test -timeout 60m -v ./ -existing -provider azure --tags=integration | ||||
| 2023/03/24 02:32:25 Setting up azure e2e test infrastructure | ||||
| 2023/03/24 02:32:25 Terraform binary:  /usr/local/bin/terraform | ||||
| 2023/03/24 02:32:25 Init Terraform | ||||
| ....[some output has been cut out] | ||||
| 2023/03/24 02:39:33 helm repository condition not ready | ||||
| --- PASS: TestACRHelmRelease (15.31s) | ||||
| === RUN   TestKeyVaultSops | ||||
| --- PASS: TestKeyVaultSops (15.98s) | ||||
| PASS | ||||
| 2023/03/24 02:40:12 Destroying environment... | ||||
| ok      github.com/fluxcd/flux2/tests/integration       947.341s | ||||
| ``` | ||||
| 
 | ||||
| In the above, the test created a build directory build/ and the flux cli binary is copied build/flux. It would be used | ||||
| to bootstrap Flux on the cluster. You can configure the location of the Flux CLI binary by setting the FLUX_BINARY variable. | ||||
| We also pull two version of `ghcr.io/stefanprodan/podinfo` image. These images are pushed to the cloud provider's | ||||
| Container Registry and used to test `ImageRepository` and `ImageUpdateAutomation`. The terraform resources get created | ||||
| and the tests are run. | ||||
| 
 | ||||
| **IMPORTANT:** In case the terraform infrastructure results in a bad state, maybe due to a crash during the apply, | ||||
| the whole infrastructure can be destroyed by running terraform destroy in terraform/<provider> directory. | ||||
| 
 | ||||
| ### Debugging the tests | ||||
| 
 | ||||
| For debugging environment provisioning, enable verbose output with `-verbose` test flag. | ||||
| 
 | ||||
| ```sh | ||||
| make test-azure GO_TEST_ARGS="-verbose" | ||||
| ``` | ||||
| 
 | ||||
| The test environment is destroyed at the end by default. Run the tests with -retain flag to retain the created test infrastructure. | ||||
| 
 | ||||
| ```sh | ||||
| make test-azure GO_TEST_ARGS="-retain" | ||||
| ``` | ||||
| The tests require the infrastructure state to be clean. For re-running the tests with a retained infrastructure, set -existing flag. | ||||
| 
 | ||||
| ```sh | ||||
| make test-azure GO_TEST_ARGS="-retain -existing" | ||||
| ``` | ||||
| 
 | ||||
| To delete an existing infrastructure created with -retain flag: | ||||
| 
 | ||||
| ```sh | ||||
| make test-azure GO_TEST_ARGS="-existing" | ||||
| ``` | ||||
| 
 | ||||
| To debug issues on the cluster created by the test (provided you passed in the `-retain` flag): | ||||
| 
 | ||||
| ```sh | ||||
| export KUBECONFIG=./build/kubeconfig | ||||
| kubectl get pods | ||||
| ``` | ||||
| @ -0,0 +1,210 @@ | ||||
| //go:build azure
 | ||||
| // +build azure
 | ||||
| 
 | ||||
| /* | ||||
| Copyright 2023 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" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/microsoft/azure-devops-go-api/azuredevops" | ||||
| 	"github.com/microsoft/azure-devops-go-api/azuredevops/git" | ||||
| 	. "github.com/onsi/gomega" | ||||
| 	giturls "github.com/whilp/git-urls" | ||||
| 
 | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 
 | ||||
| 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" | ||||
| 	notiv1 "github.com/fluxcd/notification-controller/api/v1" | ||||
| 	notiv1beta2 "github.com/fluxcd/notification-controller/api/v1beta2" | ||||
| 	"github.com/fluxcd/pkg/apis/meta" | ||||
| 	sourcev1 "github.com/fluxcd/source-controller/api/v1" | ||||
| ) | ||||
| 
 | ||||
| func TestAzureDevOpsCommitStatus(t *testing.T) { | ||||
| 	g := NewWithT(t) | ||||
| 
 | ||||
| 	ctx := context.TODO() | ||||
| 	branchName := "commit-status" | ||||
| 	testID := branchName + randStringRunes(5) | ||||
| 	manifest := `apiVersion: v1 | ||||
| kind: ConfigMap | ||||
| metadata: | ||||
|   name: foobar` | ||||
| 
 | ||||
| 	repoUrl := getTransportURL(cfg.applicationRepository) | ||||
| 	tmpDir := t.TempDir() | ||||
| 	c, err := getRepository(ctx, tmpDir, repoUrl, defaultBranch, cfg.defaultAuthOpts) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 	files := make(map[string]io.Reader) | ||||
| 	files["configmap.yaml"] = strings.NewReader(manifest) | ||||
| 	err = commitAndPushAll(ctx, c, files, branchName) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	modifyKsSpec := func(spec *kustomizev1.KustomizationSpec) { | ||||
| 		spec.HealthChecks = []meta.NamespacedObjectKindReference{ | ||||
| 			{ | ||||
| 				APIVersion: "v1", | ||||
| 				Kind:       "ConfigMap", | ||||
| 				Name:       "foobar", | ||||
| 				Namespace:  testID, | ||||
| 			}, | ||||
| 		} | ||||
| 	} | ||||
| 	err = setUpFluxConfig(ctx, testID, nsConfig{ | ||||
| 		ref: &sourcev1.GitRepositoryRef{ | ||||
| 			Branch: branchName, | ||||
| 		}, | ||||
| 		repoURL:      repoUrl, | ||||
| 		path:         "./", | ||||
| 		modifyKsSpec: modifyKsSpec, | ||||
| 	}) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 	t.Cleanup(func() { | ||||
| 		err := tearDownFluxConfig(ctx, testID) | ||||
| 		if err != nil { | ||||
| 			log.Printf("failed to delete resources in '%s' namespace: %s", testID, err) | ||||
| 		} | ||||
| 	}) | ||||
| 
 | ||||
| 	g.Eventually(func() bool { | ||||
| 		err := verifyGitAndKustomization(ctx, testEnv, testID, testID) | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		return true | ||||
| 	}, testTimeout, testInterval) | ||||
| 
 | ||||
| 	secret := corev1.Secret{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name:      "azuredevops-token", | ||||
| 			Namespace: testID, | ||||
| 		}, | ||||
| 		StringData: map[string]string{ | ||||
| 			"token": cfg.gitPat, | ||||
| 		}, | ||||
| 	} | ||||
| 	g.Expect(testEnv.Create(ctx, &secret)).To(Succeed()) | ||||
| 	defer testEnv.Delete(ctx, &secret) | ||||
| 
 | ||||
| 	provider := notiv1beta2.Provider{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name:      "azuredevops", | ||||
| 			Namespace: testID, | ||||
| 		}, | ||||
| 		Spec: notiv1beta2.ProviderSpec{ | ||||
| 			Type:    "azuredevops", | ||||
| 			Address: repoUrl, | ||||
| 			SecretRef: &meta.LocalObjectReference{ | ||||
| 				Name: "azuredevops-token", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	g.Expect(testEnv.Create(ctx, &provider)).To(Succeed()) | ||||
| 	defer testEnv.Delete(ctx, &provider) | ||||
| 
 | ||||
| 	alert := notiv1beta2.Alert{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name:      "azuredevops", | ||||
| 			Namespace: testID, | ||||
| 		}, | ||||
| 		Spec: notiv1beta2.AlertSpec{ | ||||
| 			ProviderRef: meta.LocalObjectReference{ | ||||
| 				Name: provider.Name, | ||||
| 			}, | ||||
| 			EventSources: []notiv1.CrossNamespaceObjectReference{ | ||||
| 				{ | ||||
| 					Kind:      "Kustomization", | ||||
| 					Name:      testID, | ||||
| 					Namespace: testID, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	g.Expect(testEnv.Create(ctx, &alert)).To(Succeed()) | ||||
| 	defer testEnv.Delete(ctx, &alert) | ||||
| 
 | ||||
| 	url, err := ParseAzureDevopsURL(repoUrl) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	rev, err := c.Head() | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	connection := azuredevops.NewPatConnection(url.OrgURL, cfg.gitPat) | ||||
| 	client, err := git.NewClient(ctx, connection) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 	getArgs := git.GetStatusesArgs{ | ||||
| 		Project:      &url.Project, | ||||
| 		RepositoryId: &url.Repo, | ||||
| 		CommitId:     &rev, | ||||
| 	} | ||||
| 	g.Eventually(func() bool { | ||||
| 		statuses, err := client.GetStatuses(ctx, getArgs) | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		if len(*statuses) != 1 { | ||||
| 			return false | ||||
| 		} | ||||
| 		return true | ||||
| 	}, 500*time.Second, 5*time.Second) | ||||
| } | ||||
| 
 | ||||
| type AzureDevOpsURL struct { | ||||
| 	OrgURL  string | ||||
| 	Project string | ||||
| 	Repo    string | ||||
| } | ||||
| 
 | ||||
| // TODO(somtochiama): move this into fluxcd/pkg and reuse in NC
 | ||||
| func ParseAzureDevopsURL(s string) (AzureDevOpsURL, error) { | ||||
| 	var args AzureDevOpsURL | ||||
| 	u, err := giturls.Parse(s) | ||||
| 	if err != nil { | ||||
| 		return args, nil | ||||
| 	} | ||||
| 
 | ||||
| 	scheme := u.Scheme | ||||
| 	if u.Scheme == "ssh" { | ||||
| 		scheme = "https" | ||||
| 	} | ||||
| 
 | ||||
| 	id := strings.TrimLeft(u.Path, "/") | ||||
| 	id = strings.TrimSuffix(id, ".git") | ||||
| 
 | ||||
| 	comp := strings.Split(id, "/") | ||||
| 	if len(comp) != 4 { | ||||
| 		return args, fmt.Errorf("invalid repository id %q", id) | ||||
| 	} | ||||
| 
 | ||||
| 	args = AzureDevOpsURL{ | ||||
| 		OrgURL:  fmt.Sprintf("%s://%s/%s", scheme, u.Host, comp[0]), | ||||
| 		Project: comp[1], | ||||
| 		Repo:    comp[3], | ||||
| 	} | ||||
| 
 | ||||
| 	return args, nil | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,175 @@ | ||||
| /* | ||||
| Copyright 2023 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" | ||||
| 	"os" | ||||
| 
 | ||||
| 	eventhub "github.com/Azure/azure-event-hubs-go/v3" | ||||
| 	"github.com/fluxcd/pkg/git" | ||||
| 	"github.com/fluxcd/test-infra/tftestenv" | ||||
| 	tfjson "github.com/hashicorp/terraform-json" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	azureDevOpsKnownHosts = "ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H" | ||||
| ) | ||||
| 
 | ||||
| // createKubeConfigAKS constructs kubeconfig for an AKS cluster from the
 | ||||
| // terraform state output at the given kubeconfig path.
 | ||||
| func createKubeConfigAKS(ctx context.Context, state map[string]*tfjson.StateOutput, kcPath string) error { | ||||
| 	kubeconfigYaml, ok := state["aks_kubeconfig"].Value.(string) | ||||
| 	if !ok || kubeconfigYaml == "" { | ||||
| 		return fmt.Errorf("failed to obtain kubeconfig from tf output") | ||||
| 	} | ||||
| 	return tftestenv.CreateKubeconfigAKS(ctx, kubeconfigYaml, kcPath) | ||||
| } | ||||
| 
 | ||||
| func getTestConfigAKS(ctx context.Context, outputs map[string]*tfjson.StateOutput) (*testConfig, error) { | ||||
| 	fleetInfraRepository := outputs["fleet_infra_repository"].Value.(map[string]interface{}) | ||||
| 	applicationRepository := outputs["application_repository"].Value.(map[string]interface{}) | ||||
| 
 | ||||
| 	eventHubSas := outputs["event_hub_sas"].Value.(string) | ||||
| 	sharedSopsId := outputs["sops_id"].Value.(string) | ||||
| 
 | ||||
| 	kustomizeYaml := ` | ||||
| resources: | ||||
|   - gotk-components.yaml | ||||
|   - gotk-sync.yaml | ||||
| patchesStrategicMerge: | ||||
|   - |- | ||||
|     apiVersion: apps/v1 | ||||
|     kind: Deployment | ||||
|     metadata: | ||||
|       name: kustomize-controller | ||||
|       namespace: flux-system | ||||
|     spec: | ||||
|       template: | ||||
|         spec: | ||||
|           containers: | ||||
|           - name: manager | ||||
|             env: | ||||
|             - name: AZURE_AUTH_METHOD | ||||
|               value: msi | ||||
| ` | ||||
| 
 | ||||
| 	privateKeyFile, ok := os.LookupEnv(envVarGitRepoSSHPath) | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("%s env variable isn't set", envVarGitRepoSSHPath) | ||||
| 	} | ||||
| 	privateKeyData, err := os.ReadFile(privateKeyFile) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error getting azure devops private key, '%s': %w", privateKeyFile, err) | ||||
| 	} | ||||
| 
 | ||||
| 	pubKeyFile, ok := os.LookupEnv(envVarGitRepoSSHPubPath) | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("%s env variable isn't set", envVarGitRepoSSHPubPath) | ||||
| 	} | ||||
| 	pubKeyData, err := os.ReadFile(pubKeyFile) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error getting ssh pubkey '%s', %w", pubKeyFile, err) | ||||
| 	} | ||||
| 
 | ||||
| 	c := make(chan []byte, 10) | ||||
| 	closefn, err := setupEventHubHandler(ctx, c, eventHubSas) | ||||
| 
 | ||||
| 	var notificationCfg = notificationConfig{ | ||||
| 		notificationChan: c, | ||||
| 		providerType:     "azureeventhub", | ||||
| 		closeChan:        closefn, | ||||
| 		secret: map[string]string{ | ||||
| 			"address": eventHubSas, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	config := &testConfig{ | ||||
| 		defaultGitTransport: git.HTTP, | ||||
| 		gitUsername:         git.DefaultPublicKeyAuthUser, | ||||
| 		gitPat:              outputs["azure_devops_access_token"].Value.(string), | ||||
| 		gitPrivateKey:       string(privateKeyData), | ||||
| 		gitPublicKey:        string(pubKeyData), | ||||
| 		knownHosts:          azureDevOpsKnownHosts, | ||||
| 		fleetInfraRepository: gitUrl{ | ||||
| 			http: fleetInfraRepository["http"].(string), | ||||
| 			ssh:  fleetInfraRepository["ssh"].(string), | ||||
| 		}, | ||||
| 		applicationRepository: gitUrl{ | ||||
| 			http: applicationRepository["http"].(string), | ||||
| 			ssh:  applicationRepository["ssh"].(string), | ||||
| 		}, | ||||
| 		notificationCfg: notificationCfg, | ||||
| 		sopsArgs:        fmt.Sprintf("--azure-kv %s", sharedSopsId), | ||||
| 		sopsSecretData: map[string]string{ | ||||
| 			"sops.azure-kv": fmt.Sprintf(`clientId: %s`, outputs["aks_client_id"].Value.(string)), | ||||
| 		}, | ||||
| 		kustomizationYaml: kustomizeYaml, | ||||
| 	} | ||||
| 
 | ||||
| 	opts, err := authOpts(config.fleetInfraRepository.http, map[string][]byte{ | ||||
| 		"password": []byte(config.gitPat), | ||||
| 		"username": []byte("git"), | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	config.defaultAuthOpts = opts | ||||
| 
 | ||||
| 	return config, nil | ||||
| } | ||||
| 
 | ||||
| // registryLoginACR logs into the Azure Container Registries using the
 | ||||
| // provider's CLI tools and returns the test repositories.
 | ||||
| func registryLoginACR(ctx context.Context, output map[string]*tfjson.StateOutput) (string, error) { | ||||
| 	// NOTE: ACR registry accept dynamic repository creation by just pushing a
 | ||||
| 	// new image with a new repository name.
 | ||||
| 	registryURL := output["acr_url"].Value.(string) | ||||
| 	if err := tftestenv.RegistryLoginACR(ctx, registryURL); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	return registryURL, nil | ||||
| } | ||||
| 
 | ||||
| func setupEventHubHandler(ctx context.Context, c chan []byte, eventHubSas string) (func(), error) { | ||||
| 	hub, err := eventhub.NewHubFromConnectionString(eventHubSas) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	handler := func(ctx context.Context, event *eventhub.Event) error { | ||||
| 		c <- event.Data | ||||
| 		return nil | ||||
| 	} | ||||
| 	runtimeInfo, err := hub.GetRuntimeInformation(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	listenerHandler, err := hub.Receive(ctx, runtimeInfo.PartitionIDs[0], handler, eventhub.ReceiveWithLatestOffset()) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	closefn := func() { | ||||
| 		listenerHandler.Close(ctx) | ||||
| 		hub.Close(ctx) | ||||
| 	} | ||||
| 
 | ||||
| 	return closefn, nil | ||||
| } | ||||
| @ -0,0 +1,168 @@ | ||||
| /* | ||||
| Copyright 2023 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" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/fluxcd/pkg/git" | ||||
| 	sourcev1 "github.com/fluxcd/source-controller/api/v1" | ||||
| 	. "github.com/onsi/gomega" | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	"k8s.io/apimachinery/pkg/types" | ||||
| ) | ||||
| 
 | ||||
| func TestFluxInstallation(t *testing.T) { | ||||
| 	g := NewWithT(t) | ||||
| 	ctx := context.TODO() | ||||
| 	g.Eventually(func() bool { | ||||
| 		err := verifyGitAndKustomization(ctx, testEnv.Client, "flux-system", "flux-system") | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		return true | ||||
| 	}, 60*time.Second, 5*time.Second) | ||||
| } | ||||
| 
 | ||||
| func TestRepositoryCloning(t *testing.T) { | ||||
| 	ctx := context.TODO() | ||||
| 	branchName := "feature/branch" | ||||
| 	tagName := "v1" | ||||
| 
 | ||||
| 	g := NewWithT(t) | ||||
| 
 | ||||
| 	type testStruct struct { | ||||
| 		name      string | ||||
| 		refType   string | ||||
| 		cloneType git.TransportType | ||||
| 	} | ||||
| 
 | ||||
| 	tests := []testStruct{ | ||||
| 		{ | ||||
| 			name:      "ssh-feature-branch", | ||||
| 			refType:   "branch", | ||||
| 			cloneType: git.SSH, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:      "ssh-v1", | ||||
| 			refType:   "tag", | ||||
| 			cloneType: git.SSH, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	// Not all cloud providers have repositories that support authentication with an accessToken
 | ||||
| 	// we don't run http tests for these.
 | ||||
| 	if cfg.gitPat != "" { | ||||
| 		httpTests := []testStruct{ | ||||
| 			{ | ||||
| 				name:      "https-feature-branch", | ||||
| 				refType:   "branch", | ||||
| 				cloneType: git.HTTP, | ||||
| 			}, | ||||
| 			{ | ||||
| 				name:      "https-v1", | ||||
| 				refType:   "tag", | ||||
| 				cloneType: git.HTTP, | ||||
| 			}, | ||||
| 		} | ||||
| 
 | ||||
| 		tests = append(tests, httpTests...) | ||||
| 	} | ||||
| 
 | ||||
| 	t.Log("Creating application sources") | ||||
| 	url := getTransportURL(cfg.applicationRepository) | ||||
| 	tmpDir := t.TempDir() | ||||
| 	client, err := getRepository(ctx, tmpDir, url, defaultBranch, cfg.defaultAuthOpts) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	files := make(map[string]io.Reader) | ||||
| 	for _, tt := range tests { | ||||
| 		manifest := `apiVersion: v1 | ||||
| kind: ConfigMap | ||||
| metadata: | ||||
|   name: foobar | ||||
|     ` | ||||
| 		name := fmt.Sprintf("cloning-test/%s/configmap.yaml", tt.name) | ||||
| 		files[name] = strings.NewReader(manifest) | ||||
| 	} | ||||
| 
 | ||||
| 	err = commitAndPushAll(ctx, client, files, branchName) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 	err = createTagAndPush(ctx, client, branchName, tagName) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	t.Log("Verifying application-gitops namespaces") | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			g := NewWithT(t) | ||||
| 			ref := &sourcev1.GitRepositoryRef{ | ||||
| 				Branch: branchName, | ||||
| 			} | ||||
| 			if tt.refType == "tag" { | ||||
| 				ref = &sourcev1.GitRepositoryRef{ | ||||
| 					Tag: tagName, | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			url := cfg.applicationRepository.http | ||||
| 			if tt.cloneType == git.SSH { | ||||
| 				url = cfg.applicationRepository.ssh | ||||
| 			} | ||||
| 
 | ||||
| 			testID := fmt.Sprintf("%s-%s", tt.name, randStringRunes(5)) | ||||
| 			err := setUpFluxConfig(ctx, testID, nsConfig{ | ||||
| 				repoURL:    url, | ||||
| 				ref:        ref, | ||||
| 				protocol:   tt.cloneType, | ||||
| 				objectName: testID, | ||||
| 				path:       fmt.Sprintf("./cloning-test/%s", tt.name), | ||||
| 			}) | ||||
| 			g.Expect(err).ToNot(HaveOccurred()) | ||||
| 			t.Cleanup(func() { | ||||
| 				err := tearDownFluxConfig(ctx, testID) | ||||
| 				if err != nil { | ||||
| 					t.Logf("failed to delete resources in '%s' namespace: %s", tt.name, err) | ||||
| 				} | ||||
| 			}) | ||||
| 
 | ||||
| 			g.Eventually(func() bool { | ||||
| 				err := verifyGitAndKustomization(ctx, testEnv.Client, testID, testID) | ||||
| 				if err != nil { | ||||
| 					return false | ||||
| 				} | ||||
| 				return true | ||||
| 			}, 120*time.Second, 5*time.Second).Should(BeTrue()) | ||||
| 
 | ||||
| 			// Wait for configmap to be deployed
 | ||||
| 			g.Eventually(func() bool { | ||||
| 				nn := types.NamespacedName{Name: "foobar", Namespace: testID} | ||||
| 				cm := &corev1.ConfigMap{} | ||||
| 				err = testEnv.Get(ctx, nn, cm) | ||||
| 				if err != nil { | ||||
| 					return false | ||||
| 				} | ||||
| 
 | ||||
| 				return true | ||||
| 			}, 120*time.Second, 5*time.Second).Should(BeTrue()) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| @ -0,0 +1,178 @@ | ||||
| /* | ||||
| Copyright 2023 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" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"cloud.google.com/go/pubsub" | ||||
| 	tfjson "github.com/hashicorp/terraform-json" | ||||
| 	"google.golang.org/grpc/codes" | ||||
| 	"google.golang.org/grpc/status" | ||||
| 
 | ||||
| 	"github.com/fluxcd/pkg/git" | ||||
| 	"github.com/fluxcd/test-infra/tftestenv" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	gcpSourceRepoKnownHosts = "[source.developers.google.com]:2022 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBB5Iy4/cq/gt/fPqe3uyMy4jwv1Alc94yVPxmnwNhBzJqEV5gRPiRk5u4/JJMbbu9QUVAguBABxL7sBZa5PH/xY=" | ||||
| ) | ||||
| 
 | ||||
| // createKubeConfigGKE constructs kubeconfig for a GKE cluster from the
 | ||||
| // terraform state output at the given kubeconfig path.
 | ||||
| func createKubeConfigGKE(ctx context.Context, state map[string]*tfjson.StateOutput, kcPath string) error { | ||||
| 	kubeconfigYaml, ok := state["gke_kubeconfig"].Value.(string) | ||||
| 	if !ok || kubeconfigYaml == "" { | ||||
| 		return fmt.Errorf("failed to obtain kubeconfig from tf output") | ||||
| 	} | ||||
| 	return tftestenv.CreateKubeconfigGKE(ctx, kubeconfigYaml, kcPath) | ||||
| } | ||||
| 
 | ||||
| // registryLoginGCR logs into the Artifact registries using the gcloud
 | ||||
| // and returns a list of test repositories.
 | ||||
| func registryLoginGCR(ctx context.Context, output map[string]*tfjson.StateOutput) (string, error) { | ||||
| 	project := output["gcp_project_id"].Value.(string) | ||||
| 	region := output["gcp_region"].Value.(string) | ||||
| 	repositoryID := output["artifact_registry_id"].Value.(string) | ||||
| 	artifactRegistryURL, artifactRepoURL := tftestenv.GetGoogleArtifactRegistryAndRepository(project, region, repositoryID) | ||||
| 	if err := tftestenv.RegistryLoginGCR(ctx, artifactRegistryURL); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	return artifactRepoURL, nil | ||||
| } | ||||
| 
 | ||||
| func getTestConfigGKE(ctx context.Context, outputs map[string]*tfjson.StateOutput) (*testConfig, error) { | ||||
| 	sharedSopsId := outputs["sops_id"].Value.(string) | ||||
| 
 | ||||
| 	privateKeyFile, ok := os.LookupEnv(envVarGitRepoSSHPath) | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("%s env variable isn't set", envVarGitRepoSSHPath) | ||||
| 	} | ||||
| 	privateKeyData, err := os.ReadFile(privateKeyFile) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error getting gcp source repositories private key, '%s': %w", privateKeyFile, err) | ||||
| 	} | ||||
| 
 | ||||
| 	pubKeyFile, ok := os.LookupEnv(envVarGitRepoSSHPubPath) | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("%s env variable isn't set", envVarGitRepoSSHPubPath) | ||||
| 	} | ||||
| 	pubKeyData, err := os.ReadFile(pubKeyFile) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error getting ssh pubkey '%s', %w", pubKeyFile, err) | ||||
| 	} | ||||
| 
 | ||||
| 	c := make(chan []byte, 10) | ||||
| 	projectID := outputs["gcp_project_id"].Value.(string) | ||||
| 	topicID := outputs["pubsub_topic"].Value.(string) | ||||
| 
 | ||||
| 	fn, err := setupPubSubReceiver(ctx, c, projectID, topicID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	var notificationCfg = notificationConfig{ | ||||
| 		providerType:     "googlepubsub", | ||||
| 		providerChannel:  topicID, | ||||
| 		notificationChan: c, | ||||
| 		closeChan:        fn, | ||||
| 		secret: map[string]string{ | ||||
| 			"address": projectID, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	config := &testConfig{ | ||||
| 		defaultGitTransport: git.SSH, | ||||
| 		gitUsername:         "git", | ||||
| 		gitPrivateKey:       string(privateKeyData), | ||||
| 		gitPublicKey:        string(pubKeyData), | ||||
| 		knownHosts:          gcpSourceRepoKnownHosts, | ||||
| 		fleetInfraRepository: gitUrl{ | ||||
| 			ssh: outputs["fleet_infra_repository"].Value.(string), | ||||
| 		}, | ||||
| 		applicationRepository: gitUrl{ | ||||
| 			ssh: outputs["application_repository"].Value.(string), | ||||
| 		}, | ||||
| 		notificationCfg: notificationCfg, | ||||
| 		sopsArgs:        fmt.Sprintf("--gcp-kms %s", sharedSopsId), | ||||
| 	} | ||||
| 
 | ||||
| 	opts, err := authOpts(config.fleetInfraRepository.ssh, map[string][]byte{ | ||||
| 		"identity":    []byte(config.gitPrivateKey), | ||||
| 		"known_hosts": []byte(config.knownHosts), | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	config.defaultAuthOpts = opts | ||||
| 
 | ||||
| 	// In Azure, the repository is initialized with a default branch through
 | ||||
| 	// terraform. We have to do it manually here for GCP to prevent errors
 | ||||
| 	// when trying to clone later. We only need to do it for the application repository
 | ||||
| 	// since flux bootstrap pushes to the main branch.
 | ||||
| 	files := make(map[string]io.Reader) | ||||
| 	files["README.md"] = strings.NewReader("# Flux test repo") | ||||
| 	tmpDir, err := os.MkdirTemp("", "*-flux-test") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer os.RemoveAll(tmpDir) | ||||
| 
 | ||||
| 	client, err := getRepository(context.Background(), tmpDir, config.applicationRepository.ssh, defaultBranch, config.defaultAuthOpts) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	err = commitAndPushAll(context.Background(), client, files, defaultBranch) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return config, nil | ||||
| } | ||||
| 
 | ||||
| func setupPubSubReceiver(ctx context.Context, c chan []byte, projectID string, topicID string) (func(), error) { | ||||
| 	newCtx, cancel := context.WithCancel(ctx) | ||||
| 	pubsubClient, err := pubsub.NewClient(newCtx, projectID) | ||||
| 	if err != nil { | ||||
| 		cancel() | ||||
| 		return nil, fmt.Errorf("error creating pubsub client: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	sub := pubsubClient.Subscription(topicID) | ||||
| 	go func() { | ||||
| 		err = sub.Receive(ctx, func(ctx context.Context, message *pubsub.Message) { | ||||
| 			c <- message.Data | ||||
| 			message.Ack() | ||||
| 		}) | ||||
| 		if err != nil && status.Code(err) != codes.Canceled { | ||||
| 			log.Printf("error receiving message in subscription: %s\n", err) | ||||
| 			return | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	return func() { | ||||
| 		cancel() | ||||
| 		pubsubClient.Close() | ||||
| 	}, nil | ||||
| } | ||||
| @ -0,0 +1,143 @@ | ||||
| module github.com/fluxcd/flux2/tests/integration | ||||
| 
 | ||||
| go 1.18 | ||||
| 
 | ||||
| require ( | ||||
| 	cloud.google.com/go/pubsub v1.31.0 | ||||
| 	github.com/Azure/azure-event-hubs-go/v3 v3.6.0 | ||||
| 	github.com/fluxcd/helm-controller/api v0.34.1 | ||||
| 	github.com/fluxcd/image-automation-controller/api v0.34.1 | ||||
| 	github.com/fluxcd/image-reflector-controller/api v0.28.0 | ||||
| 	github.com/fluxcd/kustomize-controller/api v1.0.0-rc.4 | ||||
| 	github.com/fluxcd/notification-controller/api v1.0.0-rc.4 | ||||
| 	github.com/fluxcd/pkg/apis/event v0.5.1 | ||||
| 	github.com/fluxcd/pkg/apis/meta v1.1.1 | ||||
| 	github.com/fluxcd/pkg/git v0.12.2 | ||||
| 	github.com/fluxcd/pkg/git/gogit v0.12.0 | ||||
| 	github.com/fluxcd/pkg/runtime v0.39.0 | ||||
| 	github.com/fluxcd/source-controller/api v1.0.0-rc.5 | ||||
| 	github.com/fluxcd/test-infra/tftestenv v0.0.0-20230531151340-931581bd0a3e | ||||
| 	github.com/go-git/go-git/v5 v5.7.0 | ||||
| 	github.com/google/go-containerregistry v0.14.0 | ||||
| 	github.com/hashicorp/terraform-json v0.16.0 | ||||
| 	github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 | ||||
| 	github.com/onsi/gomega v1.27.8 | ||||
| 	github.com/whilp/git-urls v1.0.0 | ||||
| 	google.golang.org/grpc v1.55.0 | ||||
| 	k8s.io/api v0.27.3 | ||||
| 	k8s.io/apimachinery v0.27.3 | ||||
| 	k8s.io/client-go v0.27.3 | ||||
| 	sigs.k8s.io/controller-runtime v0.15.0 | ||||
| ) | ||||
| 
 | ||||
| require ( | ||||
| 	cloud.google.com/go v0.110.2 // indirect | ||||
| 	cloud.google.com/go/compute v1.19.0 // indirect | ||||
| 	cloud.google.com/go/compute/metadata v0.2.3 // indirect | ||||
| 	cloud.google.com/go/iam v1.0.1 // indirect | ||||
| 	github.com/Azure/azure-amqp-common-go/v4 v4.2.0 // indirect | ||||
| 	github.com/Azure/azure-sdk-for-go v65.0.0+incompatible // indirect | ||||
| 	github.com/Azure/go-amqp v1.0.0 // indirect | ||||
| 	github.com/Azure/go-autorest v14.2.0+incompatible // indirect | ||||
| 	github.com/Azure/go-autorest/autorest v0.11.28 // indirect | ||||
| 	github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect | ||||
| 	github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect | ||||
| 	github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect | ||||
| 	github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect | ||||
| 	github.com/Azure/go-autorest/logger v0.2.1 // indirect | ||||
| 	github.com/Azure/go-autorest/tracing v0.6.0 // indirect | ||||
| 	github.com/Masterminds/semver/v3 v3.2.1 // indirect | ||||
| 	github.com/Microsoft/go-winio v0.6.1 // indirect | ||||
| 	github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect | ||||
| 	github.com/acomagu/bufpipe v1.0.4 // indirect | ||||
| 	github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect | ||||
| 	github.com/cloudflare/circl v1.3.3 // indirect | ||||
| 	github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect | ||||
| 	github.com/cyphar/filepath-securejoin v0.2.3 // indirect | ||||
| 	github.com/davecgh/go-spew v1.1.1 // indirect | ||||
| 	github.com/devigned/tab v0.1.1 // indirect | ||||
| 	github.com/docker/cli v23.0.1+incompatible // indirect | ||||
| 	github.com/docker/distribution v2.8.1+incompatible // indirect | ||||
| 	github.com/docker/docker v23.0.1+incompatible // indirect | ||||
| 	github.com/docker/docker-credential-helpers v0.7.0 // indirect | ||||
| 	github.com/emicklei/go-restful/v3 v3.10.0 // indirect | ||||
| 	github.com/emirpasic/gods v1.18.1 // indirect | ||||
| 	github.com/evanphx/json-patch/v5 v5.6.0 // indirect | ||||
| 	github.com/fluxcd/pkg/apis/acl v0.1.0 // indirect | ||||
| 	github.com/fluxcd/pkg/apis/kustomize v1.1.0 // indirect | ||||
| 	github.com/fluxcd/pkg/ssh v0.7.4 // indirect | ||||
| 	github.com/fluxcd/pkg/version v0.2.2 // indirect | ||||
| 	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect | ||||
| 	github.com/go-git/go-billy/v5 v5.4.1 // indirect | ||||
| 	github.com/go-logr/logr v1.2.4 // indirect | ||||
| 	github.com/go-openapi/jsonpointer v0.19.6 // indirect | ||||
| 	github.com/go-openapi/jsonreference v0.20.1 // indirect | ||||
| 	github.com/go-openapi/swag v0.22.3 // indirect | ||||
| 	github.com/gogo/protobuf v1.3.2 // indirect | ||||
| 	github.com/golang-jwt/jwt/v4 v4.4.2 // indirect | ||||
| 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect | ||||
| 	github.com/golang/protobuf v1.5.3 // indirect | ||||
| 	github.com/google/gnostic v0.6.9 // indirect | ||||
| 	github.com/google/go-cmp v0.5.9 // indirect | ||||
| 	github.com/google/gofuzz v1.2.0 // indirect | ||||
| 	github.com/google/s2a-go v0.1.4 // indirect | ||||
| 	github.com/google/uuid v1.3.0 // indirect | ||||
| 	github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect | ||||
| 	github.com/googleapis/gax-go/v2 v2.9.1 // 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.5.0 // indirect | ||||
| 	github.com/hashicorp/terraform-exec v0.18.1 // indirect | ||||
| 	github.com/imdario/mergo v0.3.15 // indirect | ||||
| 	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect | ||||
| 	github.com/josharian/intern v1.0.0 // indirect | ||||
| 	github.com/jpillora/backoff 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.16.0 // indirect | ||||
| 	github.com/mailru/easyjson v0.7.7 // indirect | ||||
| 	github.com/mitchellh/go-homedir v1.1.0 // indirect | ||||
| 	github.com/mitchellh/mapstructure v1.5.0 // indirect | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	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-rc2 // indirect | ||||
| 	github.com/pjbgf/sha1cd v0.3.0 // indirect | ||||
| 	github.com/pkg/errors v0.9.1 // indirect | ||||
| 	github.com/sergi/go-diff v1.3.1 // indirect | ||||
| 	github.com/sirupsen/logrus v1.9.0 // indirect | ||||
| 	github.com/skeema/knownhosts v1.1.1 // indirect | ||||
| 	github.com/spf13/pflag v1.0.5 // indirect | ||||
| 	github.com/vbatts/tar-split v0.11.2 // indirect | ||||
| 	github.com/xanzy/ssh-agent v0.3.3 // indirect | ||||
| 	github.com/zclconf/go-cty v1.13.0 // indirect | ||||
| 	go.opencensus.io v0.24.0 // indirect | ||||
| 	golang.org/x/crypto v0.9.0 // indirect | ||||
| 	golang.org/x/mod v0.10.0 // indirect | ||||
| 	golang.org/x/net v0.10.0 // indirect | ||||
| 	golang.org/x/oauth2 v0.8.0 // indirect | ||||
| 	golang.org/x/sync v0.2.0 // indirect | ||||
| 	golang.org/x/sys v0.8.0 // indirect | ||||
| 	golang.org/x/term v0.8.0 // indirect | ||||
| 	golang.org/x/text v0.9.0 // indirect | ||||
| 	golang.org/x/time v0.3.0 // indirect | ||||
| 	golang.org/x/tools v0.9.1 // indirect | ||||
| 	google.golang.org/api v0.124.0 // indirect | ||||
| 	google.golang.org/appengine v1.6.7 // indirect | ||||
| 	google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect | ||||
| 	google.golang.org/protobuf v1.30.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.27.3 // indirect | ||||
| 	k8s.io/klog/v2 v2.100.1 // indirect | ||||
| 	k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect | ||||
| 	k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect | ||||
| 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect | ||||
| 	sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect | ||||
| 	sigs.k8s.io/yaml v1.3.0 // indirect | ||||
| ) | ||||
| @ -0,0 +1,613 @@ | ||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= | ||||
| cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= | ||||
| cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= | ||||
| cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= | ||||
| cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= | ||||
| cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= | ||||
| cloud.google.com/go/iam v1.0.1 h1:lyeCAU6jpnVNrE9zGQkTl3WgNgK/X+uWwaw0kynZJMU= | ||||
| cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= | ||||
| cloud.google.com/go/kms v1.10.2 h1:8UePKEypK3SQ6g+4mn/s/VgE5L7XOh+FwGGRUqvY3Hw= | ||||
| cloud.google.com/go/pubsub v1.31.0 h1:aXdyyJz90kA+bor9+6+xHAciMD5mj8v15WqFZ5E0sek= | ||||
| cloud.google.com/go/pubsub v1.31.0/go.mod h1:dYmJ3K97NCQ/e4OwZ20rD4Ym3Bu8Gu9m/aJdWQjdcks= | ||||
| github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= | ||||
| github.com/Azure/azure-amqp-common-go/v4 v4.2.0 h1:q/jLx1KJ8xeI8XGfkOWMN9XrXzAfVTkyvCxPvHCjd2I= | ||||
| github.com/Azure/azure-amqp-common-go/v4 v4.2.0/go.mod h1:GD3m/WPPma+621UaU6KNjKEo5Hl09z86viKwQjTpV0Q= | ||||
| github.com/Azure/azure-event-hubs-go/v3 v3.6.0 h1:UXRi5KewXYoTiekVjrj0gyGfbyGvtbYdot6/4IMf4I4= | ||||
| github.com/Azure/azure-event-hubs-go/v3 v3.6.0/go.mod h1:UgyRnRU7H5e33igaLHJTqbkoNR1uj0j3MA/n7dABU24= | ||||
| github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw= | ||||
| github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= | ||||
| github.com/Azure/go-amqp v1.0.0 h1:QfCugi1M+4F2JDTRgVnRw7PYXLXZ9hmqk3+9+oJh3OA= | ||||
| github.com/Azure/go-amqp v1.0.0/go.mod h1:+bg0x3ce5+Q3ahCEXnCsGG3ETpDQe3MEVnOuT2ywPwc= | ||||
| github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= | ||||
| github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= | ||||
| github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= | ||||
| github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= | ||||
| github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= | ||||
| github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk= | ||||
| github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= | ||||
| github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 h1:iM6UAvjR97ZIeR93qTcwpKNMpV+/FTWjwEbuPD495Tk= | ||||
| github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 h1:LXl088ZQlP0SBppGFsRZonW6hSvwgL5gRByMbvUbx8U= | ||||
| github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= | ||||
| github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= | ||||
| github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= | ||||
| github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= | ||||
| github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= | ||||
| github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= | ||||
| github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= | ||||
| github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= | ||||
| github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= | ||||
| github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= | ||||
| github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= | ||||
| github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= | ||||
| github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= | ||||
| github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= | ||||
| 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/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= | ||||
| github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= | ||||
| github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= | ||||
| 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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||||
| github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= | ||||
| github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek= | ||||
| github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= | ||||
| github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= | ||||
| github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= | ||||
| github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= | ||||
| github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= | ||||
| github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= | ||||
| github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= | ||||
| github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= | ||||
| github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= | ||||
| github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= | ||||
| 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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= | ||||
| github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= | ||||
| github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= | ||||
| github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= | ||||
| github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | ||||
| github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= | ||||
| github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= | ||||
| github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||||
| github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= | ||||
| github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= | ||||
| github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= | ||||
| github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= | ||||
| github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= | ||||
| github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= | ||||
| github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= | ||||
| github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= | ||||
| github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= | ||||
| github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= | ||||
| github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= | ||||
| github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= | ||||
| github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= | ||||
| github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= | ||||
| github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= | ||||
| github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||||
| github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= | ||||
| github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/devigned/tab v0.1.1 h1:3mD6Kb1mUOYeLpJvTVSDwSg5ZsfSxfvxGRTxRsJsITA= | ||||
| github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= | ||||
| github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= | ||||
| github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM= | ||||
| github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= | ||||
| github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= | ||||
| github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= | ||||
| github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY= | ||||
| github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= | ||||
| github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= | ||||
| github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= | ||||
| github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= | ||||
| github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= | ||||
| github.com/emicklei/go-restful/v3 v3.10.0 h1:X4gma4HM7hFm6WMeAsTfqA0GOfdNoCzBIkHGoRLGXuM= | ||||
| github.com/emicklei/go-restful/v3 v3.10.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= | ||||
| github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= | ||||
| github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= | ||||
| github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= | ||||
| github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= | ||||
| github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= | ||||
| github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= | ||||
| github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= | ||||
| github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | ||||
| github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= | ||||
| github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= | ||||
| github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= | ||||
| github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= | ||||
| github.com/fluxcd/gitkit v0.6.0 h1:iNg5LTx6ePo+Pl0ZwqHTAkhbUHxGVSY3YCxCdw7VIFg= | ||||
| github.com/fluxcd/helm-controller/api v0.34.1 h1:oL6GG7weRIqkTlTFRoTY3DJpxqKpAFEoDDsYoxQCa8g= | ||||
| github.com/fluxcd/helm-controller/api v0.34.1/go.mod h1:1v1UqS5jOgWdMDzvJBgdcY40RminDUO6A0by4IkHd5s= | ||||
| github.com/fluxcd/image-automation-controller/api v0.34.1 h1:Y2RFhmltELcSGm3lsGgmRcJCBf0SdGlH/PwCxewk4w0= | ||||
| github.com/fluxcd/image-automation-controller/api v0.34.1/go.mod h1:vRJscxpWXuDMmBj8vlFM3pgpVHqh5hf65RVnCaOnSGo= | ||||
| github.com/fluxcd/image-reflector-controller/api v0.28.0 h1:2a1UxPU1RHTxl+5YFFB0KuOCHrR3hL0B7fvAPJo2UXY= | ||||
| github.com/fluxcd/image-reflector-controller/api v0.28.0/go.mod h1:bY28TT8Jv/pvdF/m+c64QCEiCY2BShMe22l1zRDYm4s= | ||||
| github.com/fluxcd/kustomize-controller/api v1.0.0-rc.4 h1:e5dO5HaFISFNzhfi4zuPniE545vVnEi3VnSBcbxcZqU= | ||||
| github.com/fluxcd/kustomize-controller/api v1.0.0-rc.4/go.mod h1:UTJu1JMr+rkabWkUWMNiOeFeDu+4ZKfJIK+oqEwOjzc= | ||||
| github.com/fluxcd/notification-controller/api v1.0.0-rc.4 h1:bDqIirpscGUY0+1u+RKvTEmX43iiZ2xeQLz27FoJJD8= | ||||
| github.com/fluxcd/notification-controller/api v1.0.0-rc.4/go.mod h1:XL5h5/x46e41rtNc8mwxTKX4kAtgTNuzJS0PV2c0iIQ= | ||||
| github.com/fluxcd/pkg/apis/acl v0.1.0 h1:EoAl377hDQYL3WqanWCdifauXqXbMyFuK82NnX6pH4Q= | ||||
| github.com/fluxcd/pkg/apis/acl v0.1.0/go.mod h1:zfEZzz169Oap034EsDhmCAGgnWlcWmIObZjYMusoXS8= | ||||
| github.com/fluxcd/pkg/apis/event v0.5.1 h1:UrEmKwTK/lt42gMZunl8BQBMzjf8PSqGbWDs/GB839c= | ||||
| github.com/fluxcd/pkg/apis/event v0.5.1/go.mod h1:GzBAzS8bq7751wvNkaSnr3kuwFVuWTPL20D77UgSNJQ= | ||||
| github.com/fluxcd/pkg/apis/kustomize v1.1.0 h1:Fbv4dCB57r2+fiusozN7at8r7upTz58Z4wWw1njHPyU= | ||||
| github.com/fluxcd/pkg/apis/kustomize v1.1.0/go.mod h1:CAe9Mjf9KVoTm1V4wpvq/FGXFDSnpBwfww/IG7mw3gM= | ||||
| github.com/fluxcd/pkg/apis/meta v1.1.1 h1:sLAKLbEu7rRzJ+Mytffu3NcpfdbOBTa6hcpOQzFWm+M= | ||||
| github.com/fluxcd/pkg/apis/meta v1.1.1/go.mod h1:soCfzjFWbm1mqybDcOywWKTCEYlH3skpoNGTboVk234= | ||||
| github.com/fluxcd/pkg/git v0.12.2 h1:96xH3hy3WfwiD0DioyJZcGapYT3lmPc2s7jU5UM8buw= | ||||
| github.com/fluxcd/pkg/git v0.12.2/go.mod h1:9TG4fEfGCF1XHLt9Xs7X2YOmkmWOiwfjH9tdGIQs8/8= | ||||
| github.com/fluxcd/pkg/git/gogit v0.12.0 h1:0mCwQND0WpCVZYHLWcXJxRvKVcyWxH4JjMQFMaea8Q4= | ||||
| github.com/fluxcd/pkg/git/gogit v0.12.0/go.mod h1:Kn+GfYfZBBIaXmQj39cQvrDxT/6y8leQxXZ5/B+YYTQ= | ||||
| github.com/fluxcd/pkg/gittestserver v0.8.4 h1:rA/QUZnfH77ZZG+5xfMqjgEHJdzeeE6Nn1o8cops/bU= | ||||
| github.com/fluxcd/pkg/runtime v0.39.0 h1:vgmzYS+DT0w8ikX9MqGsOdmMagoiKys2RMGdl/EDbgc= | ||||
| github.com/fluxcd/pkg/runtime v0.39.0/go.mod h1:0A/0kZv/MPciAj5AoSEDKVeqUFEF6371q7o+zk6l81g= | ||||
| github.com/fluxcd/pkg/ssh v0.7.4 h1:8GYneCKH2dxrHQBalcDgOCC2NtqD0JO91FlWgvnzrfo= | ||||
| github.com/fluxcd/pkg/ssh v0.7.4/go.mod h1:9Syc8nVJaZEToPTU4E99j0jZ99w39oZtov+uiNX17sc= | ||||
| github.com/fluxcd/pkg/version v0.2.2 h1:ZpVXECeLA5hIQMft11iLp6gN3cKcz6UNuVTQPw/bRdI= | ||||
| github.com/fluxcd/pkg/version v0.2.2/go.mod h1:NGnh/no8S6PyfCDxRFrPY3T5BUnqP48MxfxNRU0z8C0= | ||||
| github.com/fluxcd/source-controller/api v1.0.0-rc.5 h1:muoGOb/VitVEIOh877Ledi9AvibbyevPqvuH5byWU6g= | ||||
| github.com/fluxcd/source-controller/api v1.0.0-rc.5/go.mod h1:W6tNXr3mRPhdc5+Jke9OZnuk/3THNzGzRJVhAtLfzss= | ||||
| github.com/fluxcd/test-infra/tftestenv v0.0.0-20230531151340-931581bd0a3e h1:71jXb0t9pQAGleqRklVtdW38nXVTZ/KqeLSENnBldNk= | ||||
| github.com/fluxcd/test-infra/tftestenv v0.0.0-20230531151340-931581bd0a3e/go.mod h1:liFlLEXgambGVdWSJ4JzbIHf1Vjpp1HwUyPazPIVZug= | ||||
| github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= | ||||
| github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= | ||||
| github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= | ||||
| github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= | ||||
| github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= | ||||
| github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= | ||||
| github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= | ||||
| 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.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= | ||||
| github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= | ||||
| github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= | ||||
| github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= | ||||
| github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= | ||||
| github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= | ||||
| github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= | ||||
| github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE= | ||||
| github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8= | ||||
| github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= | ||||
| github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= | ||||
| github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= | ||||
| github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= | ||||
| github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= | ||||
| github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= | ||||
| github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= | ||||
| github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= | ||||
| github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= | ||||
| github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= | ||||
| github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= | ||||
| github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= | ||||
| 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/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= | ||||
| github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= | ||||
| github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= | ||||
| github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= | ||||
| github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | ||||
| github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= | ||||
| github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= | ||||
| github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | ||||
| github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | ||||
| github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | ||||
| github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | ||||
| github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | ||||
| github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= | ||||
| github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||||
| github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||||
| github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= | ||||
| github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||||
| github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= | ||||
| github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||||
| github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||
| github.com/google/go-containerregistry v0.14.0 h1:z58vMqHxuwvAsVwvKEkmVBz2TlgBgH5k6koEXBtlYkw= | ||||
| github.com/google/go-containerregistry v0.14.0/go.mod h1:aiJ2fp/SXvkWgmYHioXnbMdlgB8eXiiYOY55gfN91Wk= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= | ||||
| github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= | ||||
| github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= | ||||
| github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= | ||||
| github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | ||||
| github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= | ||||
| github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= | ||||
| github.com/googleapis/gax-go/v2 v2.9.1 h1:DpTpJqzZ3NvX9zqjhIuI1oVzYZMvboZe+3LoeEIJjHM= | ||||
| github.com/googleapis/gax-go/v2 v2.9.1/go.mod h1:4FG3gMrVZlyMp5itSYKMU9z/lBE7+SbnUOvzH2HqbEY= | ||||
| github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= | ||||
| github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= | ||||
| github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | ||||
| github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= | ||||
| github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | ||||
| github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= | ||||
| github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= | ||||
| github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= | ||||
| github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= | ||||
| github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= | ||||
| github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= | ||||
| github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= | ||||
| github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= | ||||
| github.com/hashicorp/hc-install v0.5.0 h1:D9bl4KayIYKEeJ4vUDe9L5huqxZXczKaykSRcmQ0xY0= | ||||
| github.com/hashicorp/hc-install v0.5.0/go.mod h1:JyzMfbzfSBSjoDCRPna1vi/24BEDxFaCPfdHtM5SCdo= | ||||
| github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= | ||||
| github.com/hashicorp/terraform-exec v0.18.1 h1:LAbfDvNQU1l0NOQlTuudjczVhHj061fNX5H8XZxHlH4= | ||||
| github.com/hashicorp/terraform-exec v0.18.1/go.mod h1:58wg4IeuAJ6LVsLUeD2DWZZoc/bYi6dzhLHzxM41980= | ||||
| github.com/hashicorp/terraform-json v0.16.0 h1:UKkeWRWb23do5LNAFlh/K3N0ymn1qTOO8c+85Albo3s= | ||||
| github.com/hashicorp/terraform-json v0.16.0/go.mod h1:v0Ufk9jJnk6tcIZvScHvetlKfiNTC+WS21mnXIlc0B0= | ||||
| github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= | ||||
| github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= | ||||
| github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= | ||||
| github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= | ||||
| github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= | ||||
| github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= | ||||
| github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= | ||||
| github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= | ||||
| github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= | ||||
| github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= | ||||
| github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= | ||||
| github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= | ||||
| github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= | ||||
| github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= | ||||
| github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= | ||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | ||||
| github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= | ||||
| github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= | ||||
| github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= | ||||
| github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= | ||||
| github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | ||||
| github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= | ||||
| github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= | ||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | ||||
| github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||
| github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= | ||||
| github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= | ||||
| github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | ||||
| github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||
| github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= | ||||
| github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= | ||||
| github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= | ||||
| github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= | ||||
| github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= | ||||
| github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= | ||||
| github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= | ||||
| github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 h1:YH424zrwLTlyHSH/GzLMJeu5zhYVZSx5RQxGKm1h96s= | ||||
| github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5/go.mod h1:PoGiBqKSQK1vIfQ+yVaFcGjDySHvym6FM1cNYnwzbrY= | ||||
| github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= | ||||
| github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= | ||||
| 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/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= | ||||
| github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | ||||
| github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= | ||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= | ||||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | ||||
| github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= | ||||
| github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= | ||||
| github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= | ||||
| github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= | ||||
| github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= | ||||
| github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= | ||||
| github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= | ||||
| github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= | ||||
| github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= | ||||
| github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= | ||||
| github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= | ||||
| github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= | ||||
| github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= | ||||
| github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= | ||||
| github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||
| github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= | ||||
| github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= | ||||
| github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= | ||||
| github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= | ||||
| github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= | ||||
| github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||||
| github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= | ||||
| github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= | ||||
| github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= | ||||
| github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= | ||||
| github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | ||||
| github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= | ||||
| github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= | ||||
| github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= | ||||
| github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= | ||||
| github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE= | ||||
| github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= | ||||
| github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= | ||||
| github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= | ||||
| github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||||
| github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||
| github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||||
| 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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||
| github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| 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= | ||||
| github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||
| github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||||
| github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= | ||||
| github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= | ||||
| github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= | ||||
| github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= | ||||
| github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= | ||||
| github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= | ||||
| github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= | ||||
| github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= | ||||
| github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= | ||||
| github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= | ||||
| github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= | ||||
| github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | ||||
| github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0= | ||||
| github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= | ||||
| go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= | ||||
| go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= | ||||
| go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= | ||||
| go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= | ||||
| go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= | ||||
| go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= | ||||
| golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||
| 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-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= | ||||
| golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= | ||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||
| golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||
| golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||
| golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||
| golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= | ||||
| golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= | ||||
| golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= | ||||
| golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= | ||||
| golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | ||||
| golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||||
| golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||||
| golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= | ||||
| golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||||
| golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | ||||
| 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-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||
| golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= | ||||
| golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= | ||||
| golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||||
| golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||||
| golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||||
| golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= | ||||
| golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | ||||
| golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= | ||||
| golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= | ||||
| golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= | ||||
| golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= | ||||
| golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= | ||||
| golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||
| golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= | ||||
| golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= | ||||
| golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= | ||||
| golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= | ||||
| golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||||
| golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= | ||||
| golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||
| golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||
| golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | ||||
| golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= | ||||
| golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | ||||
| golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= | ||||
| golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= | ||||
| golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||
| golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||||
| golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= | ||||
| golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= | ||||
| golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= | ||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= | ||||
| google.golang.org/api v0.124.0 h1:dP6Ef1VgOGqQ8eiv4GiY8RhmeyqzovcXBYPDUYG8Syo= | ||||
| google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= | ||||
| google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= | ||||
| google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||
| google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||
| google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||
| google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= | ||||
| google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= | ||||
| google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= | ||||
| google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= | ||||
| google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||
| google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= | ||||
| google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= | ||||
| google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||
| google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= | ||||
| google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= | ||||
| google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= | ||||
| google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= | ||||
| google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= | ||||
| google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= | ||||
| google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= | ||||
| google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | ||||
| google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | ||||
| google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||||
| google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | ||||
| google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | ||||
| google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= | ||||
| google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | ||||
| google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | ||||
| google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | ||||
| google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= | ||||
| google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= | ||||
| 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-20200227125254-8fa46927fb4f/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/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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.3.0/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= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= | ||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= | ||||
| k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= | ||||
| k8s.io/apiextensions-apiserver v0.27.3 h1:xAwC1iYabi+TDfpRhxh4Eapl14Hs2OftM2DN5MpgKX4= | ||||
| k8s.io/apiextensions-apiserver v0.27.3/go.mod h1:BH3wJ5NsB9XE1w+R6SSVpKmYNyIiyIz9xAmBl8Mb+84= | ||||
| k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= | ||||
| k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= | ||||
| k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= | ||||
| k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= | ||||
| k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= | ||||
| k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= | ||||
| k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= | ||||
| k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= | ||||
| k8s.io/utils v0.0.0-20230505201702-9f6742963106 h1:EObNQ3TW2D+WptiYXlApGNLVy0zm/JIBVY9i+M4wpAU= | ||||
| k8s.io/utils v0.0.0-20230505201702-9f6742963106/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= | ||||
| sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= | ||||
| sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= | ||||
| sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= | ||||
| sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= | ||||
| sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= | ||||
| sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= | ||||
| sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= | ||||
| sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= | ||||
| @ -0,0 +1,192 @@ | ||||
| /* | ||||
| Copyright 2023 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 ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	. "github.com/onsi/gomega" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 
 | ||||
| 	automationv1beta1 "github.com/fluxcd/image-automation-controller/api/v1beta1" | ||||
| 	reflectorv1beta2 "github.com/fluxcd/image-reflector-controller/api/v1beta2" | ||||
| 	"github.com/fluxcd/pkg/apis/meta" | ||||
| 	sourcev1 "github.com/fluxcd/source-controller/api/v1" | ||||
| ) | ||||
| 
 | ||||
| func TestImageRepositoryAndAutomation(t *testing.T) { | ||||
| 	g := NewWithT(t) | ||||
| 	ctx := context.TODO() | ||||
| 	branchName := "image-repository" | ||||
| 	testID := branchName + "-" + randStringRunes(5) | ||||
| 	imageURL := fmt.Sprintf("%s/podinfo", cfg.testRegistry) | ||||
| 
 | ||||
| 	manifest := fmt.Sprintf(`apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: podinfo | ||||
|   namespace: %[1]s | ||||
| spec: | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: podinfo | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: podinfo | ||||
|     spec: | ||||
|       containers: | ||||
|       - name: podinfod | ||||
|         image: %[2]s:%[3]s # {"$imagepolicy": "%[1]s:podinfo"} | ||||
|         readinessProbe: | ||||
|           exec: | ||||
|             command: | ||||
|             - podcli | ||||
|             - check | ||||
|             - http | ||||
|             - localhost:9898/readyz | ||||
|           initialDelaySeconds: 5 | ||||
|           timeoutSeconds: 5 | ||||
| `, testID, imageURL, oldPodinfoVersion) | ||||
| 
 | ||||
| 	repoUrl := getTransportURL(cfg.applicationRepository) | ||||
| 	client, err := getRepository(ctx, t.TempDir(), repoUrl, defaultBranch, cfg.defaultAuthOpts) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 	files := make(map[string]io.Reader) | ||||
| 	files[testID+"/podinfo.yaml"] = strings.NewReader(manifest) | ||||
| 
 | ||||
| 	err = commitAndPushAll(ctx, client, files, branchName) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	err = setUpFluxConfig(ctx, testID, nsConfig{ | ||||
| 		repoURL: repoUrl, | ||||
| 		path:    testID, | ||||
| 		ref: &sourcev1.GitRepositoryRef{ | ||||
| 			Branch: branchName, | ||||
| 		}, | ||||
| 	}) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 	t.Cleanup(func() { | ||||
| 		err := tearDownFluxConfig(ctx, testID) | ||||
| 		if err != nil { | ||||
| 			t.Logf("failed to delete resources in '%s' namespace: %s", testID, err) | ||||
| 		} | ||||
| 	}) | ||||
| 
 | ||||
| 	g.Eventually(func() bool { | ||||
| 		err := verifyGitAndKustomization(ctx, testEnv.Client, testID, testID) | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		return true | ||||
| 	}, testTimeout, testInterval).Should(BeTrue()) | ||||
| 
 | ||||
| 	imageRepository := reflectorv1beta2.ImageRepository{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name:      "podinfo", | ||||
| 			Namespace: testID, | ||||
| 		}, | ||||
| 		Spec: reflectorv1beta2.ImageRepositorySpec{ | ||||
| 			Image: imageURL, | ||||
| 			Interval: metav1.Duration{ | ||||
| 				Duration: 1 * time.Minute, | ||||
| 			}, | ||||
| 			Provider: infraOpts.Provider, | ||||
| 		}, | ||||
| 	} | ||||
| 	g.Expect(testEnv.Create(ctx, &imageRepository)).To(Succeed()) | ||||
| 	defer testEnv.Delete(ctx, &imageRepository) | ||||
| 
 | ||||
| 	imagePolicy := reflectorv1beta2.ImagePolicy{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name:      "podinfo", | ||||
| 			Namespace: testID, | ||||
| 		}, | ||||
| 		Spec: reflectorv1beta2.ImagePolicySpec{ | ||||
| 			ImageRepositoryRef: meta.NamespacedObjectReference{ | ||||
| 				Name: imageRepository.Name, | ||||
| 			}, | ||||
| 			Policy: reflectorv1beta2.ImagePolicyChoice{ | ||||
| 				SemVer: &reflectorv1beta2.SemVerPolicy{ | ||||
| 					Range: "6.0.x", | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	g.Expect(testEnv.Create(ctx, &imagePolicy)).To(Succeed()) | ||||
| 	defer testEnv.Delete(ctx, &imagePolicy) | ||||
| 
 | ||||
| 	imageAutomation := automationv1beta1.ImageUpdateAutomation{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name:      "podinfo", | ||||
| 			Namespace: testID, | ||||
| 		}, | ||||
| 		Spec: automationv1beta1.ImageUpdateAutomationSpec{ | ||||
| 			Interval: metav1.Duration{ | ||||
| 				Duration: 1 * time.Minute, | ||||
| 			}, | ||||
| 			SourceRef: automationv1beta1.CrossNamespaceSourceReference{ | ||||
| 				Kind: "GitRepository", | ||||
| 				Name: testID, | ||||
| 			}, | ||||
| 			GitSpec: &automationv1beta1.GitSpec{ | ||||
| 				Checkout: &automationv1beta1.GitCheckoutSpec{ | ||||
| 					Reference: sourcev1.GitRepositoryRef{ | ||||
| 						Branch: branchName, | ||||
| 					}, | ||||
| 				}, | ||||
| 				Commit: automationv1beta1.CommitSpec{ | ||||
| 					Author: automationv1beta1.CommitUser{ | ||||
| 						Email: "imageautomation@example.com", | ||||
| 						Name:  "imageautomation", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			Update: &automationv1beta1.UpdateStrategy{ | ||||
| 				Path:     testID, | ||||
| 				Strategy: automationv1beta1.UpdateStrategySetters, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	g.Expect(testEnv.Create(ctx, &imageAutomation)).To(Succeed()) | ||||
| 	defer testEnv.Delete(ctx, &imageAutomation) | ||||
| 
 | ||||
| 	// Wait for image repository to be ready
 | ||||
| 	g.Eventually(func() bool { | ||||
| 		client, err := getRepository(ctx, t.TempDir(), repoUrl, branchName, cfg.defaultAuthOpts) | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		b, err := os.ReadFile(filepath.Join(client.Path(), testID, "podinfo.yaml")) | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		if bytes.Contains(b, []byte(newPodinfoVersion)) == false { | ||||
| 			return false | ||||
| 		} | ||||
| 		return true | ||||
| 	}, testTimeout, testInterval).Should(BeTrue()) | ||||
| } | ||||
| @ -0,0 +1,204 @@ | ||||
| /* | ||||
| Copyright 2023 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" | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	. "github.com/onsi/gomega" | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/types" | ||||
| 
 | ||||
| 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" | ||||
| 	notiv1 "github.com/fluxcd/notification-controller/api/v1" | ||||
| 	notiv1beta2 "github.com/fluxcd/notification-controller/api/v1beta2" | ||||
| 	events "github.com/fluxcd/pkg/apis/event/v1beta1" | ||||
| 	"github.com/fluxcd/pkg/apis/meta" | ||||
| 	sourcev1 "github.com/fluxcd/source-controller/api/v1" | ||||
| ) | ||||
| 
 | ||||
| func TestNotification(t *testing.T) { | ||||
| 	g := NewWithT(t) | ||||
| 
 | ||||
| 	ctx := context.TODO() | ||||
| 	branchName := "test-notification" | ||||
| 	testID := branchName + "-" + randStringRunes(5) | ||||
| 	defer cfg.notificationCfg.closeChan() | ||||
| 
 | ||||
| 	// Setup Flux resources
 | ||||
| 	manifest := `apiVersion: v1 | ||||
| kind: ConfigMap | ||||
| metadata: | ||||
|   name: foobar` | ||||
| 	repoUrl := getTransportURL(cfg.applicationRepository) | ||||
| 	client, err := getRepository(ctx, t.TempDir(), repoUrl, defaultBranch, cfg.defaultAuthOpts) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 	files := make(map[string]io.Reader) | ||||
| 	files["configmap.yaml"] = strings.NewReader(manifest) | ||||
| 	err = commitAndPushAll(ctx, client, files, branchName) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	namespace := corev1.Namespace{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name: testID, | ||||
| 		}, | ||||
| 	} | ||||
| 	g.Expect(testEnv.Create(ctx, &namespace)).To(Succeed()) | ||||
| 	defer testEnv.Delete(ctx, &namespace) | ||||
| 
 | ||||
| 	provider := notiv1beta2.Provider{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name:      testID, | ||||
| 			Namespace: testID, | ||||
| 		}, | ||||
| 		Spec: notiv1beta2.ProviderSpec{ | ||||
| 			Type:    cfg.notificationCfg.providerType, | ||||
| 			Address: cfg.notificationCfg.providerAddress, | ||||
| 			Channel: cfg.notificationCfg.providerChannel, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.notificationCfg.secret != nil { | ||||
| 		secret := corev1.Secret{ | ||||
| 			ObjectMeta: metav1.ObjectMeta{ | ||||
| 				Name:      testID, | ||||
| 				Namespace: testID, | ||||
| 			}, | ||||
| 			StringData: cfg.notificationCfg.secret, | ||||
| 		} | ||||
| 
 | ||||
| 		g.Expect(testEnv.Create(ctx, &secret)).To(Succeed()) | ||||
| 		defer testEnv.Delete(ctx, &secret) | ||||
| 
 | ||||
| 		provider.Spec.SecretRef = &meta.LocalObjectReference{ | ||||
| 			Name: testID, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	g.Expect(testEnv.Create(ctx, &provider)).To(Succeed()) | ||||
| 	defer testEnv.Delete(ctx, &provider) | ||||
| 
 | ||||
| 	alert := notiv1beta2.Alert{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name:      testID, | ||||
| 			Namespace: testID, | ||||
| 		}, | ||||
| 		Spec: notiv1beta2.AlertSpec{ | ||||
| 			ProviderRef: meta.LocalObjectReference{ | ||||
| 				Name: provider.Name, | ||||
| 			}, | ||||
| 			EventSources: []notiv1.CrossNamespaceObjectReference{ | ||||
| 				{ | ||||
| 					Kind:      "Kustomization", | ||||
| 					Name:      testID, | ||||
| 					Namespace: testID, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	g.Expect(testEnv.Create(ctx, &alert)).ToNot(HaveOccurred()) | ||||
| 	defer testEnv.Delete(ctx, &alert) | ||||
| 
 | ||||
| 	g.Eventually(func() bool { | ||||
| 		nn := types.NamespacedName{Name: provider.Name, Namespace: provider.Namespace} | ||||
| 		obj := ¬iv1beta2.Provider{} | ||||
| 		err := testEnv.Get(ctx, nn, obj) | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		if err := checkReadyCondition(obj); err != nil { | ||||
| 			t.Log(err) | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		nn = types.NamespacedName{Name: alert.Name, Namespace: alert.Namespace} | ||||
| 		alertObj := ¬iv1beta2.Alert{} | ||||
| 		err = testEnv.Get(ctx, nn, alertObj) | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		if err := checkReadyCondition(alertObj); err != nil { | ||||
| 			t.Log(err) | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		return true | ||||
| 	}, testTimeout, testInterval).Should(BeTrue()) | ||||
| 
 | ||||
| 	modifyKsSpec := func(spec *kustomizev1.KustomizationSpec) { | ||||
| 		spec.Interval = metav1.Duration{Duration: 30 * time.Second} | ||||
| 		spec.HealthChecks = []meta.NamespacedObjectKindReference{ | ||||
| 			{ | ||||
| 				APIVersion: "v1", | ||||
| 				Kind:       "ConfigMap", | ||||
| 				Name:       "foobar", | ||||
| 				Namespace:  testID, | ||||
| 			}, | ||||
| 		} | ||||
| 	} | ||||
| 	g.Expect(setUpFluxConfig(ctx, testID, nsConfig{ | ||||
| 		repoURL: repoUrl, | ||||
| 		ref: &sourcev1.GitRepositoryRef{ | ||||
| 			Branch: branchName, | ||||
| 		}, | ||||
| 		path:         "./", | ||||
| 		modifyKsSpec: modifyKsSpec, | ||||
| 	})).To(Succeed()) | ||||
| 	t.Cleanup(func() { | ||||
| 		err := tearDownFluxConfig(ctx, testID) | ||||
| 		if err != nil { | ||||
| 			t.Logf("failed to delete resources in '%s' namespace: %s", testID, err) | ||||
| 		} | ||||
| 	}) | ||||
| 
 | ||||
| 	g.Eventually(func() bool { | ||||
| 		err := verifyGitAndKustomization(ctx, testEnv, testID, testID) | ||||
| 		if err != nil { | ||||
| 			t.Log(err) | ||||
| 			return false | ||||
| 		} | ||||
| 		return true | ||||
| 	}, testTimeout, testInterval).Should(BeTrue()) | ||||
| 
 | ||||
| 	// Wait to read event from notification channel.
 | ||||
| 	g.Eventually(func() bool { | ||||
| 		select { | ||||
| 		case eventJson := <-cfg.notificationCfg.notificationChan: | ||||
| 			event := &events.Event{} | ||||
| 			err := json.Unmarshal([]byte(eventJson), event) | ||||
| 			if err != nil { | ||||
| 				t.Logf("the received event type does not match Flux format, error: %v", err) | ||||
| 				return false | ||||
| 			} | ||||
| 
 | ||||
| 			if event.InvolvedObject.Kind == kustomizev1.KustomizationKind && | ||||
| 				event.InvolvedObject.Name == testID && event.InvolvedObject.Namespace == testID { | ||||
| 				return true | ||||
| 			} | ||||
| 
 | ||||
| 			return false | ||||
| 		default: | ||||
| 			return false | ||||
| 		} | ||||
| 	}, testTimeout, 1*time.Second).Should(BeTrue()) | ||||
| } | ||||
| @ -0,0 +1,137 @@ | ||||
| /* | ||||
| Copyright 2023 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" | ||||
| 	"time" | ||||
| 
 | ||||
| 	. "github.com/onsi/gomega" | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/types" | ||||
| 
 | ||||
| 	helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" | ||||
| 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" | ||||
| ) | ||||
| 
 | ||||
| func TestOCIHelmRelease(t *testing.T) { | ||||
| 	g := NewWithT(t) | ||||
| 	ctx := context.TODO() | ||||
| 
 | ||||
| 	// Create namespace for test
 | ||||
| 	testID := "oci-helm-" + randStringRunes(5) | ||||
| 	namespace := corev1.Namespace{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name: testID, | ||||
| 		}, | ||||
| 	} | ||||
| 	g.Expect(testEnv.Create(ctx, &namespace)).To(Succeed()) | ||||
| 	defer testEnv.Delete(ctx, &namespace) | ||||
| 
 | ||||
| 	repoURL := fmt.Sprintf("%s/charts/podinfo", cfg.testRegistry) | ||||
| 	err := pushImagesFromURL(repoURL, "ghcr.io/stefanprodan/charts/podinfo:6.2.0", []string{"6.2.0"}) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	// Create HelmRepository and wait for it to sync
 | ||||
| 	helmRepository := sourcev1.HelmRepository{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{Name: testID, Namespace: testID}, | ||||
| 		Spec: sourcev1.HelmRepositorySpec{ | ||||
| 			URL: fmt.Sprintf("oci://%s", cfg.testRegistry), | ||||
| 			Interval: metav1.Duration{ | ||||
| 				Duration: 5 * time.Minute, | ||||
| 			}, | ||||
| 			Provider:        infraOpts.Provider, | ||||
| 			PassCredentials: true, | ||||
| 			Type:            "oci", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	g.Expect(testEnv.Create(ctx, &helmRepository)).To(Succeed()) | ||||
| 	defer testEnv.Delete(ctx, &helmRepository) | ||||
| 
 | ||||
| 	g.Eventually(func() bool { | ||||
| 		obj := &sourcev1.HelmRepository{} | ||||
| 		nn := types.NamespacedName{Name: helmRepository.Name, Namespace: helmRepository.Namespace} | ||||
| 		err := testEnv.Get(ctx, nn, obj) | ||||
| 		if err != nil { | ||||
| 			t.Logf("error getting helm repository %s", err.Error()) | ||||
| 			return false | ||||
| 		} | ||||
| 		if err := checkReadyCondition(obj); err != nil { | ||||
| 			t.Logf("%v", err) | ||||
| 			return false | ||||
| 		} | ||||
| 		return true | ||||
| 	}, testTimeout, testInterval).Should(BeTrue()) | ||||
| 
 | ||||
| 	// create helm release
 | ||||
| 	helmRelease := helmv2.HelmRelease{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{Name: testID, Namespace: testID}, | ||||
| 		Spec: helmv2.HelmReleaseSpec{ | ||||
| 			Chart: helmv2.HelmChartTemplate{ | ||||
| 				Spec: helmv2.HelmChartTemplateSpec{ | ||||
| 					Interval: &metav1.Duration{ | ||||
| 						Duration: 10 * time.Minute, | ||||
| 					}, | ||||
| 					Chart:   "charts/podinfo", | ||||
| 					Version: "6.2.0", | ||||
| 					SourceRef: helmv2.CrossNamespaceObjectReference{ | ||||
| 						Kind:      sourcev1.HelmRepositoryKind, | ||||
| 						Name:      helmRepository.Name, | ||||
| 						Namespace: helmRepository.Namespace, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	g.Expect(testEnv.Create(ctx, &helmRelease)).To(Succeed()) | ||||
| 	defer testEnv.Delete(ctx, &helmRelease) | ||||
| 
 | ||||
| 	g.Eventually(func() bool { | ||||
| 		chart := &sourcev1.HelmChart{} | ||||
| 		nn := types.NamespacedName{ | ||||
| 			Name:      fmt.Sprintf("%s-%s", helmRelease.Name, helmRelease.Namespace), | ||||
| 			Namespace: helmRelease.Namespace, | ||||
| 		} | ||||
| 		if err := testEnv.Get(ctx, nn, chart); err != nil { | ||||
| 			t.Logf("error getting helm chart %s\n", err.Error()) | ||||
| 			return false | ||||
| 		} | ||||
| 		if err := checkReadyCondition(chart); err != nil { | ||||
| 			t.Log(err) | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		obj := &helmv2.HelmRelease{} | ||||
| 		nn = types.NamespacedName{Name: helmRelease.Name, Namespace: helmRelease.Namespace} | ||||
| 		if err := testEnv.Get(ctx, nn, obj); err != nil { | ||||
| 			t.Logf("error getting helm release %s\n", err.Error()) | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		if err := checkReadyCondition(obj); err != nil { | ||||
| 			t.Log(err) | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		return true | ||||
| 	}, testTimeout, testInterval).Should(BeTrue()) | ||||
| } | ||||
| @ -0,0 +1,138 @@ | ||||
| /* | ||||
| Copyright 2023 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" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	. "github.com/onsi/gomega" | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/types" | ||||
| 
 | ||||
| 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" | ||||
| 	"github.com/fluxcd/pkg/apis/meta" | ||||
| 	sourcev1 "github.com/fluxcd/source-controller/api/v1" | ||||
| 	"github.com/fluxcd/test-infra/tftestenv" | ||||
| ) | ||||
| 
 | ||||
| func TestKeyVaultSops(t *testing.T) { | ||||
| 	g := NewWithT(t) | ||||
| 	ctx := context.TODO() | ||||
| 	branchName := "key-vault" | ||||
| 	testID := branchName + "-" + randStringRunes(5) | ||||
| 	secretYaml := `apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: "test" | ||||
| stringData: | ||||
|   foo: "bar"` | ||||
| 
 | ||||
| 	repoUrl := getTransportURL(cfg.applicationRepository) | ||||
| 	tmpDir := t.TempDir() | ||||
| 	client, err := getRepository(ctx, tmpDir, repoUrl, defaultBranch, cfg.defaultAuthOpts) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	dir := client.Path() + "/key-vault-sops" | ||||
| 	g.Expect(os.Mkdir(dir, 0o700)).To(Succeed()) | ||||
| 
 | ||||
| 	filename := dir + "secret.enc.yaml" | ||||
| 	f, err := os.Create(filename) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 	defer f.Close() | ||||
| 
 | ||||
| 	_, err = f.Write([]byte(secretYaml)) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 	g.Expect(f.Sync()).To(Succeed()) | ||||
| 
 | ||||
| 	err = tftestenv.RunCommand(ctx, client.Path(), | ||||
| 		fmt.Sprintf("sops --encrypt --encrypted-regex '^(data|stringData)$' %s --in-place %s", cfg.sopsArgs, filename), | ||||
| 		tftestenv.RunCommandOptions{}) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	r, err := os.Open(filename) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	files := make(map[string]io.Reader) | ||||
| 	files["key-vault-sops/secret.enc.yaml"] = r | ||||
| 	err = commitAndPushAll(ctx, client, files, branchName) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 	modifyKsSpec := func(spec *kustomizev1.KustomizationSpec) { | ||||
| 		spec.Decryption = &kustomizev1.Decryption{ | ||||
| 			Provider: "sops", | ||||
| 		} | ||||
| 		if cfg.sopsSecretData != nil { | ||||
| 			spec.Decryption.SecretRef = &meta.LocalObjectReference{ | ||||
| 				Name: "sops-keys", | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	err = setUpFluxConfig(ctx, testID, nsConfig{ | ||||
| 		ref: &sourcev1.GitRepositoryRef{ | ||||
| 			Branch: branchName, | ||||
| 		}, | ||||
| 		repoURL:      repoUrl, | ||||
| 		path:         "./key-vault-sops", | ||||
| 		modifyKsSpec: modifyKsSpec, | ||||
| 		protocol:     cfg.defaultGitTransport, | ||||
| 	}) | ||||
| 	g.Expect(err).ToNot(HaveOccurred()) | ||||
| 	t.Cleanup(func() { | ||||
| 		err := tearDownFluxConfig(ctx, testID) | ||||
| 		if err != nil { | ||||
| 			log.Printf("failed to delete resources in '%s' namespace", testID) | ||||
| 		} | ||||
| 	}) | ||||
| 
 | ||||
| 	if cfg.sopsSecretData != nil { | ||||
| 		secret := corev1.Secret{ | ||||
| 			ObjectMeta: metav1.ObjectMeta{ | ||||
| 				Name:      "sops-keys", | ||||
| 				Namespace: testID, | ||||
| 			}, | ||||
| 			StringData: cfg.sopsSecretData, | ||||
| 		} | ||||
| 		g.Expect(testEnv.Create(ctx, &secret)).To(Succeed()) | ||||
| 		defer testEnv.Delete(ctx, &secret) | ||||
| 	} | ||||
| 
 | ||||
| 	g.Eventually(func() bool { | ||||
| 		err := verifyGitAndKustomization(ctx, testEnv.Client, testID, testID) | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		nn := types.NamespacedName{Name: "test", Namespace: testID} | ||||
| 		secret := &corev1.Secret{} | ||||
| 		err = testEnv.Get(ctx, nn, secret) | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		if string(secret.Data["foo"]) == "bar" { | ||||
| 			return true | ||||
| 		} | ||||
| 
 | ||||
| 		return false | ||||
| 	}, testTimeout, testInterval).Should(BeTrue()) | ||||
| } | ||||
| @ -0,0 +1,322 @@ | ||||
| /* | ||||
| Copyright 2023 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" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"math/rand" | ||||
| 	"os" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	tfjson "github.com/hashicorp/terraform-json" | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/client-go/kubernetes/scheme" | ||||
| 
 | ||||
| 	helmv2beta1 "github.com/fluxcd/helm-controller/api/v2beta1" | ||||
| 	automationv1beta1 "github.com/fluxcd/image-automation-controller/api/v1beta1" | ||||
| 	reflectorv1beta2 "github.com/fluxcd/image-reflector-controller/api/v1beta2" | ||||
| 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" | ||||
| 	notiv1beta2 "github.com/fluxcd/notification-controller/api/v1beta2" | ||||
| 	"github.com/fluxcd/pkg/git" | ||||
| 	sourcev1 "github.com/fluxcd/source-controller/api/v1" | ||||
| 	sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2" | ||||
| 	"github.com/fluxcd/test-infra/tftestenv" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	// azureTerraformPath is the path to the folder containing the
 | ||||
| 	// terraform files for azure infra
 | ||||
| 	azureTerraformPath = "./terraform/azure" | ||||
| 	// gcpTerraformPath is the path to the folder containing the
 | ||||
| 	// terraform files for gcp infra
 | ||||
| 	gcpTerraformPath = "./terraform/gcp" | ||||
| 
 | ||||
| 	// kubeconfigPath is the path of the file containing the kubeconfig
 | ||||
| 	kubeconfigPath = "./build/kubeconfig" | ||||
| 
 | ||||
| 	// default branch to be used when cloning git repositories
 | ||||
| 	defaultBranch = "main" | ||||
| 
 | ||||
| 	// envVarGitRepoSSHPath is the environment variable that contains the path
 | ||||
| 	// to the ssh key for the git repository
 | ||||
| 	envVarGitRepoSSHPath = "GITREPO_SSH_PATH" | ||||
| 	// envVarGitRepoSSHPubPath is the environment variable that contains the path
 | ||||
| 	// to the ssh public key for the git repository
 | ||||
| 	envVarGitRepoSSHPubPath = "GITREPO_SSH_PUB_PATH" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// supportedProviders are the providers supported by the test.
 | ||||
| 	supportedProviders = []string{"azure", "gcp"} | ||||
| 
 | ||||
| 	// cfg is a struct containing different variables needed for the test.
 | ||||
| 	cfg *testConfig | ||||
| 
 | ||||
| 	// infraOpts are the options for running the terraform environment
 | ||||
| 	infraOpts tftestenv.Options | ||||
| 
 | ||||
| 	// versions to tag and push for the podinfo image
 | ||||
| 	oldPodinfoVersion = "6.0.0" | ||||
| 	newPodinfoVersion = "6.0.1" | ||||
| 	podinfoTags       = []string{oldPodinfoVersion, newPodinfoVersion} | ||||
| 
 | ||||
| 	// testEnv is the test environment. It contains test infrastructure and
 | ||||
| 	// kubernetes client of the created cluster.
 | ||||
| 	testEnv *tftestenv.Environment | ||||
| 
 | ||||
| 	// testTimeout is used as a timeout when testing a condition with gomega's eventually
 | ||||
| 	testTimeout = 60 * time.Second | ||||
| 	// testInterval is used as an interval when testing a condition with gomega's eventually
 | ||||
| 	testInterval = 5 * time.Second | ||||
| 
 | ||||
| 	random *rand.Rand | ||||
| 
 | ||||
| 	letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890") | ||||
| 
 | ||||
| 	localImg = "ghcr.io/stefanprodan/podinfo" | ||||
| ) | ||||
| 
 | ||||
| // testConfig hold different variable that will be needed by the different test functions.
 | ||||
| type testConfig struct { | ||||
| 	// authentication info for git repositories
 | ||||
| 	gitPat                string | ||||
| 	gitUsername           string | ||||
| 	gitPrivateKey         string | ||||
| 	gitPublicKey          string | ||||
| 	defaultGitTransport   git.TransportType | ||||
| 	defaultAuthOpts       *git.AuthOptions | ||||
| 	knownHosts            string | ||||
| 	fleetInfraRepository  gitUrl | ||||
| 	applicationRepository gitUrl | ||||
| 
 | ||||
| 	// sopsArgs is the cloud provider dependent argument to pass to the sops cli
 | ||||
| 	sopsArgs string | ||||
| 
 | ||||
| 	// notificationCfg contains the values needed to properly set up notification on the
 | ||||
| 	// cluster.
 | ||||
| 	notificationCfg notificationConfig | ||||
| 
 | ||||
| 	// sopsSecretData is the secret's data for the sops decryption
 | ||||
| 	sopsSecretData map[string]string | ||||
| 	// kustomizationYaml is the  content of the kustomization.yaml for customizing the Flux manifests
 | ||||
| 	kustomizationYaml string | ||||
| 
 | ||||
| 	// testRegistry is the registry of the cloud provider.
 | ||||
| 	testRegistry string | ||||
| } | ||||
| 
 | ||||
| // notificationConfig contains various fields for configuring
 | ||||
| // providers and testing notifications for the different
 | ||||
| // cloud providers.
 | ||||
| type notificationConfig struct { | ||||
| 	providerChannel  string | ||||
| 	providerType     string | ||||
| 	providerAddress  string | ||||
| 	secret           map[string]string | ||||
| 	notificationChan chan []byte | ||||
| 	closeChan        func() | ||||
| } | ||||
| 
 | ||||
| // gitUrl contains the http/ssh urls for the created git repositories
 | ||||
| // on the various cloud providers.
 | ||||
| type gitUrl struct { | ||||
| 	http string | ||||
| 	ssh  string | ||||
| } | ||||
| 
 | ||||
| // getTestConfig gets the test configuration that contains different variables for running the tests
 | ||||
| type getTestConfig func(ctx context.Context, output map[string]*tfjson.StateOutput) (*testConfig, error) | ||||
| 
 | ||||
| // registryLoginFunc is used to perform registry login against a provider based
 | ||||
| // on the terraform state output values. It returns the test registry
 | ||||
| // to test against, read from the terraform state output.
 | ||||
| type registryLoginFunc func(ctx context.Context, output map[string]*tfjson.StateOutput) (string, error) | ||||
| 
 | ||||
| // providerConfig contains the test configurations for the different cloud providers
 | ||||
| type providerConfig struct { | ||||
| 	terraformPath    string | ||||
| 	createKubeconfig tftestenv.CreateKubeconfig | ||||
| 	getTestConfig    getTestConfig | ||||
| 	// registryLogin is used to perform registry login.
 | ||||
| 	registryLogin registryLoginFunc | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	utilruntime.Must(sourcev1.AddToScheme(scheme.Scheme)) | ||||
| 	utilruntime.Must(sourcev1beta2.AddToScheme(scheme.Scheme)) | ||||
| 	utilruntime.Must(kustomizev1.AddToScheme(scheme.Scheme)) | ||||
| 	utilruntime.Must(helmv2beta1.AddToScheme(scheme.Scheme)) | ||||
| 	utilruntime.Must(reflectorv1beta2.AddToScheme(scheme.Scheme)) | ||||
| 	utilruntime.Must(automationv1beta1.AddToScheme(scheme.Scheme)) | ||||
| 	utilruntime.Must(notiv1beta2.AddToScheme(scheme.Scheme)) | ||||
| 
 | ||||
| 	random = rand.New(rand.NewSource(time.Now().UnixNano())) | ||||
| } | ||||
| 
 | ||||
| func TestMain(m *testing.M) { | ||||
| 	ctx := context.TODO() | ||||
| 
 | ||||
| 	infraOpts.Bindflags(flag.CommandLine) | ||||
| 	flag.Parse() | ||||
| 
 | ||||
| 	// Validate the provider.
 | ||||
| 	if infraOpts.Provider == "" { | ||||
| 		log.Fatalf("-provider flag must be set to one of %v", supportedProviders) | ||||
| 	} | ||||
| 	var supported bool | ||||
| 	for _, p := range supportedProviders { | ||||
| 		if p == infraOpts.Provider { | ||||
| 			supported = true | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if !supported { | ||||
| 		log.Fatalf("Unsupported provider %q, must be one of %v", infraOpts.Provider, supportedProviders) | ||||
| 	} | ||||
| 	// get provider specific configuration
 | ||||
| 	providerCfg := getProviderConfig(infraOpts.Provider) | ||||
| 	if providerCfg == nil { | ||||
| 		log.Fatalf("Failed to get provider config for %q", infraOpts.Provider) | ||||
| 	} | ||||
| 
 | ||||
| 	// Initialize with non-zero exit code to indicate failure by default unless
 | ||||
| 	// set by a successful test run.
 | ||||
| 	exitCode := 1 | ||||
| 
 | ||||
| 	// Setup Terraform binary and init state
 | ||||
| 	log.Printf("Setting up %s e2e test infrastructure", infraOpts.Provider) | ||||
| 	envOpts := []tftestenv.EnvironmentOption{ | ||||
| 		tftestenv.WithExisting(infraOpts.Existing), | ||||
| 		tftestenv.WithRetain(infraOpts.Retain), | ||||
| 		tftestenv.WithVerbose(infraOpts.Verbose), | ||||
| 		tftestenv.WithCreateKubeconfig(providerCfg.createKubeconfig), | ||||
| 	} | ||||
| 
 | ||||
| 	// Create terraform infrastructure
 | ||||
| 	var err error | ||||
| 	testEnv, err = tftestenv.New(ctx, scheme.Scheme, providerCfg.terraformPath, kubeconfigPath, envOpts...) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("Failed to provision the test infrastructure: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	defer func() { | ||||
| 		if err := testEnv.Stop(ctx); err != nil { | ||||
| 			log.Printf("Failed to stop environment: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		// Log the panic error before exit to surface the cause of panic.
 | ||||
| 		if err := recover(); err != nil { | ||||
| 			log.Printf("panic: %v", err) | ||||
| 		} | ||||
| 		os.Exit(exitCode) | ||||
| 	}() | ||||
| 
 | ||||
| 	// get terrraform infrastructure
 | ||||
| 	outputs, err := testEnv.StateOutput(ctx) | ||||
| 	if err != nil { | ||||
| 		panic(fmt.Sprintf("Failed to get the terraform state output: %v", err)) | ||||
| 	} | ||||
| 
 | ||||
| 	// get provider specific test configuration
 | ||||
| 	cfg, err = providerCfg.getTestConfig(ctx, outputs) | ||||
| 	if err != nil { | ||||
| 		panic(fmt.Sprintf("Failed to get test config: %v", err)) | ||||
| 	} | ||||
| 
 | ||||
| 	regUrl, err := providerCfg.registryLogin(ctx, outputs) | ||||
| 	if err != nil { | ||||
| 		panic(fmt.Sprintf("Failed to log into registry: %v", err)) | ||||
| 	} | ||||
| 
 | ||||
| 	cfg.testRegistry = regUrl | ||||
| 	err = pushTestImages(ctx, cfg.testRegistry, podinfoTags) | ||||
| 	if err != nil { | ||||
| 		panic(fmt.Sprintf("Failed to push test images: %v", err)) | ||||
| 	} | ||||
| 
 | ||||
| 	tmpDir, err := os.MkdirTemp("", "*-flux-test") | ||||
| 	if err != nil { | ||||
| 		panic(fmt.Sprintf("Failed to create tmp dir: %v", err)) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		err := os.RemoveAll(tmpDir) | ||||
| 		if err != nil { | ||||
| 			log.Printf("error removing tmp dir: %s\n", err) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	log.Println("Installing flux") | ||||
| 	err = installFlux(ctx, tmpDir, kubeconfigPath) | ||||
| 	defer func() { | ||||
| 		log.Println("Uninstalling Flux") | ||||
| 		if err := uninstallFlux(ctx); err != nil { | ||||
| 			log.Printf("Failed to uninstall: %v", err) | ||||
| 		} | ||||
| 	}() | ||||
| 	if err != nil { | ||||
| 		panic(fmt.Sprintf("error installing Flux: %v", err)) | ||||
| 	} | ||||
| 
 | ||||
| 	log.Println("Running e2e tests") | ||||
| 	exitCode = m.Run() | ||||
| } | ||||
| 
 | ||||
| func getProviderConfig(provider string) *providerConfig { | ||||
| 	switch provider { | ||||
| 	case "azure": | ||||
| 		return &providerConfig{ | ||||
| 			terraformPath:    azureTerraformPath, | ||||
| 			createKubeconfig: createKubeConfigAKS, | ||||
| 			getTestConfig:    getTestConfigAKS, | ||||
| 			registryLogin:    registryLoginACR, | ||||
| 		} | ||||
| 	case "gcp": | ||||
| 		return &providerConfig{ | ||||
| 			terraformPath:    gcpTerraformPath, | ||||
| 			createKubeconfig: createKubeConfigGKE, | ||||
| 			getTestConfig:    getTestConfigGKE, | ||||
| 			registryLogin:    registryLoginGCR, | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // pushTestImages pushes the local podinfo image to the remote repository specified
 | ||||
| // by repoURL. The image should be existing on the machine.
 | ||||
| func pushTestImages(ctx context.Context, repoURL string, tags []string) error { | ||||
| 	for _, tag := range tags { | ||||
| 		remoteImg := fmt.Sprintf("%s/podinfo:%s", repoURL, tag) | ||||
| 		err := tftestenv.RetagAndPush(ctx, fmt.Sprintf("%s:%s", localImg, tag), remoteImg) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func randStringRunes(n int) string { | ||||
| 	b := make([]rune, n) | ||||
| 	for i := range b { | ||||
| 		b[i] = letterRunes[random.Intn(len(letterRunes))] | ||||
| 	} | ||||
| 	return string(b) | ||||
| } | ||||
| @ -0,0 +1,20 @@ | ||||
| module "aks" { | ||||
|   source = "git::https://github.com/fluxcd/test-infra.git//tf-modules/azure/aks" | ||||
| 
 | ||||
|   name     = local.name | ||||
|   location = var.azure_location | ||||
|   tags     = var.tags | ||||
| } | ||||
| 
 | ||||
| module "acr" { | ||||
|   source = "git::https://github.com/fluxcd/test-infra.git//tf-modules/azure/acr" | ||||
| 
 | ||||
|   name             = local.name | ||||
|   location         = var.azure_location | ||||
|   aks_principal_id = [module.aks.principal_id] | ||||
|   resource_group   = module.aks.resource_group | ||||
|   admin_enabled    = true | ||||
|   tags             = var.tags | ||||
| 
 | ||||
|   depends_on = [module.aks] | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| resource "azuredevops_project" "e2e" { | ||||
|   name               = local.name | ||||
|   visibility         = "private" | ||||
|   version_control    = "Git" | ||||
|   work_item_template = "Agile" | ||||
|   description        = "Test Project for Flux E2E test - Managed by Terraform" | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| resource "azuredevops_git_repository" "fleet_infra" { | ||||
|   project_id     = azuredevops_project.e2e.id | ||||
|   name           = "fleet-infra-${local.name}" | ||||
|   default_branch = "refs/heads/main" | ||||
|   initialization { | ||||
|     init_type = "Clean" | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| resource "azuredevops_git_repository" "application" { | ||||
|   project_id     = azuredevops_project.e2e.id | ||||
|   name           = "application-${local.name}" | ||||
|   default_branch = "refs/heads/main" | ||||
|   initialization { | ||||
|     init_type = "Clean" | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,27 @@ | ||||
| resource "azurerm_eventhub_namespace" "this" { | ||||
|   name                = local.name | ||||
|   location            = var.azure_location | ||||
|   resource_group_name = module.aks.resource_group | ||||
|   sku                 = "Basic" | ||||
|   capacity            = 1 | ||||
|   tags                = var.tags | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| resource "azurerm_eventhub" "this" { | ||||
|   name                = local.name | ||||
|   namespace_name      = azurerm_eventhub_namespace.this.name | ||||
|   resource_group_name = module.aks.resource_group | ||||
|   partition_count     = 1 | ||||
|   message_retention   = 1 | ||||
| } | ||||
| 
 | ||||
| resource "azurerm_eventhub_authorization_rule" "this" { | ||||
|   name                = local.name | ||||
|   resource_group_name = module.aks.resource_group | ||||
|   namespace_name      = azurerm_eventhub_namespace.this.name | ||||
|   eventhub_name       = azurerm_eventhub.this.name | ||||
|   listen              = true | ||||
|   send                = true | ||||
|   manage              = false | ||||
| } | ||||
| @ -0,0 +1,61 @@ | ||||
| resource "azurerm_key_vault" "this" { | ||||
|   name                = local.name | ||||
|   resource_group_name = module.aks.resource_group | ||||
|   location            = var.azure_location | ||||
|   tenant_id           = data.azurerm_client_config.current.tenant_id | ||||
|   sku_name            = "standard" | ||||
|   tags                = var.tags | ||||
| } | ||||
| 
 | ||||
| resource "azurerm_key_vault_access_policy" "admin" { | ||||
|   key_vault_id = azurerm_key_vault.this.id | ||||
|   tenant_id    = data.azurerm_client_config.current.tenant_id | ||||
|   object_id    = data.azurerm_client_config.current.object_id | ||||
| 
 | ||||
|   key_permissions = [ | ||||
|     "Create", | ||||
|     "Update", | ||||
|     "Encrypt", | ||||
|     "Delete", | ||||
|     "Get", | ||||
|     "List", | ||||
|     "Purge", | ||||
|     "Recover", | ||||
|     "GetRotationPolicy", | ||||
|     "SetRotationPolicy" | ||||
|   ] | ||||
| 
 | ||||
|   secret_permissions = [ | ||||
|     "Get", | ||||
|     "Delete", | ||||
|     "Purge", | ||||
|     "Recover" | ||||
|   ] | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| resource "azurerm_key_vault_access_policy" "cluster_binding" { | ||||
|   key_vault_id = azurerm_key_vault.this.id | ||||
|   tenant_id    = data.azurerm_client_config.current.tenant_id | ||||
|   object_id    = module.aks.principal_id | ||||
| 
 | ||||
|   key_permissions = [ | ||||
|     "Decrypt", | ||||
|     "Encrypt", | ||||
|   ] | ||||
| } | ||||
| 
 | ||||
| resource "azurerm_key_vault_key" "sops" { | ||||
|   depends_on = [azurerm_key_vault_access_policy.admin] | ||||
| 
 | ||||
|   name         = "sops" | ||||
|   key_vault_id = azurerm_key_vault.this.id | ||||
|   key_type     = "RSA" | ||||
|   key_size     = 2048 | ||||
|   tags         = var.tags | ||||
| 
 | ||||
|   key_opts = [ | ||||
|     "decrypt", | ||||
|     "encrypt", | ||||
|   ] | ||||
| } | ||||
| @ -0,0 +1,35 @@ | ||||
| terraform { | ||||
|   required_providers { | ||||
|     azurerm = { | ||||
|       source  = "hashicorp/azurerm" | ||||
|       version = ">=3.20.0" | ||||
|     } | ||||
|     azuread = { | ||||
|       source  = "hashicorp/azuread" | ||||
|       version = ">=2.28.0" | ||||
|     } | ||||
|     azuredevops = { | ||||
|       source  = "microsoft/azuredevops" | ||||
|       version = ">=0.2.2" | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| provider "azurerm" { | ||||
|   features {} | ||||
| } | ||||
| 
 | ||||
| provider "azuredevops" { | ||||
|   org_service_url       = "https://dev.azure.com/${var.azuredevops_org}" | ||||
|   personal_access_token = var.azuredevops_pat | ||||
| } | ||||
| 
 | ||||
| data "azurerm_client_config" "current" {} | ||||
| 
 | ||||
| resource "random_pet" "suffix" { | ||||
|   separator = "o" | ||||
| } | ||||
| 
 | ||||
| locals { | ||||
|   name = "e2e${random_pet.suffix.id}" | ||||
| } | ||||
| @ -0,0 +1,41 @@ | ||||
| output "aks_kubeconfig" { | ||||
|   description = "kubeconfig of the created AKS cluster" | ||||
|   value       = module.aks.kubeconfig | ||||
|   sensitive   = true | ||||
| } | ||||
| 
 | ||||
| output "azure_devops_access_token" { | ||||
|   sensitive = true | ||||
|   value     = var.azuredevops_pat | ||||
| } | ||||
| 
 | ||||
| output "fleet_infra_repository" { | ||||
|   value = { | ||||
|     http = azuredevops_git_repository.fleet_infra.remote_url | ||||
|     ssh  = "ssh://git@ssh.dev.azure.com/v3/${var.azuredevops_org}/${azuredevops_git_repository.fleet_infra.project_id}/${azuredevops_git_repository.fleet_infra.name}" | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| output "application_repository" { | ||||
|   value = { | ||||
|     http = azuredevops_git_repository.application.remote_url | ||||
|     ssh  = "ssh://git@ssh.dev.azure.com/v3/${var.azuredevops_org}/${azuredevops_git_repository.application.project_id}/${azuredevops_git_repository.application.name}" | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| output "aks_client_id" { | ||||
|   value = module.aks.kubelet_client_id | ||||
| } | ||||
| 
 | ||||
| output "event_hub_sas" { | ||||
|   value     = azurerm_eventhub_authorization_rule.this.primary_connection_string | ||||
|   sensitive = true | ||||
| } | ||||
| 
 | ||||
| output "sops_id" { | ||||
|   value = azurerm_key_vault_key.sops.id | ||||
| } | ||||
| 
 | ||||
| output "acr_url" { | ||||
|   value = module.acr.registry_url | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| variable "azuredevops_org" { | ||||
|   type        = string | ||||
|   description = "Name of Azure DevOps organizations were the repositories will be created" | ||||
| } | ||||
| 
 | ||||
| variable "azure_location" { | ||||
|   type        = string | ||||
|   description = "Location of the resource group" | ||||
|   default     = "eastus" | ||||
| } | ||||
| 
 | ||||
| variable "tags" { | ||||
|   type        = map(string) | ||||
|   default     = {} | ||||
|   description = "Tags for created Azure resources" | ||||
| } | ||||
| 
 | ||||
| variable "azuredevops_pat" { | ||||
|   type        = string | ||||
|   description = "Personal access token for Azure DevOps repository" | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| module "gke" { | ||||
|   source = "git::https://github.com/fluxcd/test-infra.git//tf-modules/gcp/gke" | ||||
| 
 | ||||
|   name = local.name | ||||
|   tags = var.tags | ||||
| } | ||||
| 
 | ||||
| module "gcr" { | ||||
|   source = "git::https://github.com/fluxcd/test-infra.git//tf-modules/gcp/gcr" | ||||
| 
 | ||||
|   name = local.name | ||||
|   tags = var.tags | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| data "google_kms_key_ring" "keyring" { | ||||
|   name     = var.gcp_keyring | ||||
|   location = "global" | ||||
| } | ||||
| 
 | ||||
| data "google_kms_crypto_key" "my_crypto_key" { | ||||
|   name     = var.gcp_crypto_key | ||||
|   key_ring = data.google_kms_key_ring.keyring.id | ||||
| } | ||||
| 
 | ||||
| resource "google_kms_key_ring_iam_binding" "key_ring" { | ||||
|   key_ring_id = data.google_kms_key_ring.keyring.id | ||||
|   role        = "roles/cloudkms.cryptoKeyEncrypterDecrypter" | ||||
| 
 | ||||
|   members = [ | ||||
|     "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com", | ||||
|   ] | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| provider "google" { | ||||
|   project = var.gcp_project_id | ||||
|   region  = var.gcp_region | ||||
|   zone    = var.gcp_zone | ||||
| } | ||||
| 
 | ||||
| resource "random_pet" "suffix" {} | ||||
| 
 | ||||
| locals { | ||||
|   name = "e2e-${random_pet.suffix.id}" | ||||
| } | ||||
| 
 | ||||
| data "google_project" "project" {} | ||||
| @ -0,0 +1,32 @@ | ||||
| output "gke_kubeconfig" { | ||||
|   value     = module.gke.kubeconfig | ||||
|   sensitive = true | ||||
| } | ||||
| 
 | ||||
| output "gcp_project_id" { | ||||
|   value = var.gcp_project_id | ||||
| } | ||||
| 
 | ||||
| output "gcp_region" { | ||||
|   value = var.gcp_region | ||||
| } | ||||
| 
 | ||||
| output "artifact_registry_id" { | ||||
|   value = module.gcr.artifact_repository_id | ||||
| } | ||||
| 
 | ||||
| output "sops_id" { | ||||
|   value = data.google_kms_crypto_key.my_crypto_key.id | ||||
| } | ||||
| 
 | ||||
| output "fleet_infra_repository" { | ||||
|   value = "ssh://${var.gcp_email}@source.developers.google.com:2022/p/${var.gcp_project_id}/r/${google_sourcerepo_repository.fleet-infra.name}" | ||||
| } | ||||
| 
 | ||||
| output "application_repository" { | ||||
|   value = "ssh://${var.gcp_email}@source.developers.google.com:2022/p/${var.gcp_project_id}/r/${google_sourcerepo_repository.application.name}" | ||||
| } | ||||
| 
 | ||||
| output "pubsub_topic" { | ||||
|   value = google_pubsub_topic.pubsub.name | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| resource "google_pubsub_topic" "pubsub" { | ||||
|   name                       = local.name | ||||
|   labels                     = var.tags | ||||
|   message_retention_duration = "7200s" | ||||
| } | ||||
| 
 | ||||
| resource "google_pubsub_subscription" "sub" { | ||||
|   project = var.gcp_project_id | ||||
|   name    = local.name | ||||
|   topic   = google_pubsub_topic.pubsub.name | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| resource "google_sourcerepo_repository" "fleet-infra" { | ||||
|   name = "fleet-infra-${random_pet.suffix.id}" | ||||
| } | ||||
| 
 | ||||
| resource "google_sourcerepo_repository" "application" { | ||||
|   name = "application-${random_pet.suffix.id}" | ||||
| } | ||||
| 
 | ||||
| resource "google_sourcerepo_repository_iam_binding" "application_binding" { | ||||
|   project = google_sourcerepo_repository.application.project | ||||
|   repository = google_sourcerepo_repository.application.name | ||||
|   role = "roles/source.admin" | ||||
|   members = [ | ||||
|     "user:${var.gcp_email}", | ||||
|   ] | ||||
| } | ||||
| 
 | ||||
| resource "google_sourcerepo_repository_iam_binding" "fleet-infra_binding" { | ||||
|   project = google_sourcerepo_repository.fleet-infra.project | ||||
|   repository = google_sourcerepo_repository.fleet-infra.name | ||||
|   role = "roles/source.admin" | ||||
|   members = [ | ||||
|     "user:${var.gcp_email}", | ||||
|   ] | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,37 @@ | ||||
| variable "gcp_project_id" { | ||||
|   type        = string | ||||
|   description = "GCP project to create the resources in" | ||||
| } | ||||
| 
 | ||||
| variable "gcp_email" { | ||||
|   type        = string | ||||
|   description = "GCP user email" | ||||
| } | ||||
| 
 | ||||
| variable "gcp_region" { | ||||
|   type        = string | ||||
|   default     = "us-central1" | ||||
|   description = "GCP region" | ||||
| } | ||||
| 
 | ||||
| variable "gcp_zone" { | ||||
|   type        = string | ||||
|   default     = "us-central1" | ||||
|   description = "GCP zone" | ||||
| } | ||||
| 
 | ||||
| variable "gcp_keyring" { | ||||
|   type        = string | ||||
|   description = "GCP keyring that contains crypto key for encrypting secrets" | ||||
| } | ||||
| 
 | ||||
| variable "gcp_crypto_key" { | ||||
|   type        = string | ||||
|   description = "GCP crypto key for encrypting secrets" | ||||
| } | ||||
| 
 | ||||
| variable "tags" { | ||||
|   type        = map(string) | ||||
|   default     = {} | ||||
|   description = "tags for created resources" | ||||
| } | ||||
| @ -0,0 +1,413 @@ | ||||
| /* | ||||
| Copyright 2023 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" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	extgogit "github.com/go-git/go-git/v5" | ||||
| 	"github.com/go-git/go-git/v5/plumbing" | ||||
| 	"github.com/go-git/go-git/v5/plumbing/object" | ||||
| 	"github.com/google/go-containerregistry/pkg/crane" | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	apierrors "k8s.io/apimachinery/pkg/api/errors" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/types" | ||||
| 	kerrors "k8s.io/apimachinery/pkg/util/errors" | ||||
| 	"sigs.k8s.io/controller-runtime/pkg/client" | ||||
| 
 | ||||
| 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" | ||||
| 	"github.com/fluxcd/pkg/apis/meta" | ||||
| 	"github.com/fluxcd/pkg/git" | ||||
| 	"github.com/fluxcd/pkg/git/gogit" | ||||
| 	"github.com/fluxcd/pkg/git/repository" | ||||
| 	"github.com/fluxcd/pkg/runtime/conditions" | ||||
| 	sourcev1 "github.com/fluxcd/source-controller/api/v1" | ||||
| 	"github.com/fluxcd/test-infra/tftestenv" | ||||
| ) | ||||
| 
 | ||||
| // installFlux adds the core Flux components to the cluster specified in the kubeconfig file.
 | ||||
| func installFlux(ctx context.Context, tmpDir string, kubeconfigPath string) error { | ||||
| 	// Create flux-system namespace
 | ||||
| 	namespace := corev1.Namespace{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name: "flux-system", | ||||
| 		}, | ||||
| 	} | ||||
| 	err := testEnv.Create(ctx, &namespace) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	repoURL := getTransportURL(cfg.fleetInfraRepository) | ||||
| 	if cfg.kustomizationYaml != "" { | ||||
| 		files := make(map[string]io.Reader) | ||||
| 		files["clusters/e2e/flux-system/kustomization.yaml"] = strings.NewReader(cfg.kustomizationYaml) | ||||
| 		files["clusters/e2e/flux-system/gotk-components.yaml"] = strings.NewReader("") | ||||
| 		files["clusters/e2e/flux-system/gotk-sync.yaml"] = strings.NewReader("") | ||||
| 		c, err := getRepository(ctx, tmpDir, repoURL, defaultBranch, cfg.defaultAuthOpts) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		err = commitAndPushAll(ctx, c, files, defaultBranch) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var bootstrapArgs string | ||||
| 	if cfg.defaultGitTransport == git.SSH { | ||||
| 		f, err := os.CreateTemp("", "flux-e2e-ssh-key-*") | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		err = os.WriteFile(f.Name(), []byte(cfg.gitPrivateKey), 0o644) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		bootstrapArgs = fmt.Sprintf("--private-key-file=%s -s", f.Name()) | ||||
| 	} else { | ||||
| 		bootstrapArgs = fmt.Sprintf("--token-auth --password=%s", cfg.gitPat) | ||||
| 	} | ||||
| 
 | ||||
| 	bootstrapCmd := fmt.Sprintf("./build/flux bootstrap git  --url=%s %s --kubeconfig=%s --path=clusters/e2e "+ | ||||
| 		" --components-extra image-reflector-controller,image-automation-controller", | ||||
| 		repoURL, bootstrapArgs, kubeconfigPath) | ||||
| 
 | ||||
| 	return tftestenv.RunCommand(ctx, "./", bootstrapCmd, tftestenv.RunCommandOptions{ | ||||
| 		Timeout: 15 * time.Minute, | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func uninstallFlux(ctx context.Context) error { | ||||
| 	uninstallCmd := fmt.Sprintf("./build/flux uninstall --kubeconfig %s -s", kubeconfigPath) | ||||
| 	if err := tftestenv.RunCommand(ctx, "./", uninstallCmd, tftestenv.RunCommandOptions{ | ||||
| 		Timeout: 15 * time.Minute, | ||||
| 	}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // verifyGitAndKustomization checks that the gitrespository and kustomization combination are working properly.
 | ||||
| func verifyGitAndKustomization(ctx context.Context, kubeClient client.Client, namespace, name string) error { | ||||
| 	nn := types.NamespacedName{ | ||||
| 		Name:      name, | ||||
| 		Namespace: namespace, | ||||
| 	} | ||||
| 	source := &sourcev1.GitRepository{} | ||||
| 	if err := kubeClient.Get(ctx, nn, source); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := checkReadyCondition(source); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	kustomization := &kustomizev1.Kustomization{} | ||||
| 	if err := kubeClient.Get(ctx, nn, kustomization); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := checkReadyCondition(kustomization); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| type nsConfig struct { | ||||
| 	repoURL      string | ||||
| 	ref          *sourcev1.GitRepositoryRef | ||||
| 	protocol     git.TransportType | ||||
| 	objectName   string | ||||
| 	path         string | ||||
| 	modifyKsSpec func(spec *kustomizev1.KustomizationSpec) | ||||
| } | ||||
| 
 | ||||
| // setUpFluxConfigs creates the namespace, then creates the git secret,
 | ||||
| // git repository and kustomization in that namespace
 | ||||
| func setUpFluxConfig(ctx context.Context, name string, opts nsConfig) error { | ||||
| 	transport := cfg.defaultGitTransport | ||||
| 	if opts.protocol != "" { | ||||
| 		transport = opts.protocol | ||||
| 	} | ||||
| 
 | ||||
| 	namespace := corev1.Namespace{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name: name, | ||||
| 		}, | ||||
| 	} | ||||
| 	if err := testEnv.Create(ctx, &namespace); err != nil && !apierrors.IsAlreadyExists(err) { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	secret := corev1.Secret{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name:      "git-credentials", | ||||
| 			Namespace: name, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	secret.StringData = map[string]string{ | ||||
| 		"username": cfg.gitUsername, | ||||
| 		"password": cfg.gitPat, | ||||
| 	} | ||||
| 
 | ||||
| 	if transport == git.SSH { | ||||
| 		secret.StringData = map[string]string{ | ||||
| 			"identity":     cfg.gitPrivateKey, | ||||
| 			"identity.pub": cfg.gitPublicKey, | ||||
| 			"known_hosts":  cfg.knownHosts, | ||||
| 		} | ||||
| 	} | ||||
| 	if err := testEnv.Create(ctx, &secret); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	ref := &sourcev1.GitRepositoryRef{ | ||||
| 		Branch: name, | ||||
| 	} | ||||
| 
 | ||||
| 	if opts.ref != nil { | ||||
| 		ref = opts.ref | ||||
| 	} | ||||
| 
 | ||||
| 	gitSpec := &sourcev1.GitRepositorySpec{ | ||||
| 		Interval: metav1.Duration{ | ||||
| 			Duration: 1 * time.Minute, | ||||
| 		}, | ||||
| 		Reference: ref, | ||||
| 		SecretRef: &meta.LocalObjectReference{ | ||||
| 			Name: secret.Name, | ||||
| 		}, | ||||
| 		URL: opts.repoURL, | ||||
| 	} | ||||
| 
 | ||||
| 	source := &sourcev1.GitRepository{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace.Name}, | ||||
| 		Spec:       *gitSpec, | ||||
| 	} | ||||
| 	if err := testEnv.Create(ctx, source); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	ksSpec := &kustomizev1.KustomizationSpec{ | ||||
| 		Path:            opts.path, | ||||
| 		TargetNamespace: name, | ||||
| 		SourceRef: kustomizev1.CrossNamespaceSourceReference{ | ||||
| 			Kind:      sourcev1.GitRepositoryKind, | ||||
| 			Name:      source.Name, | ||||
| 			Namespace: source.Namespace, | ||||
| 		}, | ||||
| 		Interval: metav1.Duration{ | ||||
| 			Duration: 1 * time.Minute, | ||||
| 		}, | ||||
| 		Prune: true, | ||||
| 	} | ||||
| 	if opts.modifyKsSpec != nil { | ||||
| 		opts.modifyKsSpec(ksSpec) | ||||
| 	} | ||||
| 	kustomization := &kustomizev1.Kustomization{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace.Name}, | ||||
| 		Spec:       *ksSpec, | ||||
| 	} | ||||
| 
 | ||||
| 	return testEnv.Create(ctx, kustomization) | ||||
| } | ||||
| 
 | ||||
| func tearDownFluxConfig(ctx context.Context, name string) error { | ||||
| 	var allErr []error | ||||
| 
 | ||||
| 	source := &sourcev1.GitRepository{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: name}} | ||||
| 	if err := testEnv.Delete(ctx, source); err != nil { | ||||
| 		allErr = append(allErr, err) | ||||
| 	} | ||||
| 
 | ||||
| 	kustomization := &kustomizev1.Kustomization{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: name}} | ||||
| 	if err := testEnv.Delete(ctx, kustomization); err != nil { | ||||
| 		allErr = append(allErr, err) | ||||
| 	} | ||||
| 
 | ||||
| 	namespace := corev1.Namespace{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name: name, | ||||
| 		}, | ||||
| 	} | ||||
| 	if err := testEnv.Delete(ctx, &namespace); err != nil { | ||||
| 		allErr = append(allErr, err) | ||||
| 	} | ||||
| 
 | ||||
| 	return kerrors.NewAggregate(allErr) | ||||
| } | ||||
| 
 | ||||
| // getRepository and clones the git repository to the directory.
 | ||||
| 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 checks out to the specified branch, creates the files, commits and then pushes them to
 | ||||
| // the remote git repository.
 | ||||
| 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 | ||||
| } | ||||
| 
 | ||||
| func createTagAndPush(ctx context.Context, client *gogit.Client, branchName, newTag string) error { | ||||
| 	repo, err := extgogit.PlainOpen(client.Path()) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	ref, err := repo.Reference(plumbing.NewBranchReferenceName(branchName), false) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	tags, err := repo.TagObjects() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	err = tags.ForEach(func(tag *object.Tag) error { | ||||
| 		if tag.Name == newTag { | ||||
| 			err = repo.DeleteTag(tag.Name) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return nil | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error deleting local tag: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Delete remote tag
 | ||||
| 	if err := client.Push(ctx, repository.PushConfig{ | ||||
| 		Refspecs: []string{fmt.Sprintf(":refs/tags/%s", newTag)}, | ||||
| 		Force:    true, | ||||
| 	}); err != nil && !errors.Is(err, extgogit.NoErrAlreadyUpToDate) { | ||||
| 		return fmt.Errorf("unable to delete existing tag: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	sig := &object.Signature{ | ||||
| 		Name:  git.DefaultPublicKeyAuthUser, | ||||
| 		Email: "test@example.com", | ||||
| 		When:  time.Now(), | ||||
| 	} | ||||
| 	if _, err = repo.CreateTag(newTag, ref.Hash(), &extgogit.CreateTagOptions{ | ||||
| 		Tagger:  sig, | ||||
| 		Message: "create tag", | ||||
| 	}); err != nil { | ||||
| 		return fmt.Errorf("unable to create tag: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return client.Push(ctx, repository.PushConfig{ | ||||
| 		Refspecs: []string{"refs/tags/*:refs/tags/*"}, | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func pushImagesFromURL(repoURL, imgURL string, tags []string) error { | ||||
| 	img, err := crane.Pull(imgURL) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tag := range tags { | ||||
| 		if err := crane.Push(img, fmt.Sprintf("%s:%s", repoURL, tag)); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func getTransportURL(urls gitUrl) string { | ||||
| 	if cfg.defaultGitTransport == git.SSH { | ||||
| 		return urls.ssh | ||||
| 	} | ||||
| 
 | ||||
| 	return urls.http | ||||
| } | ||||
| 
 | ||||
| func authOpts(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) | ||||
| } | ||||
| 
 | ||||
| // checkReadyCondition checks for a Ready condition, it returns nil if the condition is true
 | ||||
| // or an error (with the message if the Ready condition is present).
 | ||||
| func checkReadyCondition(from conditions.Getter) error { | ||||
| 	if conditions.IsReady(from) { | ||||
| 		return nil | ||||
| 	} | ||||
| 	errMsg := fmt.Sprintf("object not ready") | ||||
| 	readyMsg := conditions.GetMessage(from, meta.ReadyCondition) | ||||
| 	if readyMsg != "" { | ||||
| 		errMsg += ": " + readyMsg | ||||
| 	} | ||||
| 	return errors.New(errMsg) | ||||
| } | ||||
					Loading…
					
					
				
		Reference in New Issue