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