/* Copyright 2021 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 test import ( "bytes" "context" b64 "encoding/base64" "encoding/json" "fmt" "log" "os" "path/filepath" "strings" "testing" "time" eventhub "github.com/Azure/azure-event-hubs-go/v3" "github.com/hashicorp/terraform-exec/tfexec" "github.com/hashicorp/terraform-exec/tfinstall" git2go "github.com/libgit2/git2go/v31" "github.com/microsoft/azure-devops-go-api/azuredevops" "github.com/microsoft/azure-devops-go-api/azuredevops/git" "github.com/stretchr/testify/require" giturls "github.com/whilp/git-urls" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" automationv1beta1 "github.com/fluxcd/image-automation-controller/api/v1beta1" reflectorv1beta1 "github.com/fluxcd/image-reflector-controller/api/v1beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2" notiv1beta1 "github.com/fluxcd/notification-controller/api/v1beta1" "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/runtime/events" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" ) type config struct { kubeconfigPath string kubeClient client.Client azdoPat string idRsa string idRsaPub string knownHosts string fleetInfraRepository repoConfig applicationRepository repoConfig fluxAzureSp spConfig sopsId string acr acrConfig eventHubSas string } type spConfig struct { tenantId string clientId string clientSecret string } type repoConfig struct { http string ssh string } type acrConfig struct { url string username string password string } var cfg config func TestMain(m *testing.M) { ctx := context.TODO() log.Println("Setting up Azure test infrastructure") execPath, err := tfinstall.Find(ctx, &whichTerraform{}) if err != nil { log.Fatalf("terraform exec path not found: %v", err) } tf, err := tfexec.NewTerraform("./terraform/aks", execPath) if err != nil { log.Fatalf("could not create terraform instance: %v", err) } log.Println("Init Terraform") err = tf.Init(ctx, tfexec.Upgrade(true)) if err != nil { log.Fatalf("error running init: %v", err) } log.Println("Applying Terraform") err = tf.Apply(ctx) if err != nil { log.Fatalf("error running apply: %v", err) } state, err := tf.Show(ctx) if err != nil { log.Fatalf("error running show: %v", err) } outputs := state.Values.Outputs kubeconfig := outputs["aks_kube_config"].Value.(string) aksHost := outputs["aks_host"].Value.(string) aksCert := outputs["aks_client_certificate"].Value.(string) aksKey := outputs["aks_client_key"].Value.(string) aksCa := outputs["aks_cluster_ca_certificate"].Value.(string) azdoPat := outputs["shared_pat"].Value.(string) idRsa := outputs["shared_id_rsa"].Value.(string) idRsaPub := outputs["shared_id_rsa_pub"].Value.(string) fleetInfraRepository := outputs["fleet_infra_repository"].Value.(map[string]interface{}) applicationRepository := outputs["application_repository"].Value.(map[string]interface{}) fluxAzureSp := outputs["flux_azure_sp"].Value.(map[string]interface{}) sharedSopsId := outputs["sops_id"].Value.(string) acr := outputs["acr"].Value.(map[string]interface{}) eventHubSas := outputs["event_hub_sas"].Value.(string) log.Println("Creating Kubernetes client") kubeconfigPath, kubeClient, err := getKubernetesCredentials(kubeconfig, aksHost, aksCert, aksKey, aksCa) if err != nil { log.Fatalf("error create Kubernetes client: %v", err) } defer os.RemoveAll(filepath.Dir(kubeconfigPath)) cfg = config{ kubeconfigPath: kubeconfigPath, kubeClient: kubeClient, azdoPat: azdoPat, idRsa: idRsa, idRsaPub: idRsaPub, knownHosts: "ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H", fleetInfraRepository: repoConfig{ http: fleetInfraRepository["http"].(string), ssh: fleetInfraRepository["ssh"].(string), }, applicationRepository: repoConfig{ http: applicationRepository["http"].(string), ssh: applicationRepository["ssh"].(string), }, fluxAzureSp: spConfig{ tenantId: fluxAzureSp["tenant_id"].(string), clientId: fluxAzureSp["client_id"].(string), clientSecret: fluxAzureSp["client_secret"].(string), }, sopsId: sharedSopsId, acr: acrConfig{ url: acr["url"].(string), username: acr["username"].(string), password: acr["password"].(string), }, eventHubSas: eventHubSas, } err = installFlux(ctx, kubeClient, kubeconfigPath, cfg.fleetInfraRepository.http, azdoPat, cfg.fluxAzureSp) if err != nil { log.Fatalf("error installing Flux: %v", err) } log.Println("Running Azure e2e tests") exitVal := m.Run() log.Println("Tearing down Azure test infrastructure") err = tf.Destroy(ctx) if err != nil { log.Fatalf("error running Show: %v", err) } os.Exit(exitVal) } func TestFluxInstallation(t *testing.T) { ctx := context.TODO() require.Eventually(t, func() bool { err := verifyGitAndKustomization(ctx, cfg.kubeClient, "flux-system", "flux-system") if err != nil { return false } return true }, 60*time.Second, 5*time.Second) } func TestAzureDevOpsCloning(t *testing.T) { ctx := context.TODO() branchName := "feature/branch" tagName := "v1" tests := []struct { name string refType string cloneType string }{ { name: "https-feature-branch", refType: "branch", cloneType: "http", }, { name: "https-v1", refType: "tag", cloneType: "http", }, { name: "ssh-feature-branch", refType: "branch", cloneType: "ssh", }, { name: "ssh-v1", refType: "tag", cloneType: "ssh", }, } t.Log("Creating application sources") repo, repoDir, err := getRepository(cfg.applicationRepository.http, branchName, true, cfg.azdoPat) require.NoError(t, err) for _, tt := range tests { manifest := fmt.Sprintf(` apiVersion: v1 kind: ConfigMap metadata: name: foobar namespace: %s `, tt.name) err = runCommand(ctx, repoDir, fmt.Sprintf("mkdir -p ./cloning-test/%s", tt.name)) require.NoError(t, err) err = runCommand(ctx, repoDir, fmt.Sprintf("echo '%s' > ./cloning-test/%s/configmap.yaml", manifest, tt.name)) require.NoError(t, err) } err = commitAndPushAll(repo, branchName, cfg.azdoPat) require.NoError(t, err) err = createTagAndPush(repo, branchName, tagName, cfg.azdoPat) require.NoError(t, err) t.Log("Verifying application-gitops namespaces") for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ref := &sourcev1.GitRepositoryRef{ Branch: branchName, } if tt.refType == "tag" { ref = &sourcev1.GitRepositoryRef{ Tag: tagName, } } url := cfg.applicationRepository.http secretData := map[string]string{ "username": "git", "password": cfg.azdoPat, } if tt.cloneType == "ssh" { url = cfg.applicationRepository.ssh secretData = map[string]string{ "identity": cfg.idRsa, "identity.pub": cfg.idRsaPub, "known_hosts": cfg.knownHosts, } } namespace := corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: tt.name, }, } _, err := controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &namespace, func() error { return nil }) gitSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "git-credentials", Namespace: namespace.Name, }, } _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, gitSecret, func() error { gitSecret.StringData = secretData return nil }) source := &sourcev1.GitRepository{ObjectMeta: metav1.ObjectMeta{Name: tt.name, Namespace: namespace.Name}} _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, source, func() error { source.Spec = sourcev1.GitRepositorySpec{ GitImplementation: sourcev1.LibGit2Implementation, Reference: ref, SecretRef: &meta.LocalObjectReference{ Name: gitSecret.Name, }, URL: url, } return nil }) require.NoError(t, err) kustomization := &kustomizev1.Kustomization{ObjectMeta: metav1.ObjectMeta{Name: tt.name, Namespace: namespace.Name}} _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, kustomization, func() error { kustomization.Spec = kustomizev1.KustomizationSpec{ Path: fmt.Sprintf("./cloning-test/%s", tt.name), SourceRef: kustomizev1.CrossNamespaceSourceReference{ Kind: sourcev1.GitRepositoryKind, Name: tt.name, Namespace: namespace.Name, }, Interval: metav1.Duration{Duration: 1 * time.Minute}, Prune: true, } return nil }) require.NoError(t, err) // Wait for configmap to be deployed require.Eventually(t, func() bool { err := verifyGitAndKustomization(ctx, cfg.kubeClient, namespace.Name, tt.name) if err != nil { return false } nn := types.NamespacedName{Name: "foobar", Namespace: namespace.Name} cm := &corev1.ConfigMap{} err = cfg.kubeClient.Get(ctx, nn, cm) if err != nil { return false } return true }, 120*time.Second, 5*time.Second) }) } } func TestImageRepositoryACR(t *testing.T) { ctx := context.TODO() name := "image-repository-acr" repoUrl := cfg.applicationRepository.http oldVersion := "1.0.0" newVersion := "1.0.1" manifest := fmt.Sprintf(` apiVersion: apps/v1 kind: Deployment metadata: name: podinfo namespace: %s spec: selector: matchLabels: app: podinfo template: metadata: labels: app: podinfo spec: containers: - name: podinfod image: %s/container/podinfo:%s # {"$imagepolicy": "%s:podinfo"} readinessProbe: exec: command: - podcli - check - http - localhost:9898/readyz initialDelaySeconds: 5 timeoutSeconds: 5`, name, cfg.acr.url, oldVersion, name) repo, repoDir, err := getRepository(repoUrl, name, true, cfg.azdoPat) require.NoError(t, err) err = addFile(repoDir, "podinfo.yaml", manifest) require.NoError(t, err) err = commitAndPushAll(repo, name, cfg.azdoPat) require.NoError(t, err) err = setupNamespace(ctx, cfg.kubeClient, repoUrl, cfg.azdoPat, name) require.NoError(t, err) acrSecret := corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "acr-docker", Namespace: name}} _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &acrSecret, func() error { acrSecret.Type = corev1.SecretTypeDockerConfigJson acrSecret.StringData = map[string]string{ ".dockerconfigjson": fmt.Sprintf(` { "auths": { "%s": { "auth": "%s" } } } `, cfg.acr.url, b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", cfg.acr.username, cfg.acr.password)))), } return nil }) require.NoError(t, err) imageRepository := reflectorv1beta1.ImageRepository{ ObjectMeta: metav1.ObjectMeta{ Name: "podinfo", Namespace: name, }, } _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &imageRepository, func() error { imageRepository.Spec = reflectorv1beta1.ImageRepositorySpec{ Image: fmt.Sprintf("%s/container/podinfo", cfg.acr.url), Interval: metav1.Duration{ Duration: 1 * time.Minute, }, SecretRef: &meta.LocalObjectReference{ Name: acrSecret.Name, }, } return nil }) require.NoError(t, err) imagePolicy := reflectorv1beta1.ImagePolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "podinfo", Namespace: name, }, } _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &imagePolicy, func() error { imagePolicy.Spec = reflectorv1beta1.ImagePolicySpec{ ImageRepositoryRef: meta.NamespacedObjectReference{ Name: imageRepository.Name, }, Policy: reflectorv1beta1.ImagePolicyChoice{ SemVer: &reflectorv1beta1.SemVerPolicy{ Range: "1.0.x", }, }, } return nil }) require.NoError(t, err) imageAutomation := automationv1beta1.ImageUpdateAutomation{ ObjectMeta: metav1.ObjectMeta{ Name: "podinfo", Namespace: name, }, } _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &imageAutomation, func() error { imageAutomation.Spec = automationv1beta1.ImageUpdateAutomationSpec{ Interval: metav1.Duration{ Duration: 1 * time.Minute, }, SourceRef: automationv1beta1.SourceReference{ Kind: "GitRepository", Name: name, }, GitSpec: &automationv1beta1.GitSpec{ Checkout: &automationv1beta1.GitCheckoutSpec{ Reference: sourcev1.GitRepositoryRef{ Branch: name, }, }, Commit: automationv1beta1.CommitSpec{ Author: automationv1beta1.CommitUser{ Email: "imageautomation@example.com", Name: "imageautomation", }, }, }, } return nil }) require.NoError(t, err) // Wait for image repository to be ready require.Eventually(t, func() bool { _, repoDir, err := getRepository(repoUrl, name, false, cfg.azdoPat) if err != nil { return false } b, err := os.ReadFile(filepath.Join(repoDir, "podinfo.yaml")) if err != nil { return false } if bytes.Contains(b, []byte(newVersion)) == false { return false } return true }, 120*time.Second, 5*time.Second) } func TestKeyVaultSops(t *testing.T) { ctx := context.TODO() name := "key-vault-sops" repoUrl := cfg.applicationRepository.http secretYaml := `apiVersion: v1 kind: Secret metadata: name: "test" namespace: "key-vault-sops" stringData: foo: "bar"` repo, tmpDir, err := getRepository(repoUrl, name, true, cfg.azdoPat) err = runCommand(ctx, tmpDir, "mkdir -p ./key-vault-sops") require.NoError(t, err) err = runCommand(ctx, tmpDir, fmt.Sprintf("echo \"%s\" > ./key-vault-sops/secret.enc.yaml", secretYaml)) require.NoError(t, err) err = runCommand(ctx, tmpDir, fmt.Sprintf("sops --encrypt --encrypted-regex '^(data|stringData)$' --azure-kv %s --in-place ./key-vault-sops/secret.enc.yaml", cfg.sopsId)) require.NoError(t, err) err = commitAndPushAll(repo, name, cfg.azdoPat) require.NoError(t, err) err = setupNamespace(ctx, cfg.kubeClient, repoUrl, cfg.azdoPat, name) require.NoError(t, err) source := &sourcev1.GitRepository{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: name}} require.Eventually(t, func() bool { _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, source, func() error { source.Spec = sourcev1.GitRepositorySpec{ GitImplementation: sourcev1.LibGit2Implementation, Reference: &sourcev1.GitRepositoryRef{ Branch: name, }, SecretRef: &meta.LocalObjectReference{ Name: "https-credentials", }, URL: repoUrl, } return nil }) if err != nil { return false } return true }, 10*time.Second, 1*time.Second) kustomization := &kustomizev1.Kustomization{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: name}} require.Eventually(t, func() bool { _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, kustomization, func() error { kustomization.Spec = kustomizev1.KustomizationSpec{ Path: "./key-vault-sops", SourceRef: kustomizev1.CrossNamespaceSourceReference{ Kind: sourcev1.GitRepositoryKind, Name: source.Name, Namespace: source.Namespace, }, Interval: metav1.Duration{Duration: 1 * time.Minute}, Prune: true, Decryption: &kustomizev1.Decryption{ Provider: "sops", }, } return nil }) if err != nil { return false } return true }, 10*time.Second, 1*time.Second) require.Eventually(t, func() bool { nn := types.NamespacedName{Name: "test", Namespace: name} secret := &corev1.Secret{} err = cfg.kubeClient.Get(ctx, nn, secret) if err != nil { return false } return true }, 120*time.Second, 5*time.Second) } func TestAzureDevOpsCommitStatus(t *testing.T) { ctx := context.TODO() name := "commit-status" repoUrl := cfg.applicationRepository.http manifest := fmt.Sprintf(` apiVersion: v1 kind: ConfigMap metadata: name: foobar namespace: %s `, name) repo, repoDir, err := getRepository(repoUrl, name, true, cfg.azdoPat) require.NoError(t, err) err = addFile(repoDir, "configmap.yaml", manifest) require.NoError(t, err) err = commitAndPushAll(repo, name, cfg.azdoPat) require.NoError(t, err) err = setupNamespace(ctx, cfg.kubeClient, repoUrl, cfg.azdoPat, name) require.NoError(t, err) kustomization := &kustomizev1.Kustomization{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: name}} require.Eventually(t, func() bool { _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, kustomization, func() error { kustomization.Spec.HealthChecks = []meta.NamespacedObjectKindReference{ { APIVersion: "v1", Kind: "ConfigMap", Name: "foobar", Namespace: name, }, } return nil }) if err != nil { return false } return true }, 10*time.Second, 1*time.Second) require.Eventually(t, func() bool { err := verifyGitAndKustomization(ctx, cfg.kubeClient, name, name) if err != nil { return false } return true }, 10*time.Second, 1*time.Second) secret := corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "azuredevops-token", Namespace: name, }, } _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &secret, func() error { secret.StringData = map[string]string{ "token": cfg.azdoPat, } return nil }) provider := notiv1beta1.Provider{ ObjectMeta: metav1.ObjectMeta{ Name: "azuredevops", Namespace: name, }, } _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &provider, func() error { provider.Spec = notiv1beta1.ProviderSpec{ Type: "azuredevops", Address: repoUrl, SecretRef: &meta.LocalObjectReference{ Name: "azuredevops-token", }, } return nil }) require.NoError(t, err) alert := notiv1beta1.Alert{ ObjectMeta: metav1.ObjectMeta{ Name: "azuredevops", Namespace: name, }, } _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &alert, func() error { alert.Spec = notiv1beta1.AlertSpec{ ProviderRef: meta.LocalObjectReference{ Name: provider.Name, }, EventSources: []notiv1beta1.CrossNamespaceObjectReference{ { Kind: "Kustomization", Name: name, Namespace: name, }, }, } return nil }) require.NoError(t, err) u, err := giturls.Parse(repoUrl) require.NoError(t, err) id := strings.TrimLeft(u.Path, "/") id = strings.TrimSuffix(id, ".git") comp := strings.Split(id, "/") orgUrl := fmt.Sprintf("%s://%s/%v", u.Scheme, u.Host, comp[0]) project := comp[1] repoId := comp[3] branch, err := repo.LookupBranch(name, git2go.BranchLocal) require.NoError(t, err) commit, err := repo.LookupCommit(branch.Target()) rev := commit.Id().String() connection := azuredevops.NewPatConnection(orgUrl, cfg.azdoPat) client, err := git.NewClient(ctx, connection) require.NoError(t, err) getArgs := git.GetStatusesArgs{ Project: &project, RepositoryId: &repoId, CommitId: &rev, } require.Eventually(t, 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) } func TestEventHubNotification(t *testing.T) { ctx := context.TODO() name := "event-hub" // Start listening to eventhub with latest offset hub, err := eventhub.NewHubFromConnectionString(cfg.eventHubSas) require.NoError(t, err) c := make(chan string, 10) handler := func(ctx context.Context, event *eventhub.Event) error { c <- string(event.Data) return nil } runtimeInfo, err := hub.GetRuntimeInformation(ctx) require.NoError(t, err) require.Equal(t, 1, len(runtimeInfo.PartitionIDs)) listenerHandler, err := hub.Receive(ctx, runtimeInfo.PartitionIDs[0], handler, eventhub.ReceiveWithLatestOffset()) require.NoError(t, err) // Setup Flux resources repoUrl := cfg.applicationRepository.http manifest := fmt.Sprintf(` apiVersion: v1 kind: ConfigMap metadata: name: foobar namespace: %s `, name) repo, repoDir, err := getRepository(repoUrl, name, true, cfg.azdoPat) require.NoError(t, err) err = addFile(repoDir, "configmap.yaml", manifest) require.NoError(t, err) err = commitAndPushAll(repo, name, cfg.azdoPat) require.NoError(t, err) err = setupNamespace(ctx, cfg.kubeClient, repoUrl, cfg.azdoPat, name) require.NoError(t, err) secret := corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: name, }, } _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &secret, func() error { secret.StringData = map[string]string{ "address": cfg.eventHubSas, } return nil }) provider := notiv1beta1.Provider{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: name, }, } _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &provider, func() error { provider.Spec = notiv1beta1.ProviderSpec{ Type: "azureeventhub", Address: repoUrl, SecretRef: &meta.LocalObjectReference{ Name: name, }, } return nil }) require.NoError(t, err) alert := notiv1beta1.Alert{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: name, }, } _, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &alert, func() error { alert.Spec = notiv1beta1.AlertSpec{ ProviderRef: meta.LocalObjectReference{ Name: provider.Name, }, EventSources: []notiv1beta1.CrossNamespaceObjectReference{ { Kind: "Kustomization", Name: name, Namespace: name, }, }, } return nil }) require.NoError(t, err) kustomization := &kustomizev1.Kustomization{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: name}} require.Eventually(t, func() bool { _, err := controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, kustomization, func() error { kustomization.Spec.HealthChecks = []meta.NamespacedObjectKindReference{ { APIVersion: "v1", Kind: "ConfigMap", Name: "foobar", Namespace: name, }, } return nil }) if err != nil { return false } return true }, 10*time.Second, 1*time.Second) require.Eventually(t, func() bool { err := verifyGitAndKustomization(ctx, cfg.kubeClient, name, name) if err != nil { return false } return true }, 60*time.Second, 5*time.Second) // Wait to read even from event hub require.Eventually(t, func() bool { select { case eventJson := <-c: 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 && strings.Contains(event.Message, "Health check passed") { return true } t.Logf("event received from '%s/%s': %s", event.InvolvedObject.Kind, event.InvolvedObject.Name, event.Message) return false default: return false } }, 60*time.Second, 1*time.Second) err = listenerHandler.Close(ctx) require.NoError(t, err) err = hub.Close(ctx) require.NoError(t, err) } // TODO: Enable when source-controller supports Helm charts from OCI sources. /*func TestACRHelmRelease(t *testing.T) { ctx := context.TODO() // Create namespace for test namespace := corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "acr-helm-release", }, } err := kubeClient.Create(ctx, &namespace) require.NoError(t, err) defer func() { kubeClient.Delete(ctx, &namespace) require.NoError(t, err) }() // Copy ACR credentials to new namespace acrNn := types.NamespacedName{ Name: "acr-helm", Namespace: "flux-system", } acrSecret := corev1.Secret{} err = kubeClient.Get(ctx, acrNn, &acrSecret) require.NoError(t, err) acrSecret.ObjectMeta = metav1.ObjectMeta{ Name: acrSecret.Name, Namespace: namespace.Name, } err = kubeClient.Create(ctx, &acrSecret) require.NoError(t, err) // Create HelmRepository and wait for it to sync helmRepository := sourcev1.HelmRepository{ ObjectMeta: metav1.ObjectMeta{ Name: "acr", Namespace: namespace.Name, }, Spec: sourcev1.HelmRepositorySpec{ URL: "https://acrappsoarfish.azurecr.io/helm/podinfo", Interval: metav1.Duration{ Duration: 5 * time.Minute, }, SecretRef: &meta.LocalObjectReference{ Name: acrSecret.Name, }, PassCredentials: true, }, } err = kubeClient.Create(ctx, &helmRepository) require.NoError(t, err) }*/