diff --git a/e2e/nomostest/config_sync.go b/e2e/nomostest/config_sync.go index a0d31221e..a1710e494 100644 --- a/e2e/nomostest/config_sync.go +++ b/e2e/nomostest/config_sync.go @@ -393,11 +393,11 @@ func ValidateMultiRepoDeployments(nt *NT) error { var rs *v1beta1.RootSync switch tSource := source.(type) { case *syncsource.GitSyncSource: - rs = RootSyncObjectV1Beta1FromRootRepo(nt, id.Name) + rs = nt.RootSyncObjectGit(id.Name, tSource.Repository, tSource.Directory, tSource.SourceFormat) case *syncsource.HelmSyncSource: rs = nt.RootSyncObjectHelm(id.Name, tSource.ChartID) - // case *syncsource.OCISyncSource: - // TODO: setup OCI RootSyncs + case *syncsource.OCISyncSource: + rs = nt.RootSyncObjectOCI(id.Name, tSource.ImageID, tSource.Directory, tSource.Digest) default: nt.T.Fatalf("Invalid RootSync source %T: %s", source, id.Name) } @@ -710,8 +710,8 @@ func setupDelegatedControl(nt *NT) { } } -// RootSyncObjectV1Alpha1 returns the default RootSync object. -func RootSyncObjectV1Alpha1(name, repoURL string, sourceFormat configsync.SourceFormat) *v1alpha1.RootSync { +// RootSyncObjectV1Alpha1Git returns the default RootSync object. +func RootSyncObjectV1Alpha1Git(name, repoURL string, sourceFormat configsync.SourceFormat) *v1alpha1.RootSync { rs := k8sobjects.RootSyncObjectV1Alpha1(name) rs.Spec.SourceFormat = sourceFormat rs.Spec.SourceType = configsync.GitSource @@ -757,7 +757,7 @@ func RootSyncObjectV1Alpha1FromRootRepo(nt *NT, name string) *v1alpha1.RootSync } repoURL := nt.GitProvider.SyncURL(gitSource.Repository.RemoteRepoName) sourceFormat := gitSource.Repository.Format - rs := RootSyncObjectV1Alpha1(name, repoURL, sourceFormat) + rs := RootSyncObjectV1Alpha1Git(name, repoURL, sourceFormat) if nt.DefaultReconcileTimeout != nil { rs.Spec.SafeOverride().ReconcileTimeout = toMetav1Duration(*nt.DefaultReconcileTimeout) } else if rs.Spec.Override != nil { @@ -766,8 +766,8 @@ func RootSyncObjectV1Alpha1FromRootRepo(nt *NT, name string) *v1alpha1.RootSync return rs } -// RootSyncObjectV1Beta1 returns the default RootSync object with version v1beta1. -func RootSyncObjectV1Beta1(name, repoURL string, sourceFormat configsync.SourceFormat) *v1beta1.RootSync { +// RootSyncObjectV1Beta1Git returns the default RootSync object with version v1beta1. +func RootSyncObjectV1Beta1Git(name, repoURL string, sourceFormat configsync.SourceFormat) *v1beta1.RootSync { rs := k8sobjects.RootSyncObjectV1Beta1(name) rs.Spec.SourceFormat = sourceFormat rs.Spec.SourceType = configsync.GitSource @@ -800,8 +800,8 @@ func RootSyncObjectV1Beta1(name, repoURL string, sourceFormat configsync.SourceF // RootSyncObjectV1Beta1FromRootRepo returns a v1beta1 RootSync object which // uses a RootSync repo from nt.SyncSources. +// Use nt.RootSyncObjectGit is you want to change the source expectation. func RootSyncObjectV1Beta1FromRootRepo(nt *NT, name string) *v1beta1.RootSync { - // TODO: Allow supplying the Repository, instead of requiring it to already exist id := core.RootSyncID(name) source, found := nt.SyncSources[id] if !found { @@ -811,21 +811,13 @@ func RootSyncObjectV1Beta1FromRootRepo(nt *NT, name string) *v1beta1.RootSync { if !ok { nt.T.Fatalf("expected SyncSources for %s to have *GitSyncSource, but got %T", id, source) } - repoURL := nt.GitProvider.SyncURL(gitSource.Repository.RemoteRepoName) - sourceFormat := gitSource.Repository.Format - rs := RootSyncObjectV1Beta1(name, repoURL, sourceFormat) - if nt.DefaultReconcileTimeout != nil { - rs.Spec.SafeOverride().ReconcileTimeout = toMetav1Duration(*nt.DefaultReconcileTimeout) - } else if rs.Spec.Override != nil { - rs.Spec.Override.ReconcileTimeout = nil - } - return rs + return nt.RootSyncObjectGit(name, gitSource.Repository, gitSource.Directory, gitSource.SourceFormat) } // RootSyncObjectV1Beta1FromOtherRootRepo returns a v1beta1 RootSync object which // uses a repo from a specific nt.RootRepo. +// Use nt.RootSyncObjectGit is you want to change the source expectation. func RootSyncObjectV1Beta1FromOtherRootRepo(nt *NT, syncName, repoName string) *v1beta1.RootSync { - // TODO: Allow supplying the Repository, instead of requiring it to already exist repoID := core.RootSyncID(repoName) source, found := nt.SyncSources[repoID] if !found { @@ -835,15 +827,7 @@ func RootSyncObjectV1Beta1FromOtherRootRepo(nt *NT, syncName, repoName string) * if !ok { nt.T.Fatalf("expected SyncSources for %s to have *GitSyncSource, but got %T", repoID, source) } - repoURL := nt.GitProvider.SyncURL(gitSource.Repository.RemoteRepoName) - sourceFormat := gitSource.Repository.Format - rs := RootSyncObjectV1Beta1(syncName, repoURL, sourceFormat) - if nt.DefaultReconcileTimeout != nil { - rs.Spec.SafeOverride().ReconcileTimeout = toMetav1Duration(*nt.DefaultReconcileTimeout) - } else if rs.Spec.Override != nil { - rs.Spec.Override.ReconcileTimeout = nil - } - return rs + return nt.RootSyncObjectGit(syncName, gitSource.Repository, gitSource.Directory, gitSource.SourceFormat) } // StructuredNSPath returns structured path with namespace and resourcename in repo. @@ -851,9 +835,9 @@ func StructuredNSPath(namespace, resourceName string) string { return fmt.Sprintf("acme/namespaces/%s/%s.yaml", namespace, resourceName) } -// RepoSyncObjectV1Alpha1 returns the default RepoSync object in the given namespace. +// RepoSyncObjectV1Alpha1Git returns the default RepoSync object in the given namespace. // SourceFormat for RepoSync must be Unstructured (default), so it's left unspecified. -func RepoSyncObjectV1Alpha1(nn types.NamespacedName, repoURL string) *v1alpha1.RepoSync { +func RepoSyncObjectV1Alpha1Git(nn types.NamespacedName, repoURL string) *v1alpha1.RepoSync { rs := k8sobjects.RepoSyncObjectV1Alpha1(nn.Namespace, nn.Name) rs.Spec.SourceType = configsync.GitSource rs.Spec.Git = &v1alpha1.Git{ @@ -898,7 +882,7 @@ func RepoSyncObjectV1Alpha1FromNonRootRepo(nt *NT, nn types.NamespacedName) *v1a } repoURL := nt.GitProvider.SyncURL(gitSource.Repository.RemoteRepoName) // RepoSync is always Unstructured. So ignore repo.Format. - rs := RepoSyncObjectV1Alpha1(nn, repoURL) + rs := RepoSyncObjectV1Alpha1Git(nn, repoURL) if nt.DefaultReconcileTimeout != nil { rs.Spec.SafeOverride().ReconcileTimeout = toMetav1Duration(*nt.DefaultReconcileTimeout) } else if rs.Spec.Override != nil { @@ -914,9 +898,9 @@ func RepoSyncObjectV1Alpha1FromNonRootRepo(nt *NT, nn types.NamespacedName) *v1a return rs } -// RepoSyncObjectV1Beta1 returns the default RepoSync object +// RepoSyncObjectV1Beta1Git returns the default RepoSync object // with version v1beta1 in the given namespace. -func RepoSyncObjectV1Beta1(nn types.NamespacedName, repoURL string, sourceFormat configsync.SourceFormat) *v1beta1.RepoSync { +func RepoSyncObjectV1Beta1Git(nn types.NamespacedName, repoURL string, sourceFormat configsync.SourceFormat) *v1beta1.RepoSync { rs := k8sobjects.RepoSyncObjectV1Beta1(nn.Namespace, nn.Name) rs.Spec.SourceFormat = sourceFormat rs.Spec.SourceType = configsync.GitSource @@ -947,17 +931,66 @@ func RepoSyncObjectV1Beta1(nn types.NamespacedName, repoURL string, sourceFormat return rs } -// RootSyncObjectOCI returns a RootSync object that syncs the provided OCIImage. -func (nt *NT) RootSyncObjectOCI(name string, image *registryproviders.OCIImage) *v1beta1.RootSync { - imageURL, err := image.RemoteAddressWithTag() - if err != nil { - nt.T.Fatalf("OCIImage.RemoteAddressWithTag: %v", err) +// RootSyncObjectGit creates a new RootSync object with a Git source, using the +// default test config plus the specified inputs. +// Creates, updates, or replaces the SyncSource expectation for this RootSync. +func (nt *NT) RootSyncObjectGit(name string, repo *gitproviders.Repository, syncPath string, sourceFormat configsync.SourceFormat) *v1beta1.RootSync { + id := core.RootSyncID(name) + SetExpectedGitSource(nt, id, repo, syncPath, sourceFormat) + repoURL := nt.GitProvider.SyncURL(repo.RemoteRepoName) + rs := RootSyncObjectV1Beta1Git(name, repoURL, sourceFormat) + if nt.DefaultReconcileTimeout != nil { + rs.Spec.SafeOverride().ReconcileTimeout = toMetav1Duration(*nt.DefaultReconcileTimeout) + } else if rs.Spec.Override != nil { + rs.Spec.Override.ReconcileTimeout = nil } + // Enable automatic deletion of managed objects by default. + // This helps ensure that test artifacts are cleaned up. + EnableDeletionPropagation(rs) + return rs +} - rs := RootSyncObjectV1Beta1FromRootRepo(nt, name) +// RepoSyncObjectGit creates a new RepoSync object with a Git source, using the +// default test config plus the specified inputs. +// Creates, updates, or replaces the SyncSource expectation for this RepoSync. +func (nt *NT) RepoSyncObjectGit(nn types.NamespacedName, repo *gitproviders.Repository, syncPath string, sourceFormat configsync.SourceFormat) *v1beta1.RepoSync { + id := core.RepoSyncID(nn.Name, nn.Namespace) + SetExpectedGitSource(nt, id, repo, syncPath, sourceFormat) + repoURL := nt.GitProvider.SyncURL(repo.RemoteRepoName) + // RepoSync is always Unstructured. So ignore repo.Format. + rs := RepoSyncObjectV1Beta1Git(nn, repoURL, sourceFormat) + if nt.DefaultReconcileTimeout != nil { + rs.Spec.SafeOverride().ReconcileTimeout = toMetav1Duration(*nt.DefaultReconcileTimeout) + } else if rs.Spec.Override != nil { + rs.Spec.Override.ReconcileTimeout = nil + } + // Add dependencies to ensure managed objects can be deleted. + if err := SetRepoSyncDependencies(nt, rs); err != nil { + nt.T.Fatal(err) + } + // Enable automatic deletion of managed objects by default. + // This helps ensure that test artifacts are cleaned up. + EnableDeletionPropagation(rs) + return rs +} + +// RootSyncObjectOCI returns a RootSync object that syncs the provided OCIImage. +// ImageID is used for pulling. ImageDigest is used for validating the result. +// Creates, updates, or replaces the SyncSource expectation for this RootSync. +func (nt *NT) RootSyncObjectOCI(name string, imageID registryproviders.OCIImageID, syncPath, expectedImageDigest string) *v1beta1.RootSync { + id := core.RootSyncID(name) + // Register the OCI image for syncing + expectedSyncPath := syncPath + if syncPath == "" { + expectedSyncPath = controllers.DefaultSyncDir + } + SetExpectedOCISource(nt, id, imageID, expectedImageDigest, expectedSyncPath) + rs := k8sobjects.RootSyncObjectV1Beta1(name) + rs.Spec.SourceFormat = configsync.SourceFormatUnstructured // TODO: is SourceFormat necessary with OCI? rs.Spec.SourceType = configsync.OciSource rs.Spec.Oci = &v1beta1.Oci{ - Image: imageURL, + Image: imageID.Address(), + Dir: syncPath, Auth: configsync.AuthNone, Period: metav1.Duration{Duration: shortSyncPollingPeriod}, } @@ -974,20 +1007,34 @@ func (nt *NT) RootSyncObjectOCI(name string, image *registryproviders.OCIImage) default: nt.T.Fatalf("Unrecognized OCIProvider: %s", *e2e.OCIProvider) } + if nt.DefaultReconcileTimeout != nil { + rs.Spec.SafeOverride().ReconcileTimeout = toMetav1Duration(*nt.DefaultReconcileTimeout) + } else if rs.Spec.Override != nil { + rs.Spec.Override.ReconcileTimeout = nil + } + // Enable automatic deletion of managed objects by default. + // This helps ensure that test artifacts are cleaned up. + EnableDeletionPropagation(rs) return rs } // RepoSyncObjectOCI returns a RepoSync object that syncs the provided OCIImage. -func (nt *NT) RepoSyncObjectOCI(nn types.NamespacedName, image *registryproviders.OCIImage) *v1beta1.RepoSync { - imageURL, err := image.RemoteAddressWithTag() - if err != nil { - nt.T.Fatalf("OCIImage.RemoteAddressWithTag: %v", err) +// ImageID is used for pulling. ImageDigest is used for validating the result. +// Creates, updates, or replaces the SyncSource expectation for this RepoSync. +func (nt *NT) RepoSyncObjectOCI(nn types.NamespacedName, imageID registryproviders.OCIImageID, syncPath, expectedImageDigest string) *v1beta1.RepoSync { + id := core.RepoSyncID(nn.Name, nn.Namespace) + // Register the OCI image for syncing + expectedSyncPath := syncPath + if syncPath == "" { + expectedSyncPath = controllers.DefaultSyncDir } - - rs := RepoSyncObjectV1Beta1FromNonRootRepo(nt, nn) + SetExpectedOCISource(nt, id, imageID, expectedImageDigest, expectedSyncPath) + rs := k8sobjects.RepoSyncObjectV1Beta1(nn.Namespace, nn.Name) + rs.Spec.SourceFormat = configsync.SourceFormatUnstructured // TODO: is SourceFormat necessary with OCI? rs.Spec.SourceType = configsync.OciSource rs.Spec.Oci = &v1beta1.Oci{ - Image: imageURL, + Image: imageID.Address(), + Dir: syncPath, Auth: configsync.AuthNone, Period: metav1.Duration{Duration: shortSyncPollingPeriod}, } @@ -1004,36 +1051,33 @@ func (nt *NT) RepoSyncObjectOCI(nn types.NamespacedName, image *registryprovider default: nt.T.Fatalf("Unrecognized OCIProvider: %s", *e2e.OCIProvider) } + if nt.DefaultReconcileTimeout != nil { + rs.Spec.SafeOverride().ReconcileTimeout = toMetav1Duration(*nt.DefaultReconcileTimeout) + } else if rs.Spec.Override != nil { + rs.Spec.Override.ReconcileTimeout = nil + } + // Add dependencies to ensure managed objects can be deleted. + if err := SetRepoSyncDependencies(nt, rs); err != nil { + nt.T.Fatal(err) + } + // Enable automatic deletion of managed objects by default. + // This helps ensure that test artifacts are cleaned up. + EnableDeletionPropagation(rs) return rs } -// RootSyncObjectHelm returns a RootSync object that syncs the provided HelmPackage +// RootSyncObjectHelm returns a RootSync object that syncs the provided HelmPackage. +// Creates, updates, or replaces the SyncSource expectation for this RootSync. func (nt *NT) RootSyncObjectHelm(name string, chartID registryproviders.HelmChartID) *v1beta1.RootSync { id := core.RootSyncID(name) - source, found := nt.SyncSources[id] - if found { - if helmSource, ok := source.(*syncsource.HelmSyncSource); ok { - if helmSource.ChartID != chartID { - nt.T.Logf("Updating existing %s %T: %s", id.Kind, source, id.ObjectKey) - } - } else { - nt.T.Logf("Replacing existing %s %T with HelmSyncSource: %s", id.Kind, source, id.ObjectKey) - } - } // Register the Helm chart for syncing - nt.SyncSources[id] = &syncsource.HelmSyncSource{ - ChartID: chartID, - } - chartRepoURL, err := nt.HelmProvider.RepositoryRemoteURL() - if err != nil { - nt.T.Fatalf("HelmProvider.RepositoryRemoteURL: %v", err) - } + SetExpectedHelmSource(nt, id, chartID) rs := k8sobjects.RootSyncObjectV1Beta1(name) rs.Spec.SourceFormat = configsync.SourceFormatUnstructured rs.Spec.SourceType = configsync.HelmSource rs.Spec.Helm = &v1beta1.HelmRootSync{ HelmBase: v1beta1.HelmBase{ - Repo: chartRepoURL, + Repo: nt.HelmProvider.RepositoryRemoteURL(), Chart: chartID.Name, Version: chartID.Version, Auth: configsync.AuthNone, @@ -1064,33 +1108,18 @@ func (nt *NT) RootSyncObjectHelm(name string, chartID registryproviders.HelmChar return rs } -// RepoSyncObjectHelm returns a RepoSync object that syncs the provided HelmPackage +// RepoSyncObjectHelm returns a RepoSync object that syncs the provided HelmPackage. +// Creates, updates, or replaces the SyncSource expectation for this RepoSync. func (nt *NT) RepoSyncObjectHelm(nn types.NamespacedName, chartID registryproviders.HelmChartID) *v1beta1.RepoSync { id := core.RepoSyncID(nn.Name, nn.Namespace) - source, found := nt.SyncSources[id] - if found { - if helmSource, ok := source.(*syncsource.HelmSyncSource); ok { - if helmSource.ChartID != chartID { - nt.T.Logf("Updating existing %s %T: %s", id.Kind, source, id.ObjectKey) - } - } else { - nt.T.Logf("Replacing existing %s %T with HelmSyncSource: %s", id.Kind, source, id.ObjectKey) - } - } // Register the Helm chart for syncing - nt.SyncSources[id] = &syncsource.HelmSyncSource{ - ChartID: chartID, - } - chartRepoURL, err := nt.HelmProvider.RepositoryRemoteURL() - if err != nil { - nt.T.Fatalf("HelmProvider.RepositoryRemoteURL: %v", err) - } + SetExpectedHelmSource(nt, id, chartID) rs := k8sobjects.RepoSyncObjectV1Beta1(nn.Namespace, nn.Name) rs.Spec.SourceFormat = configsync.SourceFormatUnstructured rs.Spec.SourceType = configsync.HelmSource rs.Spec.Helm = &v1beta1.HelmRepoSync{ HelmBase: v1beta1.HelmBase{ - Repo: chartRepoURL, + Repo: nt.HelmProvider.RepositoryRemoteURL(), Chart: chartID.Name, Version: chartID.Version, Auth: configsync.AuthNone, @@ -1127,58 +1156,34 @@ func (nt *NT) RepoSyncObjectHelm(nn types.NamespacedName, chartID registryprovid // RepoSyncObjectV1Beta1FromNonRootRepo returns a v1beta1 RepoSync object which // uses a RepoSync repo from nt.SyncSources. +// Use nt.RepoSyncObjectGit is you want to change the source expectation. func RepoSyncObjectV1Beta1FromNonRootRepo(nt *NT, nn types.NamespacedName) *v1beta1.RepoSync { - // TODO: Allow supplying the Repository, instead of requiring it to already exist id := core.RepoSyncID(nn.Name, nn.Namespace) source, found := nt.SyncSources[id] if !found { - nt.T.Fatal("nonexistent %s source: %s", id.Kind, id.ObjectKey) + nt.T.Fatalf("nonexistent %s source: %s", id.Kind, id.ObjectKey) } gitSource, ok := source.(*syncsource.GitSyncSource) if !ok { - nt.T.Fatal("expected SyncSources for %s to have *GitSyncSource, but got %T", id, source) - } - repoURL := nt.GitProvider.SyncURL(gitSource.Repository.RemoteRepoName) - sourceFormat := gitSource.Repository.Format - rs := RepoSyncObjectV1Beta1(nn, repoURL, sourceFormat) - if nt.DefaultReconcileTimeout != nil { - rs.Spec.SafeOverride().ReconcileTimeout = toMetav1Duration(*nt.DefaultReconcileTimeout) - } else if rs.Spec.Override != nil { - rs.Spec.Override.ReconcileTimeout = nil - } - // Add dependencies to ensure managed objects can be deleted. - if err := SetRepoSyncDependencies(nt, rs); err != nil { - nt.T.Fatal(err) + nt.T.Fatalf("expected SyncSources for %s to have *GitSyncSource, but got %T", id, source) } - return rs + return nt.RepoSyncObjectGit(nn, gitSource.Repository, gitSource.Directory, gitSource.SourceFormat) } // RepoSyncObjectV1Beta1FromOtherRootRepo returns a v1beta1 RepoSync object which // uses a RootSync repo from nt.SyncSources. +// Use nt.RepoSyncObjectGit is you want to change the source expectation. func RepoSyncObjectV1Beta1FromOtherRootRepo(nt *NT, nn types.NamespacedName, repoName string) *v1beta1.RepoSync { - // TODO: Allow supplying the Repository, instead of requiring it to already exist repoID := core.RootSyncID(repoName) source, found := nt.SyncSources[repoID] if !found { - nt.T.Fatal("nonexistent %s source: %s", repoID.Kind, repoID.ObjectKey) + nt.T.Fatalf("nonexistent %s source: %s", repoID.Kind, repoID.ObjectKey) } gitSource, ok := source.(*syncsource.GitSyncSource) if !ok { - nt.T.Fatal("expected SyncSources for %s to have *GitSyncSource, but got %T", repoID, source) - } - repoURL := nt.GitProvider.SyncURL(gitSource.Repository.RemoteRepoName) - sourceFormat := gitSource.Repository.Format - rs := RepoSyncObjectV1Beta1(nn, repoURL, sourceFormat) - if nt.DefaultReconcileTimeout != nil { - rs.Spec.SafeOverride().ReconcileTimeout = toMetav1Duration(*nt.DefaultReconcileTimeout) - } else if rs.Spec.Override != nil { - rs.Spec.Override.ReconcileTimeout = nil - } - // Add dependencies to ensure managed objects can be deleted. - if err := SetRepoSyncDependencies(nt, rs); err != nil { - nt.T.Fatal(err) + nt.T.Fatalf("expected SyncSources for %s to have *GitSyncSource, but got %T", repoID, source) } - return rs + return nt.RepoSyncObjectGit(nn, gitSource.Repository, gitSource.Directory, gitSource.SourceFormat) } // setupCentralizedControl is a pure central-control mode. @@ -1417,28 +1422,18 @@ func DeletePodByLabel(nt *NT, label, value string, waitForChildren bool) { }, WaitTimeout(nt.DefaultWaitTimeout)) } -// SetPolicyDir updates the root-sync object with the provided policyDir. -func SetPolicyDir(nt *NT, syncName, policyDir string) { - nt.T.Logf("Set policyDir to %q", policyDir) +// SetRootSyncGitDir updates the root-sync object with the provided git dir. +func SetRootSyncGitDir(nt *NT, syncName, syncPath string) { + nt.T.Logf("Set spec.git.dir to %q", syncPath) rs := k8sobjects.RootSyncObjectV1Beta1(syncName) - if err := nt.KubeClient.Get(syncName, configmanagement.ControllerNamespace, rs); err != nil { - nt.T.Fatal(err) - } - if rs.Spec.Git.Dir != policyDir { - nt.MustMergePatch(rs, fmt.Sprintf(`{"spec": {"git": {"dir": "%s"}}}`, policyDir)) - } + nt.MustMergePatch(rs, fmt.Sprintf(`{"spec": {"git": {"dir": %q}}}`, syncPath)) } -// SetGitBranch updates the root-sync object with the provided git branch -func SetGitBranch(nt *NT, syncName, branch string) { +// SetRootSyncGitBranch updates the root-sync object with the provided git branch +func SetRootSyncGitBranch(nt *NT, syncName, branch string) { nt.T.Logf("Change git branch to %q", branch) rs := k8sobjects.RootSyncObjectV1Beta1(syncName) - if err := nt.KubeClient.Get(syncName, configmanagement.ControllerNamespace, rs); err != nil { - nt.T.Fatal(err) - } - if rs.Spec.Git.Branch != branch { - nt.MustMergePatch(rs, fmt.Sprintf(`{"spec": {"git": {"branch": "%s"}}}`, branch)) - } + nt.MustMergePatch(rs, fmt.Sprintf(`{"spec": {"git": {"branch": %q}}}`, branch)) } func toMetav1Duration(t time.Duration) *metav1.Duration { diff --git a/e2e/nomostest/config_sync_sources.go b/e2e/nomostest/config_sync_sources.go new file mode 100644 index 000000000..9b1b4e84c --- /dev/null +++ b/e2e/nomostest/config_sync_sources.go @@ -0,0 +1,139 @@ +// Copyright 2024 Google LLC +// +// 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 nomostest + +import ( + "kpt.dev/configsync/e2e/nomostest/gitproviders" + "kpt.dev/configsync/e2e/nomostest/registryproviders" + "kpt.dev/configsync/e2e/nomostest/syncsource" + "kpt.dev/configsync/pkg/api/configsync" + "kpt.dev/configsync/pkg/core" +) + +// SetExpectedSyncPath updates the SyncSource for the specified RootSync or +// RepoSync with the provided Git dir, OCI dir. +func SetExpectedSyncPath(nt *NT, syncID core.ID, syncPath string) { + source, exists := nt.SyncSources[syncID] + if !exists { + nt.T.Fatalf("Failed to update expectation for %s %s: nt.SyncSources not registered", syncID.Kind, syncID.ObjectKey) + } + switch tSource := source.(type) { + case *syncsource.GitSyncSource: + tSource.Directory = syncPath + case *syncsource.OCISyncSource: + tSource.Directory = syncPath + case *syncsource.HelmSyncSource: + nt.T.Fatalf("Failed to update expectation for %s %s: Use SetExpectedHelmChart instead", syncID.Kind, syncID.ObjectKey) + default: + nt.T.Fatalf("Invalid %s source %T: %s", syncID.Kind, source, syncID.Name) + } +} + +// SetExpectedGitSource creates, updates, or replaces the SyncSource for the +// specified RootSync or RepoSync with the provided Git repo. +func SetExpectedGitSource(nt *NT, syncID core.ID, repo *gitproviders.Repository, syncPath string, sourceFormat configsync.SourceFormat) { + source, exists := nt.SyncSources[syncID] + if !exists { + nt.T.Logf("Creating expectation for %s %s to sync with Git repo (repository: %q, directory: %q, sourceFormat: %q)", + syncID.Kind, syncID.ObjectKey, repo.Name, syncPath, sourceFormat) + nt.SyncSources[syncID] = &syncsource.GitSyncSource{ + Repository: repo, + SourceFormat: sourceFormat, + Directory: syncPath, + } + return + } + switch tSource := source.(type) { + case *syncsource.GitSyncSource: + nt.T.Logf("Updating expectation for %s %s to sync with Git repo (repository: %q, directory: %q, sourceFormat: %q)", + syncID.Kind, syncID.ObjectKey, repo.Name, syncPath, sourceFormat) + tSource.Repository = repo + tSource.SourceFormat = sourceFormat + tSource.Directory = syncPath + case *syncsource.OCISyncSource, *syncsource.HelmSyncSource: + nt.T.Logf("Replacing expectation for %s %s to sync with Git repo (repository: %q, directory: %q, sourceFormat: %q), instead of %T", + syncID.Kind, syncID.ObjectKey, repo.Name, syncPath, sourceFormat, source) + nt.SyncSources[syncID] = &syncsource.GitSyncSource{ + Repository: repo, + SourceFormat: sourceFormat, + Directory: syncPath, + } + default: + nt.T.Fatalf("Invalid %s source %T: %s", syncID.Kind, source, syncID.Name) + } +} + +// SetExpectedOCISource creates, updates, or replaces the SyncSource for the +// specified RootSync or RepoSync with the provided OCI image. +// ImageID is used for pulling. ImageDigest is used for validating the result. +func SetExpectedOCISource(nt *NT, syncID core.ID, imageID registryproviders.OCIImageID, imageDigest, syncPath string) { + source, exists := nt.SyncSources[syncID] + if !exists { + nt.T.Logf("Creating expectation for %s %s to sync with OCI image (image: %q, directory: %q)", + syncID.Kind, syncID.ObjectKey, imageID, syncPath) + nt.SyncSources[syncID] = &syncsource.OCISyncSource{ + ImageID: imageID, + Digest: imageDigest, + Directory: syncPath, + } + return + } + switch tSource := source.(type) { + case *syncsource.OCISyncSource: + nt.T.Logf("Updating expectation for %s %s to sync with OCI image (image: %q, directory: %q)", + syncID.Kind, syncID.ObjectKey, imageID, syncPath) + tSource.ImageID = imageID + tSource.Digest = imageDigest + tSource.Directory = syncPath + case *syncsource.GitSyncSource, *syncsource.HelmSyncSource: + nt.T.Logf("Replacing expectation for %s %s to sync with OCI image (image: %q, directory: %q), instead of %T", + syncID.Kind, syncID.ObjectKey, imageID, syncPath, source) + nt.SyncSources[syncID] = &syncsource.OCISyncSource{ + ImageID: imageID, + Digest: imageDigest, + Directory: syncPath, + } + default: + nt.T.Fatalf("Invalid %s source %T: %s", syncID.Kind, source, syncID.Name) + } +} + +// SetExpectedHelmSource creates, updates, or replaces the SyncSource for the +// specified RootSync or RepoSync with the provided Helm chart ID. +func SetExpectedHelmSource(nt *NT, syncID core.ID, chartID registryproviders.HelmChartID) { + source, exists := nt.SyncSources[syncID] + if !exists { + nt.T.Logf("Creating expectation for %s %s to sync with helm chart (id: %q)", + syncID.Kind, syncID.ObjectKey, chartID) + nt.SyncSources[syncID] = &syncsource.HelmSyncSource{ + ChartID: chartID, + } + return + } + switch tSource := source.(type) { + case *syncsource.HelmSyncSource: + nt.T.Logf("Updating expectation for %s %s to sync with helm chart (id: %q)", + syncID.Kind, syncID.ObjectKey, chartID) + tSource.ChartID = chartID + case *syncsource.GitSyncSource, *syncsource.OCISyncSource: + nt.T.Logf("Replacing expectation for %s %s to sync with helm chart (id: %q), instead of %T", + syncID.Kind, syncID.ObjectKey, chartID, source) + nt.SyncSources[syncID] = &syncsource.HelmSyncSource{ + ChartID: chartID, + } + default: + nt.T.Fatalf("Invalid %s source %T: %s", syncID.Kind, source, syncID.Name) + } +} diff --git a/e2e/nomostest/git-server.go b/e2e/nomostest/git-server.go index ee0895898..05d5396df 100644 --- a/e2e/nomostest/git-server.go +++ b/e2e/nomostest/git-server.go @@ -34,9 +34,9 @@ import ( const testGitNamespace = "config-management-system-test" const testGitServer = "test-git-server" -const testGitServerImage = testing.TestInfraArtifactRegistry + "/git-server:v1.0.0-68df304c" +const testGitServerImage = testing.TestInfraArtifactRepositoryAddress + "/git-server:v1.0.0-68df304c" const testGitHTTPServer = "http-git-server" -const testGitHTTPServerImage = testing.TestInfraArtifactRegistry + "/http-git-server:v1.0.0-b3b4984cd" +const testGitHTTPServerImage = testing.TestInfraArtifactRepositoryAddress + "/http-git-server:v1.0.0-b3b4984cd" const safeToEvictAnnotation = "cluster-autoscaler.kubernetes.io/safe-to-evict" func testGitServerSelector() map[string]string { diff --git a/e2e/nomostest/new.go b/e2e/nomostest/new.go index 8f31095c1..fc4ab2191 100644 --- a/e2e/nomostest/new.go +++ b/e2e/nomostest/new.go @@ -64,15 +64,7 @@ func newOptStruct(testName, tmpDir string, ntOptions ...ntopts.Opt) *ntopts.New Name: testName, TmpDir: tmpDir, MultiRepo: ntopts.MultiRepo{ - SyncSources: syncsource.Set{ - // All tests default to having a RootSync named root-sync using - // a Git repository, unless otherwise specified. - DefaultRootSyncID: &syncsource.GitSyncSource{ - // RootSync root-sync defaults to using hierarchy. - // Use ntopts.SyncWithGitSource with ntopts.Unstructured to change. - SourceFormat: configsync.SourceFormatHierarchy, - }, - }, + SyncSources: syncsource.Set{}, // Default to 1m to keep tests fast. // To override, use WithReconcileTimeout. ReconcileTimeout: ptr.To(1 * time.Minute), @@ -425,15 +417,36 @@ func applyAutoPilotKeepAlive(nt *NT) error { func setupTestCase(nt *NT, opts *ntopts.New) { nt.T.Log("[SETUP] New test case") + // Expect default RootSync root-sync + if _, found := opts.SyncSources[DefaultRootSyncID]; !found { + opts.SyncSources[DefaultRootSyncID] = &syncsource.GitSyncSource{} + } // init all repositories. This ensures that: // - local repo is initialized and empty // - remote repo exists (except for LocalProvider, which is done in portForwardGitServer) for id, source := range opts.SyncSources { switch tSource := source.(type) { case *syncsource.GitSyncSource: - nt.SyncSources[id] = &syncsource.GitSyncSource{ - Repository: initRepository(nt, id.Kind, id.ObjectKey, tSource.SourceFormat), + if tSource.Repository == nil { + // Initialize the Repository + tSource.Repository = initRepository(nt, id.Kind, id.ObjectKey, tSource.SourceFormat) + } + if tSource.Directory == "" { + // Set the default Directory + tSource.Directory = gitproviders.DefaultSyncDir + } + if tSource.SourceFormat == "" { + // Set the default SourceFormat + switch id.Kind { + case configsync.RootSyncKind: + // RootSyncs use Hierarchy by default + tSource.SourceFormat = configsync.SourceFormatHierarchy + case configsync.RepoSyncKind: + // RepoSync only ever use unstructured + tSource.SourceFormat = configsync.SourceFormatUnstructured + } } + nt.SyncSources[id] = tSource // case *syncsource.HelmSyncSource: // case *syncsource.OCISyncSource: // TODO: setup OCI & Helm RootSyncs diff --git a/e2e/nomostest/nt.go b/e2e/nomostest/nt.go index 79edf1b9a..efe9306d1 100644 --- a/e2e/nomostest/nt.go +++ b/e2e/nomostest/nt.go @@ -952,13 +952,6 @@ func RemoteRootRepoSha1Fn(nt *NT, nn types.NamespacedName) (string, error) { return commit, nil } -// HelmChartVersionShaFn returns the provided chart version as a function. -func HelmChartVersionShaFn(chartVersion string) Sha1Func { - return func(*NT, types.NamespacedName) (string, error) { - return chartVersion, nil - } -} - // RemoteNsRepoSha1Fn returns the latest commit from a RepoSync Git spec. func RemoteNsRepoSha1Fn(nt *NT, nn types.NamespacedName) (string, error) { rs := &v1beta1.RepoSync{} diff --git a/e2e/nomostest/ntopts/multi_repo.go b/e2e/nomostest/ntopts/multi_repo.go index 4fea162ad..46845c84a 100644 --- a/e2e/nomostest/ntopts/multi_repo.go +++ b/e2e/nomostest/ntopts/multi_repo.go @@ -71,15 +71,6 @@ func Unstructured(source *syncsource.GitSyncSource) { func SyncWithGitSource(id core.ID, gitOpts ...GitSourceOption) func(opt *New) { return func(opt *New) { source := &syncsource.GitSyncSource{} - // Set the default source options - switch id.Kind { - case configsync.RootSyncKind: - // RootSyncs use Hierarchy by default - source.SourceFormat = configsync.SourceFormatHierarchy - case configsync.RepoSyncKind: - // RepoSync only ever use unstructured - source.SourceFormat = configsync.SourceFormatUnstructured - } // Modify the default source with user specified options for _, gitOpt := range gitOpts { gitOpt(source) diff --git a/e2e/nomostest/registryproviders/artifact_registry.go b/e2e/nomostest/registryproviders/artifact_registry.go index bc851cf37..19ebd1b23 100644 --- a/e2e/nomostest/registryproviders/artifact_registry.go +++ b/e2e/nomostest/registryproviders/artifact_registry.go @@ -76,21 +76,27 @@ func (a *ArtifactRegistryProvider) Teardown() error { return nil } -// registryHost returns the domain of the artifact registry -func (a *ArtifactRegistryProvider) registryHost() string { +// RegistryRemoteAddress returns the remote address of the registry. +func (a *ArtifactRegistryProvider) RegistryRemoteAddress() string { return fmt.Sprintf("%s-docker.pkg.dev", a.location) } +// RepositoryPath returns the name or path of the repository, without the +// registry or image details. +func (a *ArtifactRegistryProvider) RepositoryPath() string { + return fmt.Sprintf("%s/%s/%s", a.project, a.repositoryName, a.repositorySuffix) +} + // repositoryLocalAddress returns the address of the repository in the // registry for use by local clients, like the helm or crane clients. func (a *ArtifactRegistryProvider) repositoryLocalAddress() (string, error) { - return a.repositoryRemoteAddress() + return a.repositoryRemoteAddress(), nil } // repositoryRemoteAddress returns the address of the repository in the // registry for use by remote clients, like Kubernetes or Config Sync. -func (a *ArtifactRegistryProvider) repositoryRemoteAddress() (string, error) { - return fmt.Sprintf("%s/%s/%s/%s", a.registryHost(), a.project, a.repositoryName, a.repositorySuffix), nil +func (a *ArtifactRegistryProvider) repositoryRemoteAddress() string { + return fmt.Sprintf("%s/%s", a.RegistryRemoteAddress(), a.RepositoryPath()) } // ImageLocalAddress returns the address of the image in the registry for @@ -105,12 +111,8 @@ func (a *ArtifactRegistryProvider) ImageLocalAddress(imageName string) (string, // ImageRemoteAddress returns the address of the image in the registry for // use by remote clients, like Kubernetes or Config Sync. -func (a *ArtifactRegistryProvider) ImageRemoteAddress(imageName string) (string, error) { - repoAddr, err := a.repositoryRemoteAddress() - if err != nil { - return "", err - } - return fmt.Sprintf("%s/%s", repoAddr, imageName), nil +func (a *ArtifactRegistryProvider) ImageRemoteAddress(imageName string) string { + return fmt.Sprintf("%s/%s", a.repositoryRemoteAddress(), imageName) } // createRepository uses gcloud to create the repository, if it doesn't exist. @@ -146,11 +148,7 @@ func (a *ArtifactRegistryProvider) createRepository() error { // Helm doesn't provide a way to delete remote package images, so we're using // gcloud instead. For helm charts, the image name is the chart name. func (a *ArtifactRegistryProvider) deleteImage(imageName, digest string) error { - imageAddress, err := a.ImageRemoteAddress(imageName) - if err != nil { - return err - } - imageAddress = fmt.Sprintf("%s@%s", imageAddress, digest) + imageAddress := fmt.Sprintf("%s@%s", a.ImageRemoteAddress(imageName), digest) if _, err := a.gcloudClient.Gcloud("artifacts", "docker", "images", "delete", imageAddress, "--delete-tags", "--project", a.project); err != nil { return fmt.Errorf("deleting image from registry %s: %w", imageAddress, err) } @@ -174,7 +172,7 @@ func (a *ArtifactRegistryOCIProvider) Client() CraneClient { // Login to the registry with the crane client func (a *ArtifactRegistryOCIProvider) Login() error { - registryHost := a.registryHost() + registryHost := a.RegistryRemoteAddress() if err := a.OCIClient.Login(registryHost); err != nil { return fmt.Errorf("OCIClient.Login(%q): %w", registryHost, err) } @@ -183,7 +181,7 @@ func (a *ArtifactRegistryOCIProvider) Login() error { // Logout of the registry with the crane client func (a *ArtifactRegistryOCIProvider) Logout() error { - registryHost := a.registryHost() + registryHost := a.RegistryRemoteAddress() if err := a.OCIClient.Logout(registryHost); err != nil { return fmt.Errorf("OCIClient.Logout(%q): %w", registryHost, err) } @@ -253,7 +251,7 @@ func (a *ArtifactRegistryHelmProvider) Client() HelmClient { // Login to the registry with the HelmClient func (a *ArtifactRegistryHelmProvider) Login() error { - registryHost := a.registryHost() + registryHost := a.RegistryRemoteAddress() if err := a.HelmClient.Login(registryHost); err != nil { return fmt.Errorf("HelmClient.Login(%q): %w", registryHost, err) } @@ -262,7 +260,7 @@ func (a *ArtifactRegistryHelmProvider) Login() error { // Logout of the registry with the HelmClient func (a *ArtifactRegistryHelmProvider) Logout() error { - registryHost := a.registryHost() + registryHost := a.RegistryRemoteAddress() if err := a.HelmClient.Logout(registryHost); err != nil { return fmt.Errorf("HelmClient.Logout(%q): %w", registryHost, err) } @@ -287,12 +285,8 @@ func (a *ArtifactRegistryHelmProvider) RepositoryLocalURL() (string, error) { // RepositoryRemoteURL is the repositoryRemoteAddress prepended with oci:// // For pulling with RSync's `.spec.helm.repo` -func (a *ArtifactRegistryHelmProvider) RepositoryRemoteURL() (string, error) { - repoAddr, err := a.repositoryRemoteAddress() - if err != nil { - return "", err - } - return ociURL(repoAddr), nil +func (a *ArtifactRegistryHelmProvider) RepositoryRemoteURL() string { + return ociURL(a.repositoryRemoteAddress()) } // PushPackage pushes the local helm chart as an OCI image to the remote registry. diff --git a/e2e/nomostest/registryproviders/local.go b/e2e/nomostest/registryproviders/local.go index f017e0359..3a4ba85f0 100644 --- a/e2e/nomostest/registryproviders/local.go +++ b/e2e/nomostest/registryproviders/local.go @@ -86,13 +86,7 @@ func (l *LocalProvider) repositoryLocalAddress() (string, error) { // repositoryLocalAddressWithProxy returns address of the repository using the // specified proxy address. func (l *LocalProvider) repositoryLocalAddressWithProxy(proxyAddress string) string { - return fmt.Sprintf("%s/%s/%s", proxyAddress, l.repositoryName, l.repositorySuffix) -} - -// repositoryRemoteAddress returns the address of the repository in the -// registry for use by remote clients, like Kubernetes or Config Sync. -func (l *LocalProvider) repositoryRemoteAddress() (string, error) { - return fmt.Sprintf("test-registry-server.test-registry-system/%s/%s", l.repositoryName, l.repositorySuffix), nil + return fmt.Sprintf("%s/%s", proxyAddress, l.RepositoryPath()) } // ImageLocalAddress returns the local port forwarded address that proxies to @@ -120,17 +114,31 @@ func (l *LocalProvider) ProxyAddress(localPort int) string { return fmt.Sprintf("localhost:%d", localPort) } +// RegistryRemoteAddress returns the remote address of the registry. +func (l *LocalProvider) RegistryRemoteAddress() string { + return "test-registry-server.test-registry-system" +} + +// RepositoryPath returns the name or path of the repository, without the +// registry or image details. +func (l *LocalProvider) RepositoryPath() string { + return fmt.Sprintf("%s/%s", l.repositoryName, l.repositorySuffix) +} + +// RepositoryRemoteAddress returns the address of the repository in the +// registry for use by remote clients, like Kubernetes or Config Sync. +func (l *LocalProvider) RepositoryRemoteAddress() string { + return fmt.Sprintf("%s/%s", l.RegistryRemoteAddress(), l.RepositoryPath()) +} + // ImageRemoteAddress returns the address of the registry service from within // the cluster. Fit for use from the *-sync containers inside the cluster. -func (l *LocalProvider) ImageRemoteAddress(imageName string) (string, error) { - address, err := l.repositoryRemoteAddress() - if err != nil { - return "", err - } +func (l *LocalProvider) ImageRemoteAddress(imageName string) string { + address := l.RepositoryRemoteAddress() if imageName != "" { address = fmt.Sprintf("%s/%s", address, imageName) } - return address, nil + return address } // LocalOCIProvider provides methods for interacting with the test registry-server @@ -251,12 +259,8 @@ func (l *LocalHelmProvider) RepositoryLocalURL() (string, error) { // RepositoryRemoteURL is the repositoryRemoteAddress prepended with oci:// // For pulling with RSync's `.spec.helm.repo` -func (l *LocalHelmProvider) RepositoryRemoteURL() (string, error) { - repoAddr, err := l.repositoryRemoteAddress() - if err != nil { - return repoAddr, err - } - return ociURL(repoAddr), nil +func (l *LocalHelmProvider) RepositoryRemoteURL() string { + return ociURL(l.RepositoryRemoteAddress()) } // Restore the proxy and re-push any previously pushed images. diff --git a/e2e/nomostest/registryproviders/oci_image.go b/e2e/nomostest/registryproviders/oci_image.go index ae91211d0..a20710428 100644 --- a/e2e/nomostest/registryproviders/oci_image.go +++ b/e2e/nomostest/registryproviders/oci_image.go @@ -19,6 +19,7 @@ import ( "os" "path/filepath" "strconv" + "strings" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -142,24 +143,27 @@ type OCIImage struct { Provider OCIRegistryProvider } +// OCIImageID returns the ID of the OCI image. +func (o *OCIImage) OCIImageID() OCIImageID { + return OCIImageID{ + Registry: o.Provider.RegistryRemoteAddress(), + Repository: o.Provider.RepositoryPath(), + Name: o.Name, + Tag: o.Tag, + Digest: o.Digest, + } +} + // RemoteAddressWithTag returns the image address with version tag. // For pulling with RSync's `.spec.oci.image` func (o *OCIImage) RemoteAddressWithTag() (string, error) { - imageAddr, err := o.Provider.ImageRemoteAddress(o.Name) - if err != nil { - return "", fmt.Errorf("OCIProvider.ImageRemoteAddress: %w", err) - } - return fmt.Sprintf("%s:%s", imageAddr, o.Tag), nil + return fmt.Sprintf("%s:%s", o.Provider.ImageRemoteAddress(o.Name), o.Tag), nil } // RemoteAddressWithDigest returns the image address with digest. // For pulling with RSync's `.spec.oci.image` func (o *OCIImage) RemoteAddressWithDigest() (string, error) { - imageAddr, err := o.Provider.ImageRemoteAddress(o.Name) - if err != nil { - return "", fmt.Errorf("OCIProvider.ImageRemoteAddress: %w", err) - } - return fmt.Sprintf("%s@%s", imageAddr, o.Digest), nil + return fmt.Sprintf("%s@%s", o.Provider.ImageRemoteAddress(o.Name), o.Digest), nil } // Delete the image from the remote registry using the provided registry endpoint. @@ -172,3 +176,75 @@ func (o *OCIImage) Delete() error { func ociURL(address string) string { return fmt.Sprintf("oci://%s", address) } + +// OCIImageID represents the minimum information required to pull an OCI image. +type OCIImageID struct { + // Registry address (optional, default dockerhub) without the repository or + // scheme. + Registry string + // Repository name or path (optional) used for namespacing and hierarchy. + Repository string + // Name of the image. + Name string + // Tag of the image (optional, default latest). + Tag string + // Digest of the image (optional), including the "sha256:"" prefix. + Digest string +} + +// Address returns the address of the image +// [REGISTRY/][REPOSITORY/]NAME[:TAG][@DIGEST], +// WITHOUT the "oci://" scheme. +func (id OCIImageID) Address() string { + sb := strings.Builder{} + if id.Registry != "" { + sb.WriteString(id.Registry) + sb.WriteRune('/') + } + if id.Repository != "" { + sb.WriteString(id.Repository) + sb.WriteRune('/') + } + sb.WriteString(id.Name) + if id.Tag != "" { + sb.WriteRune(':') + sb.WriteString(id.Tag) + } + if id.Digest != "" { + sb.WriteRune('@') + sb.WriteString(id.Digest) + } + return sb.String() +} + +// URL returns the URL of the image +// SCHEME://[REGISTRY/][REPOSITORY/]NAME[:TAG][@DIGEST], +// WITH the "oci://" scheme. +func (id OCIImageID) URL() string { + return ociURL(id.Address()) +} + +// String returns the URL of the image. +func (id OCIImageID) String() string { + return id.URL() +} + +// WithoutDigest returns a copy of the OCIImageID without the digest. +func (id OCIImageID) WithoutDigest() OCIImageID { + return OCIImageID{ + Registry: id.Registry, + Repository: id.Repository, + Name: id.Name, + Tag: id.Tag, + } +} + +// WithoutTag returns a copy of the OCIImageID without the tag. +func (id OCIImageID) WithoutTag() OCIImageID { + return OCIImageID{ + Registry: id.Registry, + Repository: id.Repository, + Name: id.Name, + Digest: id.Digest, + } +} diff --git a/e2e/nomostest/registryproviders/registry_provider.go b/e2e/nomostest/registryproviders/registry_provider.go index eb8fdace6..0e362663d 100644 --- a/e2e/nomostest/registryproviders/registry_provider.go +++ b/e2e/nomostest/registryproviders/registry_provider.go @@ -61,9 +61,16 @@ type ProxiedRegistryProvider interface { type OCIRegistryProvider interface { RegistryProvider + // RegistryRemoteAddress returns the address of the registry. + RegistryRemoteAddress() string + + // RepositoryPath returns the name or path of the repository, without the + // registry or image details. + RepositoryPath() string + // ImageRemoteAddress returns the address of the image in the registry. // For pulling with RSync's `.spec.oci.image` - ImageRemoteAddress(imageName string) (string, error) + ImageRemoteAddress(imageName string) string // PushImage pushes a local tarball as an OCI image to the remote registry. PushImage(imageName, tag, localSourceTgzPath string) (*OCIImage, error) @@ -81,7 +88,7 @@ type HelmRegistryProvider interface { // RepositoryRemoteURL is the RepositoryRemoteAddress prepended with oci:// // For pulling with RSync's `.spec.helm.repo` - RepositoryRemoteURL() (string, error) + RepositoryRemoteURL() string // PushPackage pushes a local helm chart as an OCI image to the remote registry. PushPackage(localChartTgzPath string) (*HelmPackage, error) diff --git a/e2e/nomostest/sync.go b/e2e/nomostest/sync.go index 522fa228e..5a8680924 100644 --- a/e2e/nomostest/sync.go +++ b/e2e/nomostest/sync.go @@ -28,9 +28,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -// RootSyncHasStatusSyncDirectory creates a Predicate that ensures that the +// RootSyncHasStatusSyncPath creates a Predicate that ensures that the // .status.sync.gitStatus.dir field on the passed RootSync matches the provided dir. -func RootSyncHasStatusSyncDirectory(dir string) testpredicates.Predicate { +func RootSyncHasStatusSyncPath(syncPath string) testpredicates.Predicate { return func(o client.Object) error { if o == nil { return testpredicates.ErrObjectNotFound @@ -51,7 +51,7 @@ func RootSyncHasStatusSyncDirectory(dir string) testpredicates.Predicate { configsync.RootSyncKind, i, log.AsJSON(condition), log.AsYAML(rs)) } } - err := statusHasSyncDirAndNoErrors(rs.Status.Status, rs.Spec.SourceType, dir) + err := statusHasSyncDirAndNoErrors(rs.Status.Status, rs.Spec.SourceType, syncPath) if err != nil { return fmt.Errorf("%s %w:\n%s", configsync.RootSyncKind, err, log.AsYAML(rs)) } @@ -59,9 +59,9 @@ func RootSyncHasStatusSyncDirectory(dir string) testpredicates.Predicate { } } -// RepoSyncHasStatusSyncDirectory creates a Predicate that ensures that the +// RepoSyncHasStatusSyncPath creates a Predicate that ensures that the // .status.sync.gitStatus.dir field on the passed RepoSync matches the provided dir. -func RepoSyncHasStatusSyncDirectory(dir string) testpredicates.Predicate { +func RepoSyncHasStatusSyncPath(syncPath string) testpredicates.Predicate { return func(o client.Object) error { if o == nil { return testpredicates.ErrObjectNotFound @@ -82,7 +82,7 @@ func RepoSyncHasStatusSyncDirectory(dir string) testpredicates.Predicate { configsync.RepoSyncKind, i, log.AsJSON(condition), log.AsYAML(rs)) } } - err := statusHasSyncDirAndNoErrors(rs.Status.Status, rs.Spec.SourceType, dir) + err := statusHasSyncDirAndNoErrors(rs.Status.Status, rs.Spec.SourceType, syncPath) if err != nil { return fmt.Errorf("%s %w:\n%s", configsync.RepoSyncKind, err, log.AsYAML(rs)) } @@ -161,35 +161,35 @@ func RepoSyncHasStatusSyncCommit(sha1 string) testpredicates.Predicate { } } -func statusHasSyncCommitAndNoErrors(status v1beta1.Status, sha1 string) error { +func statusHasSyncCommitAndNoErrors(status v1beta1.Status, commit string) error { if status.Source.ErrorSummary != nil && status.Source.ErrorSummary.TotalCount > 0 { return fmt.Errorf("status.source contains %d errors", status.Source.ErrorSummary.TotalCount) } - if commit := status.Source.Commit; commit != sha1 { - return fmt.Errorf("status.source.commit %q does not match git revision %q", commit, sha1) + if srcCommit := status.Source.Commit; srcCommit != commit { + return fmt.Errorf("status.source.commit %q does not match git revision %q", srcCommit, commit) } if status.Sync.ErrorSummary != nil && status.Sync.ErrorSummary.TotalCount > 0 { return fmt.Errorf("status.sync contains %d errors", status.Sync.ErrorSummary.TotalCount) } - if commit := status.Sync.Commit; commit != sha1 { - return fmt.Errorf("status.sync.commit %q does not match git revision %q", commit, sha1) + if syncCommit := status.Sync.Commit; syncCommit != commit { + return fmt.Errorf("status.sync.commit %q does not match git revision %q", syncCommit, commit) } if status.Rendering.ErrorSummary != nil && status.Rendering.ErrorSummary.TotalCount > 0 { return fmt.Errorf("status.rendering contains %d errors", status.Rendering.ErrorSummary.TotalCount) } - if commit := status.Rendering.Commit; commit != sha1 { - return fmt.Errorf("status.rendering.commit %q does not match git revision %q", commit, sha1) + if renderCommit := status.Rendering.Commit; renderCommit != commit { + return fmt.Errorf("status.rendering.commit %q does not match git revision %q", renderCommit, commit) } if message := status.Rendering.Message; message != parse.RenderingSucceeded && message != parse.RenderingSkipped { return fmt.Errorf("status.rendering.message %q does not indicate a successful state", message) } - if commit := status.LastSyncedCommit; commit != sha1 { - return fmt.Errorf("status.lastSyncedCommit %q does not match commit hash %q", commit, sha1) + if lastSyncedCommit := status.LastSyncedCommit; lastSyncedCommit != commit { + return fmt.Errorf("status.lastSyncedCommit %q does not match commit hash %q", lastSyncedCommit, commit) } return nil } -func statusHasSyncDirAndNoErrors(status v1beta1.Status, sourceType configsync.SourceType, dir string) error { +func statusHasSyncDirAndNoErrors(status v1beta1.Status, sourceType configsync.SourceType, syncPath string) error { if status.Source.ErrorSummary != nil && status.Source.ErrorSummary.TotalCount > 0 { return fmt.Errorf("status.source contains %d errors", status.Source.ErrorSummary.TotalCount) } @@ -207,58 +207,58 @@ func statusHasSyncDirAndNoErrors(status v1beta1.Status, sourceType configsync.So if status.Source.Oci == nil { return fmt.Errorf("status.source.oci is nil") } - if ociDir := status.Source.Oci.Dir; ociDir != dir { - return fmt.Errorf("status.source.oci.dir %q does not match the provided directory %q", ociDir, dir) + if ociDir := status.Source.Oci.Dir; ociDir != syncPath { + return fmt.Errorf("status.source.oci.dir %q does not match the expected directory %q", ociDir, syncPath) } if status.Sync.Oci == nil { return fmt.Errorf("status.sync.oci is nil") } - if ociDir := status.Sync.Oci.Dir; ociDir != dir { - return fmt.Errorf("status.sync.oci.dir %q does not match the provided directory %q", ociDir, dir) + if ociDir := status.Sync.Oci.Dir; ociDir != syncPath { + return fmt.Errorf("status.sync.oci.dir %q does not match the expected directory %q", ociDir, syncPath) } if status.Rendering.Oci == nil { return fmt.Errorf("status.rendering.oci is nil") } - if ociDir := status.Rendering.Oci.Dir; ociDir != dir { - return fmt.Errorf("status.rendering.oci.dir %q does not match the provided directory %q", ociDir, dir) + if ociDir := status.Rendering.Oci.Dir; ociDir != syncPath { + return fmt.Errorf("status.rendering.oci.dir %q does not match the expected directory %q", ociDir, syncPath) } case configsync.GitSource: if status.Source.Git == nil { return fmt.Errorf("status.source.git is nil") } - if gitDir := status.Source.Git.Dir; gitDir != dir { - return fmt.Errorf("status.source.git.dir %q does not match the provided directory %q", gitDir, dir) + if gitDir := status.Source.Git.Dir; gitDir != syncPath { + return fmt.Errorf("status.source.git.dir %q does not match the expected directory %q", gitDir, syncPath) } if status.Sync.Git == nil { return fmt.Errorf("status.sync.git is nil") } - if gitDir := status.Sync.Git.Dir; gitDir != dir { - return fmt.Errorf("status.sync.git.dir %q does not match the provided directory %q", gitDir, dir) + if gitDir := status.Sync.Git.Dir; gitDir != syncPath { + return fmt.Errorf("status.sync.git.dir %q does not match the expected directory %q", gitDir, syncPath) } if status.Rendering.Git == nil { return fmt.Errorf("status.rendering.git is nil") } - if gitDir := status.Rendering.Git.Dir; gitDir != dir { - return fmt.Errorf("status.rendering.git.dir %q does not match the provided directory %q", gitDir, dir) + if gitDir := status.Rendering.Git.Dir; gitDir != syncPath { + return fmt.Errorf("status.rendering.git.dir %q does not match the expected directory %q", gitDir, syncPath) } case configsync.HelmSource: if status.Source.Helm == nil { return fmt.Errorf("status.source.helm is nil") } - if helmChart := status.Source.Helm.Chart; helmChart != dir { - return fmt.Errorf("status.source.helm.chart %q does not match the provided chart %q", helmChart, dir) + if helmChart := status.Source.Helm.Chart; helmChart != syncPath { + return fmt.Errorf("status.source.helm.chart %q does not match the expected chart %q", helmChart, syncPath) } if status.Sync.Helm == nil { return fmt.Errorf("status.sync.helm is nil") } - if helmChart := status.Sync.Helm.Chart; helmChart != dir { - return fmt.Errorf("status.sync.helm.chart %q does not match the provided chart %q", helmChart, dir) + if helmChart := status.Sync.Helm.Chart; helmChart != syncPath { + return fmt.Errorf("status.sync.helm.chart %q does not match the expected chart %q", helmChart, syncPath) } if status.Rendering.Helm == nil { return fmt.Errorf("status.rendering.helm is nil") } - if helmChart := status.Rendering.Helm.Chart; helmChart != dir { - return fmt.Errorf("status.rendering.helm.chart %q does not match the provided chart %q", helmChart, dir) + if helmChart := status.Rendering.Helm.Chart; helmChart != syncPath { + return fmt.Errorf("status.rendering.helm.chart %q does not match the expected chart %q", helmChart, syncPath) } } return nil diff --git a/e2e/nomostest/syncsource/syncsource.go b/e2e/nomostest/syncsource/syncsource.go index 0f015875c..88a9a5167 100644 --- a/e2e/nomostest/syncsource/syncsource.go +++ b/e2e/nomostest/syncsource/syncsource.go @@ -15,7 +15,7 @@ package syncsource import ( - "fmt" + "strings" "kpt.dev/configsync/e2e/nomostest/gitproviders" "kpt.dev/configsync/e2e/nomostest/registryproviders" @@ -27,9 +27,13 @@ type SyncSource interface { // Type returns the SourceType of this source. Type() configsync.SourceType // Commit returns the current/latest "commit" for this source. - // The commit value is used to validate RSync status and metrics. + // The value is used to validate RSync status and metrics. Commit() (string, error) - // TODO: Add SourceFormat + // Path within the source to sync to the cluster. + // For Helm charts, the value represents the chart name instead. + // The value is used to validate RSync status. + Path() string + // TODO: Add SourceFormat? } // GitSyncSource is the "git" source, backed by a Git repository. @@ -39,6 +43,8 @@ type GitSyncSource struct { Repository *gitproviders.Repository // TODO: Add SyncPath and Branch/Revision to uniquely identify part of a repo SourceFormat configsync.SourceFormat + // Directory is the path within the source to sync to the cluster. + Directory string } // Type returns the SourceType of this source. @@ -51,6 +57,11 @@ func (s *GitSyncSource) Commit() (string, error) { return s.Repository.Hash() } +// Path within the git repository to sync to the cluster. +func (s *GitSyncSource) Path() string { + return s.Directory +} + // HelmSyncSource is the "helm" source, backed by a Helm chart. type HelmSyncSource struct { ChartID registryproviders.HelmChartID @@ -67,8 +78,26 @@ func (s *HelmSyncSource) Commit() (string, error) { return s.ChartID.Version, nil } +// Path returns the name of the current chart. +func (s *HelmSyncSource) Path() string { + return s.ChartID.Name +} + // OCISyncSource is the "oci" source, backed by an OCI image. type OCISyncSource struct { + // ImageID is the ID of the OCI image. + // + // This must use the "remote" address used by the reconciler, not the + // "local" address using the kubectl proxy. + // + // Since this is used to configure syncing, the image digest should only be + // specified if it should be used when pulling. + ImageID registryproviders.OCIImageID + // Digest of the OCI image, including "sha256:" prefix. + // This is the digest used for validating the image after pulling/syncing. + Digest string + // Directory is the path within the OCI image to sync to the cluster. + Directory string // TODO: add an OCI-specific image ID & migrate OCI RSyncs to use OCISyncSource // TODO: Add OCIRegistryProvider to allow for chart modifications } @@ -80,5 +109,10 @@ func (s *OCISyncSource) Type() configsync.SourceType { // Commit is not yet implemented. func (s *OCISyncSource) Commit() (string, error) { - return "", fmt.Errorf("not yet implemented: OCISyncSource.Commit") + return strings.TrimPrefix(s.Digest, "sha256:"), nil +} + +// Path within the OCI image to sync to the cluster. +func (s *OCISyncSource) Path() string { + return s.Directory } diff --git a/e2e/nomostest/testing/env.go b/e2e/nomostest/testing/env.go index 18848e551..9f49e2f84 100644 --- a/e2e/nomostest/testing/env.go +++ b/e2e/nomostest/testing/env.go @@ -15,21 +15,27 @@ package testing const ( - // ARHost is the host address of the Artifact Registry - ARHost = "us-docker.pkg.dev" - - // GCRHost is the host address of the Container Registry - GCRHost = "gcr.io" - + // ArtifactRegistryHost is the host address of the Artifact Registry + ArtifactRegistryHost = "us-docker.pkg.dev" + // GoogleContainerRegistryHost is the host address of the Container Registry + GoogleContainerRegistryHost = "gcr.io" // CSRHost is the host address of the Cloud Source Repositories CSRHost = "https://source.developers.google.com" - // TestInfraContainerRegistry is the Config Sync test-infra registry hosted on GCR - TestInfraContainerRegistry = GCRHost + "/kpt-config-sync-ci-artifacts" - // TestInfraArtifactRegistry is the Config Sync test-infra registry hosted on GAR - TestInfraArtifactRegistry = ARHost + "/kpt-config-sync-ci-artifacts/test-infra" - // ConfigSyncTestPublicRegistry is the Config Sync config-sync-test-public registry hosted on GAR - ConfigSyncTestPublicRegistry = ARHost + "/kpt-config-sync-ci-artifacts/config-sync-test-public" + // TestInfraContainerRepositoryPath is the Config Sync test-infra repository path + TestInfraContainerRepositoryPath = "kpt-config-sync-ci-artifacts" + // TestInfraArtifactRepositoryPath is the Config Sync test-infra repository path + TestInfraArtifactRepositoryPath = "kpt-config-sync-ci-artifacts/test-infra" + // ConfigSyncTestPublicRepositoryPath is the Config Sync config-sync-test-public repository path + ConfigSyncTestPublicRepositoryPath = "kpt-config-sync-ci-artifacts/config-sync-test-public" + + // TestInfraContainerRepositoryAddress is the Config Sync test-infra repository hosted on GCR + TestInfraContainerRepositoryAddress = GoogleContainerRegistryHost + "/" + TestInfraContainerRepositoryPath + // TestInfraArtifactRepositoryAddress is the Config Sync test-infra repository hosted on GAR + TestInfraArtifactRepositoryAddress = ArtifactRegistryHost + "/" + TestInfraArtifactRepositoryPath + // ConfigSyncTestPublicRepositoryAddress is the Config Sync config-sync-test-public repository hosted on GAR + ConfigSyncTestPublicRepositoryAddress = ArtifactRegistryHost + "/" + ConfigSyncTestPublicRepositoryPath + // HTTPDImage is the httpd image used by on-cluster test components HTTPDImage = "httpd:2" // RegistryImage is the registry image used by on-cluster test components diff --git a/e2e/nomostest/wait_for_sync.go b/e2e/nomostest/wait_for_sync.go index fb93e956a..5dcf3c579 100644 --- a/e2e/nomostest/wait_for_sync.go +++ b/e2e/nomostest/wait_for_sync.go @@ -22,8 +22,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - "kpt.dev/configsync/e2e/nomostest/gitproviders" - "kpt.dev/configsync/e2e/nomostest/syncsource" "kpt.dev/configsync/e2e/nomostest/taskgroup" "kpt.dev/configsync/e2e/nomostest/testpredicates" "kpt.dev/configsync/e2e/nomostest/testwatcher" @@ -45,7 +43,6 @@ type watchForAllSyncsOptions struct { skipNonRootRepos map[types.NamespacedName]bool rootSha1Fn Sha1Func repoSha1Fn Sha1Func - syncDirectoryMap map[types.NamespacedName]string } // WatchForAllSyncsOptions is an optional parameter for WaitForRepoSyncs. @@ -116,29 +113,12 @@ func SkipReadyCheck() WatchForAllSyncsOptions { } } -// SyncDirPredicatePair is a pair of the sync directory and the predicate. -type SyncDirPredicatePair struct { - Dir string +// SyncPathPredicatePair is a pair of the sync directory and the predicate. +type SyncPathPredicatePair struct { + Path string Predicate func(string) testpredicates.Predicate } -// WithSyncDirectoryMap provides a map of RootSync|RepoSync and the corresponding sync directory. -// The function is used to get the sync directory based on different RootSync|RepoSync name. -func WithSyncDirectoryMap(syncDirectoryMap map[types.NamespacedName]string) WatchForAllSyncsOptions { - return func(options *watchForAllSyncsOptions) { - options.syncDirectoryMap = syncDirectoryMap - } -} - -// syncDirectory returns the sync directory if RootSync|RepoSync is in the map. -// Otherwise, it returns the default sync directory: acme. -func syncDirectory(syncDirectoryMap map[types.NamespacedName]string, nn types.NamespacedName) string { - if syncDir, found := syncDirectoryMap[nn]; found { - return syncDir - } - return gitproviders.DefaultSyncDir -} - // WatchForAllSyncs calls WatchForSync on all Syncs in nt.SyncSources. // // If you want to validate specific fields of a Sync object, use @@ -154,7 +134,6 @@ func (nt *NT) WatchForAllSyncs(options ...WatchForAllSyncsOptions) error { skipNonRootRepos: make(map[types.NamespacedName]bool), rootSha1Fn: DefaultRootSha1Fn, repoSha1Fn: DefaultRepoSha1Fn, - syncDirectoryMap: map[types.NamespacedName]string{}, } // Override defaults with specified options for _, option := range options { @@ -174,23 +153,11 @@ func (nt *NT) WatchForAllSyncs(options ...WatchForAllSyncsOptions) error { if _, ok := opts.skipRootRepos[id.Name]; ok { continue } - // TODO: Move SyncPath logic into a SyncSource method - var syncPath string - switch tSource := source.(type) { - case *syncsource.GitSyncSource: - syncPath = syncDirectory(opts.syncDirectoryMap, id.ObjectKey) - case *syncsource.HelmSyncSource: - syncPath = tSource.ChartID.Name - // case *syncsource.OCISyncSource: - // TODO: setup OCI RootSyncs - default: - nt.T.Fatalf("Invalid %s source %T: %s", id.Kind, source, id.Name) - } idPtr := id tg.Go(func() error { return nt.WatchForSync(kinds.RootSyncV1Beta1(), idPtr.Name, idPtr.Namespace, opts.rootSha1Fn, RootSyncHasStatusSyncCommit, - &SyncDirPredicatePair{Dir: syncPath, Predicate: RootSyncHasStatusSyncDirectory}, + &SyncPathPredicatePair{Path: source.Path(), Predicate: RootSyncHasStatusSyncPath}, testwatcher.WatchTimeout(opts.timeout)) }) } @@ -201,23 +168,11 @@ func (nt *NT) WatchForAllSyncs(options ...WatchForAllSyncsOptions) error { if _, ok := opts.skipNonRootRepos[id.ObjectKey]; ok { continue } - // TODO: Move SyncPath logic into a SyncSource method - var syncPath string - switch tSource := source.(type) { - case *syncsource.GitSyncSource: - syncPath = syncDirectory(opts.syncDirectoryMap, id.ObjectKey) - case *syncsource.HelmSyncSource: - syncPath = tSource.ChartID.Name - // case *syncsource.OCISyncSource: - // TODO: setup OCI RootSyncs - default: - nt.T.Fatalf("Invalid %s source %T: %s", id.Kind, source, id.Name) - } idPtr := id tg.Go(func() error { return nt.WatchForSync(kinds.RepoSyncV1Beta1(), idPtr.Name, idPtr.Namespace, opts.repoSha1Fn, RepoSyncHasStatusSyncCommit, - &SyncDirPredicatePair{Dir: syncPath, Predicate: RepoSyncHasStatusSyncDirectory}, + &SyncPathPredicatePair{Path: source.Path(), Predicate: RepoSyncHasStatusSyncPath}, testwatcher.WatchTimeout(opts.timeout)) }) } @@ -242,7 +197,7 @@ func (nt *NT) WatchForSync( name, namespace string, sha1Func Sha1Func, syncSha1 func(string) testpredicates.Predicate, - syncDirPair *SyncDirPredicatePair, + syncDirPair *SyncPathPredicatePair, opts ...testwatcher.WatchOption, ) error { nt.T.Helper() @@ -268,7 +223,7 @@ func (nt *NT) WatchForSync( syncSha1(sha1), } if syncDirPair != nil { - predicates = append(predicates, syncDirPair.Predicate(syncDirPair.Dir)) + predicates = append(predicates, syncDirPair.Predicate(syncDirPair.Path)) } err = nt.Watcher.WatchObject(gvk, name, namespace, predicates, opts...) diff --git a/e2e/testcases/cli_test.go b/e2e/testcases/cli_test.go index 39bf7814f..9f3fa8aac 100644 --- a/e2e/testcases/cli_test.go +++ b/e2e/testcases/cli_test.go @@ -1680,9 +1680,9 @@ func TestNomosMigrateMonoRepo(t *testing.T) { err = nt.WatchForSync(kinds.RootSyncV1Beta1(), configsync.RootSyncName, configsync.ControllerNamespace, nomostest.RemoteRootRepoSha1Fn, nomostest.RootSyncHasStatusSyncCommit, - &nomostest.SyncDirPredicatePair{ - Dir: expectedRootSyncSpec.Dir, - Predicate: nomostest.RootSyncHasStatusSyncDirectory, + &nomostest.SyncPathPredicatePair{ + Path: expectedRootSyncSpec.Dir, + Predicate: nomostest.RootSyncHasStatusSyncPath, }, testwatcher.WatchTimeout(30*time.Second)) if err != nil { nt.T.Fatal(err) diff --git a/e2e/testcases/composition_test.go b/e2e/testcases/composition_test.go index 2491bd09d..d4e02babb 100644 --- a/e2e/testcases/composition_test.go +++ b/e2e/testcases/composition_test.go @@ -366,18 +366,18 @@ func waitForSync(nt *nomostest.NT, sha1Func nomostest.Sha1Func, objs ...client.O tg.Go(func() error { return nt.WatchForSync(kinds.RootSyncV1Beta1(), rsync.Name, rsync.Namespace, sha1Func, nomostest.RootSyncHasStatusSyncCommit, - &nomostest.SyncDirPredicatePair{ - Dir: rsync.Spec.Git.Dir, - Predicate: nomostest.RootSyncHasStatusSyncDirectory, + &nomostest.SyncPathPredicatePair{ + Path: rsync.Spec.Git.Dir, + Predicate: nomostest.RootSyncHasStatusSyncPath, }) }) case *v1beta1.RepoSync: tg.Go(func() error { return nt.WatchForSync(kinds.RepoSyncV1Beta1(), rsync.Name, rsync.Namespace, sha1Func, nomostest.RepoSyncHasStatusSyncCommit, - &nomostest.SyncDirPredicatePair{ - Dir: rsync.Spec.Git.Dir, - Predicate: nomostest.RepoSyncHasStatusSyncDirectory, + &nomostest.SyncPathPredicatePair{ + Path: rsync.Spec.Git.Dir, + Predicate: nomostest.RepoSyncHasStatusSyncPath, }) }) default: diff --git a/e2e/testcases/gcenode_test.go b/e2e/testcases/gcenode_test.go index d520aa527..49a7d545f 100644 --- a/e2e/testcases/gcenode_test.go +++ b/e2e/testcases/gcenode_test.go @@ -19,7 +19,6 @@ import ( "testing" appsv1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/types" "kpt.dev/configsync/e2e/nomostest" "kpt.dev/configsync/e2e/nomostest/kustomizecomponents" "kpt.dev/configsync/e2e/nomostest/ntopts" @@ -51,15 +50,16 @@ const ( // Public documentation: // https://cloud.google.com/anthos-config-management/docs/how-to/installing-config-sync#git-creds-secret func TestGCENodeCSR(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID repoSyncID := core.RepoSyncID(configsync.RepoSyncName, testNs) nt := nomostest.New(t, nomostesting.SyncSource, ntopts.RequireGKE(t), ntopts.GCENodeTest, ntopts.RequireCloudSourceRepository(t), - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ntopts.SyncWithGitSource(repoSyncID), ntopts.RepoSyncPermissions(policy.AllAdmin()), // NS reconciler manages a bunch of resources. ntopts.WithDelegatedControl) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) repoSyncKey := repoSyncID.ObjectKey repoSyncGitRepo := nt.SyncSourceGitRepository(repoSyncID) @@ -78,6 +78,7 @@ func TestGCENodeCSR(t *testing.T) { } } }`) + nomostest.SetExpectedSyncPath(nt, rootSyncID, "kustomize-components") nt.T.Log("Add the namespace-repo directory to RepoSync's repo") repoSync := k8sobjects.RepoSyncObjectV1Beta1(testNs, configsync.RepoSyncName) @@ -90,19 +91,11 @@ func TestGCENodeCSR(t *testing.T) { } } }`) + nomostest.SetExpectedSyncPath(nt, repoSyncID, "namespace-repo") - err := nt.WatchForAllSyncs( - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: "kustomize-components", - repoSyncKey: "namespace-repo", - })) - if err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) kustomizecomponents.ValidateAllTenants(nt, string(declared.RootScope), "base", "tenant-a", "tenant-b", "tenant-c") - if err := testutils.ReconcilerPodMissingFWICredsAnnotation(nt, nomostest.DefaultRootReconcilerName); err != nil { - nt.T.Fatal(err) - } + nt.Must(testutils.ReconcilerPodMissingFWICredsAnnotation(nt, nomostest.DefaultRootReconcilerName)) kustomizecomponents.ValidateTenant(nt, repoSyncKey.Namespace, repoSyncKey.Namespace, "base") } @@ -113,11 +106,12 @@ func TestGCENodeCSR(t *testing.T) { // - `roles/artifactregistry.reader` for access image in Artifact Registry. // - `roles/containerregistry.ServiceAgent` for access image in Container Registry. func TestGCENodeOCI(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID repoSyncID := core.RepoSyncID(configsync.RepoSyncName, testNs) nt := nomostest.New(t, nomostesting.SyncSource, ntopts.RequireGKE(t), ntopts.GCENodeTest, ntopts.RequireOCIArtifactRegistry(t), - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ntopts.SyncWithGitSource(repoSyncID), ntopts.RepoSyncPermissions(policy.AllAdmin()), // NS reconciler manages a bunch of resources. ntopts.WithDelegatedControl) @@ -126,20 +120,16 @@ func TestGCENodeOCI(t *testing.T) { nt.T.Fatal(err) } - rootSync := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) - rootSyncRef := nomostest.RootSyncNN(rootSync.Name) rootImage, err := nt.BuildAndPushOCIImage( - rootSyncRef, + rootSyncID.ObjectKey, registryproviders.ImageSourcePackage("hydration/kustomize-components"), registryproviders.ImageVersion("v1")) if err != nil { nt.T.Fatal(err) } nt.T.Log("Update RootSync to sync from an OCI image in Artifact Registry") - rootSyncOCI := nt.RootSyncObjectOCI(configsync.RootSyncName, rootImage) - if err = nt.KubeClient.Apply(rootSyncOCI); err != nil { - nt.T.Fatal(err) - } + rootSyncOCI := nt.RootSyncObjectOCI(rootSyncID.Name, rootImage.OCIImageID().WithoutDigest(), "", rootImage.Digest) + nt.Must(nt.KubeClient.Apply(rootSyncOCI)) repoSyncRef := repoSyncID.ObjectKey nsImage, err := nt.BuildAndPushOCIImage( @@ -150,37 +140,22 @@ func TestGCENodeOCI(t *testing.T) { nt.T.Fatal(err) } nt.T.Log("Update RepoSync to sync from an OCI image in Artifact Registry") - repoSyncOCI := nt.RepoSyncObjectOCI(repoSyncRef, nsImage) - if err = nt.KubeClient.Apply(repoSyncOCI); err != nil { - nt.T.Fatal(err) - } + repoSyncOCI := nt.RepoSyncObjectOCI(repoSyncRef, nsImage.OCIImageID().WithoutDigest(), "", nsImage.Digest) + nt.Must(nt.KubeClient.Apply(repoSyncOCI)) - err = nt.WatchForAllSyncs( + nt.Must(nt.WatchForAllSyncs( nomostest.WithRootSha1Func(imageDigestFuncByDigest(rootImage.Digest)), - nomostest.WithRepoSha1Func(imageDigestFuncByDigest(nsImage.Digest)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: ".", - repoSyncRef: ".", - })) - if err != nil { - nt.T.Fatal(err) - } + nomostest.WithRepoSha1Func(imageDigestFuncByDigest(nsImage.Digest)))) kustomizecomponents.ValidateAllTenants(nt, string(declared.RootScope), "base", "tenant-a", "tenant-b", "tenant-c") kustomizecomponents.ValidateTenant(nt, repoSyncRef.Namespace, repoSyncRef.Namespace, "base") tenant := "tenant-b" nt.T.Log("Update RootSync to sync from an OCI image in a private Google Container Registry") - nt.MustMergePatch(rootSync, fmt.Sprintf(`{"spec": {"oci": {"image": "%s", "dir": "%s"}}}`, privateGCRImage("kustomize-components"), tenant)) - err = nt.WatchForAllSyncs( + nt.MustMergePatch(rootSyncOCI, fmt.Sprintf(`{"spec": {"oci": {"image": "%s", "dir": "%s"}}}`, privateGCRImage("kustomize-components"), tenant)) + nomostest.SetExpectedSyncPath(nt, rootSyncID, tenant) + nt.Must(nt.WatchForAllSyncs( nomostest.WithRootSha1Func(imageDigestFuncByName(privateGCRImage("kustomize-components"))), - nomostest.WithRepoSha1Func(imageDigestFuncByDigest(nsImage.Digest)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: tenant, - repoSyncRef: ".", - })) - if err != nil { - nt.T.Fatal(err) - } + nomostest.WithRepoSha1Func(imageDigestFuncByDigest(nsImage.Digest)))) kustomizecomponents.ValidateAllTenants(nt, string(declared.RootScope), "../base", tenant) } diff --git a/e2e/testcases/helm_sync_test.go b/e2e/testcases/helm_sync_test.go index bb731c5e8..a15ad35d4 100644 --- a/e2e/testcases/helm_sync_test.go +++ b/e2e/testcases/helm_sync_test.go @@ -38,7 +38,6 @@ import ( "kpt.dev/configsync/e2e/nomostest/ntopts" "kpt.dev/configsync/e2e/nomostest/policy" "kpt.dev/configsync/e2e/nomostest/registryproviders" - "kpt.dev/configsync/e2e/nomostest/syncsource" nomostesting "kpt.dev/configsync/e2e/nomostest/testing" "kpt.dev/configsync/e2e/nomostest/testpredicates" "kpt.dev/configsync/pkg/api/configsync" @@ -400,9 +399,7 @@ func TestHelmLatestVersion(t *testing.T) { } nt.T.Logf("Wait for RootSync to sync from helm chart: %s", chart.HelmChartID) - nt.SyncSources[rootSyncID] = &syncsource.HelmSyncSource{ - ChartID: chart.HelmChartID, - } + nomostest.SetExpectedHelmSource(nt, rootSyncID, chart.HelmChartID) nt.Must(nt.WatchForAllSyncs()) nt.T.Log("Validate version label of a deployment from the helm chart") @@ -418,9 +415,7 @@ func TestHelmLatestVersion(t *testing.T) { } nt.T.Logf("Wait for RootSync to sync from helm chart: %s", chart.HelmChartID) - nt.SyncSources[rootSyncID] = &syncsource.HelmSyncSource{ - ChartID: chart.HelmChartID, - } + nomostest.SetExpectedHelmSource(nt, rootSyncID, chart.HelmChartID) nt.Must(nt.WatchForAllSyncs()) nt.T.Log("Validate version label of a deployment from the helm chart") @@ -450,9 +445,7 @@ func TestHelmVersionRange(t *testing.T) { Version: "15.4.1", // latest minor+patch with the same major version } nt.T.Logf("Wait for RootSync to sync from helm chart: %s", chartID) - nt.SyncSources[rootSyncID] = &syncsource.HelmSyncSource{ - ChartID: chartID, - } + nomostest.SetExpectedHelmSource(nt, rootSyncID, chartID) nt.Must(nt.WatchForAllSyncs()) nt.T.Log("Validate Deployment from chart exists") diff --git a/e2e/testcases/hydration_test.go b/e2e/testcases/hydration_test.go index 7b1b6ea85..f1f15189e 100644 --- a/e2e/testcases/hydration_test.go +++ b/e2e/testcases/hydration_test.go @@ -15,13 +15,14 @@ package e2e import ( + "fmt" "testing" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" - "k8s.io/apimachinery/pkg/types" "kpt.dev/configsync/e2e/nomostest" + "kpt.dev/configsync/e2e/nomostest/gitproviders" "kpt.dev/configsync/e2e/nomostest/kustomizecomponents" "kpt.dev/configsync/e2e/nomostest/ntopts" "kpt.dev/configsync/e2e/nomostest/taskgroup" @@ -42,30 +43,27 @@ import ( var expectedBuiltinOrigin = "configuredIn: kustomization.yaml\nconfiguredBy:\n apiVersion: builtin\n kind: HelmChartInflationGenerator\n" func TestHydrateKustomizeComponents(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.Hydration, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) - - syncDirMap := map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: "kustomize-components", - } + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) // Dry configs not yet added to repo, assert that hydration is disabled tg := taskgroup.New() tg.Go(func() error { return nt.Validate( - configsync.RootSyncName, - configsync.ControllerNamespace, + rootSyncID.Name, + rootSyncID.Namespace, &v1beta1.RootSync{}, testpredicates.MissingAnnotation(metadata.RequiresRenderingAnnotationKey), ) }) tg.Go(func() error { return nt.Validate( - core.RootReconcilerName(configsync.RootSyncName), - configsync.ControllerNamespace, + core.RootReconcilerName(rootSyncID.Name), + rootSyncID.Namespace, &appsv1.Deployment{}, testpredicates.DeploymentMissingContainer(reconcilermanager.HydrationController), testpredicates.DeploymentHasEnvVar(reconcilermanager.Reconciler, reconcilermanager.RenderingEnabled, "false"), @@ -78,24 +76,25 @@ func TestHydrateKustomizeComponents(t *testing.T) { nt.Must(rootSyncGitRepo.CommitAndPush("add DRY configs to the repository")) nt.T.Log("Update RootSync to sync from the kustomize-components directory") - rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) + rs := k8sobjects.RootSyncObjectV1Beta1(rootSyncID.Name) nt.MustMergePatch(rs, `{"spec": {"git": {"dir": "kustomize-components"}}}`) - nt.Must(nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(syncDirMap))) + nomostest.SetExpectedSyncPath(nt, rootSyncID, "kustomize-components") + nt.Must(nt.WatchForAllSyncs()) // Dry configs added to repo, assert that hydration is enabled tg = taskgroup.New() tg.Go(func() error { return nt.Validate( - configsync.RootSyncName, - configsync.ControllerNamespace, + rootSyncID.Name, + rootSyncID.Namespace, &v1beta1.RootSync{}, testpredicates.HasAnnotation(metadata.RequiresRenderingAnnotationKey, "true"), ) }) tg.Go(func() error { return nt.Validate( - core.RootReconcilerName(configsync.RootSyncName), - configsync.ControllerNamespace, + core.RootReconcilerName(rootSyncID.Name), + rootSyncID.Namespace, &appsv1.Deployment{}, testpredicates.DeploymentHasContainer(reconcilermanager.HydrationController), testpredicates.DeploymentHasEnvVar(reconcilermanager.Reconciler, reconcilermanager.RenderingEnabled, "true"), @@ -105,7 +104,7 @@ func TestHydrateKustomizeComponents(t *testing.T) { // Validate nomos status latestCommit := rootSyncGitRepo.MustHash(nt.T) - nt.Must(nt.Validate(configsync.RootSyncName, configsync.ControllerNamespace, &v1beta1.RootSync{}, + nt.Must(nt.Validate(rootSyncID.Name, rootSyncID.Namespace, &v1beta1.RootSync{}, testpredicates.RootSyncHasNomosStatus(latestCommit, "SYNCED"))) kustomizecomponents.ValidateAllTenants(nt, string(declared.RootScope), "base", "tenant-a", "tenant-b", "tenant-c") @@ -114,7 +113,7 @@ func TestHydrateKustomizeComponents(t *testing.T) { nt.Must(rootSyncGitRepo.Remove("./kustomize-components/kustomization.yml")) nt.Must(rootSyncGitRepo.CommitAndPush("remove the Kustomize configuration to make the sync fail")) latestCommit = rootSyncGitRepo.MustHash(nt.T) - nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), configsync.RootSyncName, configsync.ControllerNamespace, + nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), rootSyncID.Name, rootSyncID.Namespace, []testpredicates.Predicate{ testpredicates.RootSyncHasRenderingError(status.ActionableHydrationErrorCode, "Kustomization config file is missing from the sync directory"), testpredicates.RootSyncHasNomosStatus(latestCommit, "ERROR"), @@ -124,18 +123,18 @@ func TestHydrateKustomizeComponents(t *testing.T) { nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/kustomize-components/kustomization.yml", "./kustomize-components/kustomization.yml")) nt.Must(rootSyncGitRepo.CommitAndPush("add kustomization.yml back")) - nt.Must(nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(syncDirMap))) + nt.Must(nt.WatchForAllSyncs()) // Validate nomos status latestCommit = rootSyncGitRepo.MustHash(nt.T) - nt.Must(nt.Validate(configsync.RootSyncName, configsync.ControllerNamespace, &v1beta1.RootSync{}, + nt.Must(nt.Validate(rootSyncID.Name, rootSyncID.Namespace, &v1beta1.RootSync{}, testpredicates.RootSyncHasNomosStatus(latestCommit, "SYNCED"))) nt.T.Log("Make kustomization.yaml invalid") nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/invalid-kustomization.yaml", "./kustomize-components/kustomization.yml")) nt.Must(rootSyncGitRepo.CommitAndPush("update kustomization.yaml to make it invalid")) latestCommit = rootSyncGitRepo.MustHash(nt.T) - nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), configsync.RootSyncName, configsync.ControllerNamespace, + nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), rootSyncID.Name, rootSyncID.Namespace, []testpredicates.Predicate{ testpredicates.RootSyncHasRenderingError(status.ActionableHydrationErrorCode, "failed to run kustomize build"), testpredicates.RootSyncHasNomosStatus(latestCommit, "ERROR"), @@ -146,22 +145,22 @@ func TestHydrateKustomizeComponents(t *testing.T) { nt.Must(rootSyncGitRepo.Remove("./kustomize-components")) nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/compiled/kustomize-components", ".")) nt.Must(rootSyncGitRepo.CommitAndPush("Replace dry configs with wet configs")) - nt.Must(nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(syncDirMap))) + nt.Must(nt.WatchForAllSyncs()) nt.T.Log("Verify the hydration-controller is omitted after dry configs were removed") // Dry configs removed from repo, assert that hydration is disabled again tg = taskgroup.New() tg.Go(func() error { return nt.Validate( - configsync.RootSyncName, - configsync.ControllerNamespace, + rootSyncID.Name, + rootSyncID.Namespace, &v1beta1.RootSync{}, testpredicates.HasAnnotation(metadata.RequiresRenderingAnnotationKey, "false"), ) }) tg.Go(func() error { return nt.Validate( - core.RootReconcilerName(configsync.RootSyncName), + core.RootReconcilerName(rootSyncID.Name), configsync.ControllerNamespace, &appsv1.Deployment{}, testpredicates.DeploymentMissingContainer(reconcilermanager.HydrationController), @@ -172,24 +171,22 @@ func TestHydrateKustomizeComponents(t *testing.T) { } func TestHydrateExternalFiles(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.Hydration, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) - - syncDirMap := map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: "external-files", - } + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) nt.T.Log("Add the external files root directory") nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/external-files", ".")) nt.Must(rootSyncGitRepo.CommitAndPush("add DRY configs to the repository")) nt.T.Log("Update RootSync to sync from the external-files directory") - rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) + rs := k8sobjects.RootSyncObjectV1Beta1(rootSyncID.Name) nt.MustMergePatch(rs, `{"spec": {"git": {"dir": "external-files"}}}`) - nt.Must(nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(syncDirMap))) + nomostest.SetExpectedSyncPath(nt, rootSyncID, "external-files") + nt.Must(nt.WatchForAllSyncs()) nt.T.Log("Validating resources are synced") tg := taskgroup.New() @@ -204,30 +201,32 @@ func TestHydrateExternalFiles(t *testing.T) { } func TestHydrateHelmComponents(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.Hydration, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) nt.T.Log("Add the helm components root directory") nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/helm-components", ".")) nt.Must(rootSyncGitRepo.CommitAndPush("add DRY configs to the repository")) nt.T.Log("Update RootSync to sync from the helm-components directory") - rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) + rs := k8sobjects.RootSyncObjectV1Beta1(rootSyncID.Name) if nt.IsGKEAutopilot { // b/209458334: set a higher memory of the hydration-controller on Autopilot clusters to avoid the kustomize build failure nt.MustMergePatch(rs, `{"spec": {"git": {"dir": "helm-components"}, "override": {"resources": [{"containerName": "hydration-controller", "memoryRequest": "200Mi"}]}}}`) } else { nt.MustMergePatch(rs, `{"spec": {"git": {"dir": "helm-components"}}}`) } + nomostest.SetExpectedSyncPath(nt, rootSyncID, "helm-components") - nt.Must(nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{nomostest.DefaultRootRepoNamespacedName: "helm-components"}))) + nt.Must(nt.WatchForAllSyncs()) // Validate nomos status latestCommit := rootSyncGitRepo.MustHash(nt.T) - nt.Must(nt.Validate(configsync.RootSyncName, configsync.ControllerNamespace, &v1beta1.RootSync{}, + nt.Must(nt.Validate(rootSyncID.Name, rootSyncID.Namespace, &v1beta1.RootSync{}, testpredicates.RootSyncHasNomosStatus(latestCommit, "SYNCED"))) nt.T.Log("Validate resources are synced") @@ -251,7 +250,7 @@ func TestHydrateHelmComponents(t *testing.T) { nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/helm-components-remote-values-kustomization.yaml", "./helm-components/kustomization.yaml")) nt.Must(rootSyncGitRepo.CommitAndPush("Render with a remote values.yaml file from a public repo")) - nt.Must(nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{nomostest.DefaultRootRepoNamespacedName: "helm-components"}))) + nt.Must(nt.WatchForAllSyncs()) nt.Must(nt.Validate("my-coredns-coredns", "coredns", &appsv1.Deployment{}, testpredicates.DeploymentContainerPullPolicyEquals("coredns", "Always"), @@ -260,31 +259,33 @@ func TestHydrateHelmComponents(t *testing.T) { // Validate nomos status latestCommit = rootSyncGitRepo.MustHash(nt.T) - nt.Must(nt.Validate(configsync.RootSyncName, configsync.ControllerNamespace, &v1beta1.RootSync{}, + nt.Must(nt.Validate(rootSyncID.Name, rootSyncID.Namespace, &v1beta1.RootSync{}, testpredicates.RootSyncHasNomosStatus(latestCommit, "SYNCED"))) } func TestHydrateHelmOverlay(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.Hydration, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) nt.T.Log("Add the helm-overlay root directory") nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/helm-overlay", ".")) nt.Must(rootSyncGitRepo.CommitAndPush("add DRY configs to the repository")) nt.T.Log("Update RootSync to sync from the helm-overlay directory") - rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) + rs := k8sobjects.RootSyncObjectV1Beta1(rootSyncID.Name) if nt.IsGKEAutopilot { // b/209458334: set a higher memory of the hydration-controller on Autopilot clusters to avoid the kustomize build failure nt.MustMergePatch(rs, `{"spec": {"git": {"dir": "helm-overlay"}, "override": {"resources": [{"containerName": "hydration-controller", "memoryRequest": "200Mi"}]}}}`) } else { nt.MustMergePatch(rs, `{"spec": {"git": {"dir": "helm-overlay"}}}`) } + nomostest.SetExpectedSyncPath(nt, rootSyncID, "helm-overlay") - nt.Must(nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{nomostest.DefaultRootRepoNamespacedName: "helm-overlay"}))) + nt.Must(nt.WatchForAllSyncs()) nt.T.Log("Validate resources are synced") nt.Must(nt.Validate("my-coredns-coredns", "coredns", &appsv1.Deployment{}, @@ -297,7 +298,7 @@ func TestHydrateHelmOverlay(t *testing.T) { // Validate nomos status latestCommit := rootSyncGitRepo.MustHash(nt.T) - nt.Must(nt.Validate(configsync.RootSyncName, configsync.ControllerNamespace, &v1beta1.RootSync{}, + nt.Must(nt.Validate(rootSyncID.Name, rootSyncID.Namespace, &v1beta1.RootSync{}, testpredicates.RootSyncHasNomosStatus(latestCommit, "SYNCED"))) nt.T.Log("Make the hydration fail by checking in an invalid kustomization.yaml") @@ -305,7 +306,7 @@ func TestHydrateHelmOverlay(t *testing.T) { nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/resource-duplicate/namespace_tenant-a.yaml", "./helm-overlay/namespace_tenant-a.yaml")) nt.Must(rootSyncGitRepo.CommitAndPush("Update kustomization.yaml with duplicated resources")) latestCommit = rootSyncGitRepo.MustHash(nt.T) - nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), configsync.RootSyncName, configsync.ControllerNamespace, + nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), rootSyncID.Name, rootSyncID.Namespace, []testpredicates.Predicate{ testpredicates.RootSyncHasRenderingError(status.ActionableHydrationErrorCode, "failed to run kustomize build"), testpredicates.RootSyncHasNomosStatus(latestCommit, "ERROR"), @@ -315,7 +316,7 @@ func TestHydrateHelmOverlay(t *testing.T) { nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/deprecated-GK/kustomization.yaml", "./helm-overlay/kustomization.yaml")) nt.Must(rootSyncGitRepo.CommitAndPush("Update kustomization.yaml to render a deprecated group and kind")) latestCommit = rootSyncGitRepo.MustHash(nt.T) - nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), configsync.RootSyncName, configsync.ControllerNamespace, + nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), rootSyncID.Name, rootSyncID.Namespace, []testpredicates.Predicate{ testpredicates.RootSyncHasSourceError(nonhierarchical.DeprecatedGroupKindErrorCode, "The config is using a deprecated Group and Kind"), testpredicates.RootSyncHasNomosStatus(latestCommit, "ERROR"), @@ -323,19 +324,21 @@ func TestHydrateHelmOverlay(t *testing.T) { } func TestHydrateRemoteResources(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.Hydration, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) nt.T.Log("Add the remote-base root directory") nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/remote-base", ".")) nt.Must(rootSyncGitRepo.CommitAndPush("add DRY configs to the repository")) nt.T.Log("Update RootSync to sync from the remote-base directory without enable shell in hydration controller") - rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) + rs := k8sobjects.RootSyncObjectV1Beta1(rootSyncID.Name) nt.MustMergePatch(rs, `{"spec": {"git": {"dir": "remote-base"}}}`) - nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), configsync.RootSyncName, configsync.ControllerNamespace, + nomostest.SetExpectedSyncPath(nt, rootSyncID, "remote-base") + nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), rootSyncID.Name, rootSyncID.Namespace, []testpredicates.Predicate{ testpredicates.RootSyncHasRenderingError(status.ActionableHydrationErrorCode, ""), })) @@ -349,7 +352,7 @@ func TestHydrateRemoteResources(t *testing.T) { nt.T.Log("Enable shell in hydration controller") nt.MustMergePatch(rs, `{"spec": {"override": {"enableShellInRendering": true}}}`) - nt.Must(nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{nomostest.DefaultRootRepoNamespacedName: "remote-base"}))) + nt.Must(nt.WatchForAllSyncs()) nt.Must(nt.Validate(nomostest.DefaultRootReconcilerName, configsync.ControllerNamespace, &appsv1.Deployment{}, testpredicates.HasExactlyImage(reconcilermanager.HydrationController, reconcilermanager.HydrationControllerWithShell, "", ""))) expectedOrigin := "path: base/namespace.yaml\nrepo: https://github.com/config-sync-examples/kustomize-components\nref: main\n" @@ -361,7 +364,7 @@ func TestHydrateRemoteResources(t *testing.T) { nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/remote-overlay-kustomization.yaml", "./remote-base/kustomization.yaml")) nt.Must(rootSyncGitRepo.CommitAndPush("Update kustomization.yaml to use a remote overlay")) - nt.Must(nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{nomostest.DefaultRootRepoNamespacedName: "remote-base"}))) + nt.Must(nt.WatchForAllSyncs()) nt.T.Log("Validate resources are synced") expectedNamespaces = []string{"tenant-b"} @@ -370,7 +373,7 @@ func TestHydrateRemoteResources(t *testing.T) { // Update kustomization.yaml to use remote resources nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/remote-resources-kustomization.yaml", "./remote-base/kustomization.yaml")) nt.Must(rootSyncGitRepo.CommitAndPush("Update kustomization.yaml to use remote resources")) - nt.Must(nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{nomostest.DefaultRootRepoNamespacedName: "remote-base"}))) + nt.Must(nt.WatchForAllSyncs()) nt.T.Log("Validate resources are synced") expectedNamespaces = []string{"tenant-a", "tenant-b", "tenant-c"} @@ -380,14 +383,16 @@ func TestHydrateRemoteResources(t *testing.T) { nt.Must(rootSyncGitRepo.Remove("./remote-base")) nt.Must(rootSyncGitRepo.CommitAndPush("Remove remote-base repository")) nt.T.Log("Disable shell in hydration controller") - nt.MustMergePatch(rs, `{"spec": {"override": {"enableShellInRendering": false}, "git": {"dir": "acme"}}}`) + nt.MustMergePatch(rs, fmt.Sprintf(`{"spec": {"override": {"enableShellInRendering": false}, "git": {"dir": %q}}}`, + gitproviders.DefaultSyncDir)) + nomostest.SetExpectedSyncPath(nt, rootSyncID, gitproviders.DefaultSyncDir) nt.Must(nt.WatchForAllSyncs()) nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/remote-base", ".")) nt.Must(rootSyncGitRepo.CommitAndPush("add DRY configs to the repository")) nt.T.Log("Update RootSync to sync from the remote-base directory when disable shell in hydration controller") nt.MustMergePatch(rs, `{"spec": {"git": {"dir": "remote-base"}}}`) - nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), configsync.RootSyncName, configsync.ControllerNamespace, + nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), rootSyncID.Name, rootSyncID.Namespace, []testpredicates.Predicate{ testpredicates.RootSyncHasRenderingError(status.ActionableHydrationErrorCode, ""), })) @@ -396,25 +401,27 @@ func TestHydrateRemoteResources(t *testing.T) { } func TestHydrateResourcesInRelativePath(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.Hydration, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) nt.T.Log("Add the root directory") nt.Must(rootSyncGitRepo.Copy("../testdata/hydration/relative-path", ".")) nt.Must(rootSyncGitRepo.CommitAndPush("add DRY configs to the repository")) nt.T.Log("Update RootSync to sync from the relative-path directory") - rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) + rs := k8sobjects.RootSyncObjectV1Beta1(rootSyncID.Name) nt.MustMergePatch(rs, `{"spec": {"git": {"dir": "relative-path/overlays/dev"}}}`) + nomostest.SetExpectedSyncPath(nt, rootSyncID, "relative-path/overlays/dev") - nt.Must(nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{nomostest.DefaultRootRepoNamespacedName: "relative-path/overlays/dev"}))) + nt.Must(nt.WatchForAllSyncs()) // Validate nomos status latestCommit := rootSyncGitRepo.MustHash(nt.T) - nt.Must(nt.Validate(configsync.RootSyncName, configsync.ControllerNamespace, &v1beta1.RootSync{}, + nt.Must(nt.Validate(rootSyncID.Name, rootSyncID.Namespace, &v1beta1.RootSync{}, testpredicates.RootSyncHasNomosStatus(latestCommit, "SYNCED"))) nt.T.Log("Validating resources are synced") diff --git a/e2e/testcases/invalid_git_branch_test.go b/e2e/testcases/invalid_git_branch_test.go index 8e7a2d0e8..af480854b 100644 --- a/e2e/testcases/invalid_git_branch_test.go +++ b/e2e/testcases/invalid_git_branch_test.go @@ -34,7 +34,7 @@ func TestInvalidRootSyncBranchStatus(t *testing.T) { rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) // Update RootSync to invalid branch name - nomostest.SetGitBranch(nt, configsync.RootSyncName, "invalid-branch") + nomostest.SetRootSyncGitBranch(nt, configsync.RootSyncName, "invalid-branch") nt.WaitForRootSyncSourceError(configsync.RootSyncName, status.SourceErrorCode, "") @@ -54,7 +54,7 @@ func TestInvalidRootSyncBranchStatus(t *testing.T) { } // Update RootSync to valid branch name - nomostest.SetGitBranch(nt, configsync.RootSyncName, gitproviders.MainBranch) + nomostest.SetRootSyncGitBranch(nt, configsync.RootSyncName, gitproviders.MainBranch) if err := nt.WatchForAllSyncs(); err != nil { nt.T.Fatal(err) @@ -138,7 +138,7 @@ func TestSyncFailureAfterSuccessfulSyncs(t *testing.T) { nt.T.Cleanup(func() { nt.T.Log("Resetting all RootSync branches to main") nt.Must(rootSyncGitRepo.CheckoutBranch(gitproviders.MainBranch)) - nomostest.SetGitBranch(nt, configsync.RootSyncName, gitproviders.MainBranch) + nomostest.SetRootSyncGitBranch(nt, configsync.RootSyncName, gitproviders.MainBranch) if err := nt.WatchForAllSyncs(); err != nil { nt.T.Fatal(err) } @@ -157,7 +157,7 @@ func TestSyncFailureAfterSuccessfulSyncs(t *testing.T) { nt.Must(rootSyncGitRepo.CommitAndPushBranch("add namespace to acme directory", devBranch)) // Update RootSync to sync from the dev branch - nomostest.SetGitBranch(nt, configsync.RootSyncName, devBranch) + nomostest.SetRootSyncGitBranch(nt, configsync.RootSyncName, devBranch) if err := nt.WatchForAllSyncs(); err != nil { nt.T.Fatal(err) } diff --git a/e2e/testcases/kcc_test.go b/e2e/testcases/kcc_test.go index 5c3a787b4..392b4097b 100644 --- a/e2e/testcases/kcc_test.go +++ b/e2e/testcases/kcc_test.go @@ -29,7 +29,6 @@ import ( "kpt.dev/configsync/e2e/nomostest/testpredicates" "kpt.dev/configsync/e2e/nomostest/testresourcegroup" "kpt.dev/configsync/e2e/nomostest/testwatcher" - "kpt.dev/configsync/pkg/api/configsync" "kpt.dev/configsync/pkg/api/kpt.dev/v1alpha1" "kpt.dev/configsync/pkg/core/k8sobjects" "kpt.dev/configsync/pkg/kinds" @@ -41,21 +40,17 @@ import ( // It then deletes KCC resources and verifies the GCP resources // are removed successfully. func TestKCCResourcesOnCSR(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.SyncSource, ntopts.KCCTest, ntopts.RequireGKE(t)) - rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) + rs := k8sobjects.RootSyncObjectV1Beta1(rootSyncID.Name) nt.T.Log("sync to the kcc resources from a CSR repo") nt.MustMergePatch(rs, fmt.Sprintf(`{"spec": {"git": {"dir": "kcc", "branch": "main", "repo": "%s/p/%s/r/configsync-kcc", "auth": "gcpserviceaccount","gcpServiceAccountEmail": "e2e-test-csr-reader@%s.iam.gserviceaccount.com", "secretRef": {"name": ""}}, "sourceFormat": "unstructured"}}`, nomostesting.CSRHost, *e2e.GCPProject, *e2e.GCPProject)) + nomostest.SetExpectedSyncPath(nt, rootSyncID, "kcc") - err := nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(nomostest.RemoteRootRepoSha1Fn), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: "kcc", - })) - if err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.WithRootSha1Func(nomostest.RemoteRootRepoSha1Fn))) // Verify that the GCP resources are created. gvkPubSubTopic := schema.GroupVersionKind{ @@ -104,14 +99,9 @@ func TestKCCResourcesOnCSR(t *testing.T) { // Remove the kcc resources nt.T.Log("sync to an empty directory from a CSR repo") nt.MustMergePatch(rs, `{"spec": {"git": {"dir": "kcc-empty"}}}`) - err = nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(nomostest.RemoteRootRepoSha1Fn), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: "kcc-empty", - })) - if err != nil { - nt.T.Fatal(err) - } + nomostest.SetExpectedSyncPath(nt, rootSyncID, "kcc-empty") + nt.Must(nt.WatchForAllSyncs( + nomostest.WithRootSha1Func(nomostest.RemoteRootRepoSha1Fn))) // Wait until all objects are not found tg = taskgroup.New() diff --git a/e2e/testcases/multi_sync_test.go b/e2e/testcases/multi_sync_test.go index e2ab2525c..12aebb3b8 100644 --- a/e2e/testcases/multi_sync_test.go +++ b/e2e/testcases/multi_sync_test.go @@ -28,10 +28,10 @@ import ( "k8s.io/apimachinery/pkg/util/validation" "kpt.dev/configsync/e2e" "kpt.dev/configsync/e2e/nomostest" + "kpt.dev/configsync/e2e/nomostest/gitproviders" "kpt.dev/configsync/e2e/nomostest/metrics" "kpt.dev/configsync/e2e/nomostest/ntopts" "kpt.dev/configsync/e2e/nomostest/policy" - "kpt.dev/configsync/e2e/nomostest/syncsource" "kpt.dev/configsync/e2e/nomostest/taskgroup" nomostesting "kpt.dev/configsync/e2e/nomostest/testing" "kpt.dev/configsync/e2e/nomostest/testpredicates" @@ -67,9 +67,6 @@ func TestMultiSyncs_Unstructured_MixedControl(t *testing.T) { rootSync1ID := core.RootSyncID("rr1") rootSync2ID := core.RootSyncID("rr2") rootSync3ID := core.RootSyncID("rr3") - // rootSync1Key := rootSync1ID.ObjectKey // unused - rootSync2Key := rootSync2ID.ObjectKey - rootSync3Key := rootSync3ID.ObjectKey repoSync1ID := core.RepoSyncID("nr1", testNs) repoSync2ID := core.RepoSyncID("nr2", testNs) repoSync3ID := core.RepoSyncID("nr3", testNs) @@ -119,31 +116,15 @@ func TestMultiSyncs_Unstructured_MixedControl(t *testing.T) { } }) - var newRepos []types.NamespacedName - newRepos = append(newRepos, rootSync2Key) - newRepos = append(newRepos, rootSync3Key) - newRepos = append(newRepos, repoSync2Key) - newRepos = append(newRepos, repoSync3Key) - newRepos = append(newRepos, repoSync4Key) - newRepos = append(newRepos, repoSync5Key) + resetExpectedGitSync(nt, rootSync2ID) + resetExpectedGitSync(nt, rootSync3ID) + resetExpectedGitSync(nt, repoSync2ID) + resetExpectedGitSync(nt, repoSync3ID) + resetExpectedGitSync(nt, repoSync4ID) + resetExpectedGitSync(nt, repoSync5ID) - if nt.GitProvider.Type() == e2e.Local { - nomostest.InitGitRepos(nt, newRepos...) - } - - rootSync2GitRepo := nomostest.ResetRepository(nt, configsync.RootSyncKind, rootSync2Key, configsync.SourceFormatUnstructured) - rootSync3GitRepo := nomostest.ResetRepository(nt, configsync.RootSyncKind, rootSync3Key, configsync.SourceFormatUnstructured) - repoSync2GitRepo := nomostest.ResetRepository(nt, configsync.RepoSyncKind, repoSync2Key, configsync.SourceFormatUnstructured) - repoSync3GitRepo := nomostest.ResetRepository(nt, configsync.RepoSyncKind, repoSync3Key, configsync.SourceFormatUnstructured) - repoSync4GitRepo := nomostest.ResetRepository(nt, configsync.RepoSyncKind, repoSync4Key, configsync.SourceFormatUnstructured) - repoSync5GitRepo := nomostest.ResetRepository(nt, configsync.RepoSyncKind, repoSync5Key, configsync.SourceFormatUnstructured) - - nt.SyncSources[rootSync2ID] = &syncsource.GitSyncSource{Repository: rootSync2GitRepo} - nt.SyncSources[rootSync3ID] = &syncsource.GitSyncSource{Repository: rootSync3GitRepo} - nt.SyncSources[repoSync2ID] = &syncsource.GitSyncSource{Repository: repoSync2GitRepo} - nt.SyncSources[repoSync3ID] = &syncsource.GitSyncSource{Repository: repoSync3GitRepo} - nt.SyncSources[repoSync4ID] = &syncsource.GitSyncSource{Repository: repoSync4GitRepo} - nt.SyncSources[repoSync5ID] = &syncsource.GitSyncSource{Repository: repoSync5GitRepo} + rootSync2GitRepo := nt.SyncSourceGitRepository(rootSync2ID) + repoSync2GitRepo := nt.SyncSourceGitRepository(repoSync2ID) nrb2 := nomostest.RepoSyncRoleBinding(repoSync2Key) nrb3 := nomostest.RepoSyncRoleBinding(repoSync3Key) @@ -159,49 +140,40 @@ func TestMultiSyncs_Unstructured_MixedControl(t *testing.T) { nt.T.Logf("Add RootSync %s to the repository of RootSync %s", rootSync2ID.Name, configsync.RootSyncName) rs2 := nomostest.RootSyncObjectV1Alpha1FromRootRepo(nt, rootSync2ID.Name) - rs2ConfigFile := fmt.Sprintf("acme/rootsyncs/%s.yaml", rootSync2ID.Name) - nt.Must(rootSync0GitRepo.Add(rs2ConfigFile, rs2)) + nt.Must(rootSync0GitRepo.Add(fmt.Sprintf("acme/rootsyncs/%s.yaml", rootSync2ID.Name), rs2)) nt.Must(rootSync0GitRepo.CommitAndPush("Adding RootSync: " + rootSync2ID.Name)) // Wait for all RootSyncs and RepoSyncs to be synced, including the new rootSync2. - if err := nt.WatchForAllSyncs(nomostest.SkipRootRepos(rootSync3ID.Name), nomostest.SkipNonRootRepos(repoSync2Key, repoSync3Key, repoSync4Key, repoSync5Key)); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.SkipRootRepos(rootSync3ID.Name), + nomostest.SkipNonRootRepos(repoSync2Key, repoSync3Key, repoSync4Key, repoSync5Key))) nt.T.Logf("Add RootSync %s to the repository of RootSync %s", rootSync3ID.Name, rootSync2ID.Name) rs3 := nomostest.RootSyncObjectV1Alpha1FromRootRepo(nt, rootSync3ID.Name) - rs3ConfigFile := fmt.Sprintf("acme/rootsyncs/%s.yaml", rootSync3ID.Name) - nt.Must(rootSync2GitRepo.Add(rs3ConfigFile, rs3)) + nt.Must(rootSync2GitRepo.Add(fmt.Sprintf("acme/rootsyncs/%s.yaml", rootSync3ID.Name), rs3)) nt.Must(rootSync2GitRepo.CommitAndPush("Adding RootSync: " + rootSync3ID.Name)) // Wait for all RootSyncs and RepoSyncs to be synced, including the new rootSync3. - if err := nt.WatchForAllSyncs(nomostest.SkipNonRootRepos(repoSync2Key, repoSync3Key, repoSync4Key, repoSync5Key)); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.SkipNonRootRepos(repoSync2Key, repoSync3Key, repoSync4Key, repoSync5Key))) nt.T.Logf("Create RepoSync %s", repoSync2Key) nrs2 := nomostest.RepoSyncObjectV1Alpha1FromNonRootRepo(nt, repoSync2Key) - if err := nt.KubeClient.Create(nrs2); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.KubeClient.Create(nrs2)) // RoleBinding (nrb2) managed by RootSync root-sync, because the namespace // tenant does not have permission to manage RBAC. // Wait for all RootSyncs and RepoSyncs to be synced, including the new RepoSync nr2. - if err := nt.WatchForAllSyncs(nomostest.SkipNonRootRepos(repoSync3Key, repoSync4Key, repoSync5Key)); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.SkipNonRootRepos(repoSync3Key, repoSync4Key, repoSync5Key))) nt.T.Logf("Add RepoSync %s to RootSync %s", repoSync3Key, configsync.RootSyncName) nrs3 := nomostest.RepoSyncObjectV1Alpha1FromNonRootRepo(nt, repoSync3Key) // Ensure the RoleBinding is deleted after the RepoSync - if err := nomostest.SetDependencies(nrs3, nrb3); err != nil { - nt.T.Fatal(err) - } + nt.Must(nomostest.SetDependencies(nrs3, nrb3)) nt.Must(rootSync0GitRepo.Add(fmt.Sprintf("acme/reposyncs/%s.yaml", repoSync3Key.Name), nrs3)) nt.Must(rootSync0GitRepo.Add(fmt.Sprintf("acme/namespaces/%s/rb-%s.yaml", testNs, repoSync3Key.Name), nrb3)) nt.Must(rootSync0GitRepo.CommitAndPush("Adding RepoSync: " + repoSync3Key.String())) // Wait for all RootSyncs and RepoSyncs to be synced, including the new RepoSync nr3. - if err := nt.WatchForAllSyncs(nomostest.SkipNonRootRepos(repoSync4Key, repoSync5Key)); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.SkipNonRootRepos(repoSync4Key, repoSync5Key))) nt.T.Logf("Add RepoSync %s to RepoSync %s", repoSync4Key, repoSync2Key) nrs4 := nomostest.RepoSyncObjectV1Alpha1FromNonRootRepo(nt, repoSync4Key) @@ -210,23 +182,18 @@ func TestMultiSyncs_Unstructured_MixedControl(t *testing.T) { // does not have permission to manage RBAC. nt.Must(repoSync2GitRepo.CommitAndPush("Adding RepoSync: " + repoSync4Key.String())) // Wait for all RootSyncs and RepoSyncs to be synced, including the new RepoSync nr4. - if err := nt.WatchForAllSyncs(nomostest.SkipNonRootRepos(repoSync5Key)); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.SkipNonRootRepos(repoSync5Key))) nt.T.Logf("Add RepoSync %s to RootSync %s", repoSync5Key, rootSync1ID.Name) nrs5 := nomostest.RepoSyncObjectV1Beta1FromNonRootRepo(nt, repoSync5Key) // Ensure the RoleBinding is deleted after the RepoSync - if err := nomostest.SetDependencies(nrs5, nrb5); err != nil { - nt.T.Fatal(err) - } + nt.Must(nomostest.SetDependencies(nrs5, nrb5)) nt.Must(rootSync1GitRepo.Add(fmt.Sprintf("acme/reposyncs/%s.yaml", repoSync5Key.Name), nrs5)) nt.Must(rootSync1GitRepo.Add(fmt.Sprintf("acme/namespaces/%s/rb-%s.yaml", testNs, repoSync5Key.Name), nrb5)) nt.Must(rootSync1GitRepo.CommitAndPush("Adding RepoSync: " + repoSync5Key.String())) // Wait for all RootSyncs and RepoSyncs to be synced, including the new RepoSync nr5. - if err := nt.WatchForAllSyncs(); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) nt.T.Logf("Validate reconciler Deployment labels") validateReconcilerResource(nt, kinds.Deployment(), map[string]string{"app": "reconciler"}, 10) @@ -237,9 +204,8 @@ func TestMultiSyncs_Unstructured_MixedControl(t *testing.T) { validateReconcilerResource(nt, kinds.Deployment(), map[string]string{metadata.SyncNameLabel: repoSync1ID.Name}, 2) // Deployments may still be reconciling, wait before checking Pods - if err := waitForResourcesCurrent(nt, kinds.Deployment(), map[string]string{"app": "reconciler"}, 10); err != nil { - nt.T.Fatal(err) - } + nt.Must(waitForResourcesCurrent(nt, kinds.Deployment(), map[string]string{"app": "reconciler"}, 10)) + validateReconcilerResource(nt, kinds.Pod(), map[string]string{"app": "reconciler"}, 10) validateReconcilerResource(nt, kinds.Pod(), map[string]string{metadata.SyncNamespaceLabel: configsync.ControllerNamespace}, 4) validateReconcilerResource(nt, kinds.Pod(), map[string]string{metadata.SyncNamespaceLabel: testNs}, 5) @@ -265,6 +231,18 @@ func TestMultiSyncs_Unstructured_MixedControl(t *testing.T) { // TODO: validate sync-generation label } +// resetExpectedGitSync creates, updates, or replaces the SyncSource for the +// specified RootSync or RepoSync with a new or reset Git repository. +func resetExpectedGitSync(nt *nomostest.NT, syncID core.ID) { + if nt.GitProvider.Type() == e2e.Local { + nomostest.InitGitRepos(nt, syncID.ObjectKey) + } + syncPath := gitproviders.DefaultSyncDir + sourceFormat := configsync.SourceFormatUnstructured + repo := nomostest.ResetRepository(nt, syncID.Kind, syncID.ObjectKey, sourceFormat) + nomostest.SetExpectedGitSource(nt, syncID, repo, syncPath, sourceFormat) +} + func validateReconcilerResource(nt *nomostest.NT, gvk schema.GroupVersionKind, labels map[string]string, expectedCount int) { list := kinds.NewUnstructuredListForItemGVK(gvk) if err := nt.KubeClient.List(list, client.MatchingLabels(labels)); err != nil { @@ -871,7 +849,7 @@ func TestControllerValidationErrors(t *testing.T) { nt.T.Logf("Validate RepoSync is not allowed in the config-management-system namespace") nnControllerNamespace := nomostest.RepoSyncNN(configsync.ControllerNamespace, configsync.RepoSyncName) - rs := nomostest.RepoSyncObjectV1Beta1(nnControllerNamespace, "", configsync.SourceFormatUnstructured) + rs := nomostest.RepoSyncObjectV1Beta1Git(nnControllerNamespace, "", configsync.SourceFormatUnstructured) if err := nt.KubeClient.Create(rs); err != nil { nt.T.Fatal(err) } @@ -887,7 +865,7 @@ func TestControllerValidationErrors(t *testing.T) { } veryLongName := string(longBytes) nnTooLong := nomostest.RepoSyncNN(testNs, veryLongName) - rs = nomostest.RepoSyncObjectV1Beta1(nnTooLong, "https://github.com/test/test", configsync.SourceFormatUnstructured) + rs = nomostest.RepoSyncObjectV1Beta1Git(nnTooLong, "https://github.com/test/test", configsync.SourceFormatUnstructured) if err := nt.KubeClient.Create(rs); err != nil { nt.T.Fatal(err) } @@ -902,7 +880,7 @@ func TestControllerValidationErrors(t *testing.T) { nt.T.Logf("Validate an invalid config with a long RepoSync Secret name") nnInvalidSecretRef := nomostest.RepoSyncNN(testNs, "repo-test") - rsInvalidSecretRef := nomostest.RepoSyncObjectV1Beta1(nnInvalidSecretRef, "https://github.com/test/test", configsync.SourceFormatUnstructured) + rsInvalidSecretRef := nomostest.RepoSyncObjectV1Beta1Git(nnInvalidSecretRef, "https://github.com/test/test", configsync.SourceFormatUnstructured) rsInvalidSecretRef.Spec.Auth = configsync.AuthSSH rsInvalidSecretRef.Spec.SecretRef = &v1beta1.SecretReference{Name: veryLongName} if err := nt.KubeClient.Create(rsInvalidSecretRef); err != nil { diff --git a/e2e/testcases/oci_sync_test.go b/e2e/testcases/oci_sync_test.go index 4e113d0e4..b19bcff34 100644 --- a/e2e/testcases/oci_sync_test.go +++ b/e2e/testcases/oci_sync_test.go @@ -53,19 +53,10 @@ import ( // references resources for tenant-a, tenant-b, and tenant-c. // Each tenant includes a NetworkPolicy, a Role and a RoleBinding. -const ( - // publicGCRImage pulls the public OCI image by the default `latest` tag - // The test-infra GCR is public. - publicGCRImage = nomostesting.TestInfraContainerRegistry + "/kustomize-components" - - // publicARImage pulls the public OCI image by the default `latest` tag - publicARImage = nomostesting.ConfigSyncTestPublicRegistry + "/kustomize-components" -) - // privateGCRImage pulls the private OCI image by tag // The test environment GCR is assumed to be private. func privateGCRImage(sourcePackage string) string { - return fmt.Sprintf("%s/%s/config-sync-test/%s:v1", nomostesting.GCRHost, *e2e.GCPProject, sourcePackage) + return fmt.Sprintf("%s/%s/config-sync-test/%s:v1", nomostesting.GoogleContainerRegistryHost, *e2e.GCPProject, sourcePackage) } func gsaARReaderEmail() string { @@ -79,49 +70,60 @@ func gsaGCRReaderEmail() string { // TestPublicOCI can run on both Kind and GKE clusters. // It tests Config Sync can pull from public OCI images without any authentication. func TestPublicOCI(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.SyncSource, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured)) + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured)) + var err error - rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) - nt.T.Log("Update RootSync to sync from a public OCI image in AR") - nt.MustMergePatch(rs, fmt.Sprintf(`{"spec": {"sourceType": "%s", "oci": {"image": "%s", "auth": "none"}, "git": null}}`, - configsync.OciSource, publicARImage)) - err := nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(imageDigestFuncByName(publicARImage)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: ".", - })) + publicARImageID := registryproviders.OCIImageID{ + Registry: nomostesting.ArtifactRegistryHost, + Repository: nomostesting.ConfigSyncTestPublicRepositoryPath, + Name: "kustomize-components", + } + publicARImageID.Digest, err = getImageDigest(nt, publicARImageID.Address()) if err != nil { nt.T.Fatal(err) } + + nt.T.Log("Update RootSync to sync from a public OCI image in AR") + rs := nt.RootSyncObjectOCI(rootSyncID.Name, publicARImageID.WithoutDigest(), "", publicARImageID.Digest) + rs.Spec.Oci.Auth = configsync.AuthNone + nt.Must(nt.KubeClient.Apply(rs)) + nt.Must(nt.WatchForAllSyncs()) kustomizecomponents.ValidateAllTenants(nt, string(declared.RootScope), "base", "tenant-a", "tenant-b", "tenant-c") - tenant := "tenant-a" - nt.T.Logf("Update RootSync to sync %s from a public OCI image in GCR", tenant) - nt.MustMergePatch(rs, fmt.Sprintf(`{"spec": {"oci": {"image": "%s", "dir": "%s"}}}`, publicGCRImage, tenant)) - err = nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(imageDigestFuncByName(publicGCRImage)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: "tenant-a", - })) + publicGCRImageID := registryproviders.OCIImageID{ + Registry: nomostesting.GoogleContainerRegistryHost, + Repository: nomostesting.TestInfraContainerRepositoryPath, + Name: "kustomize-components", + } + publicGCRImageID.Digest, err = getImageDigest(nt, publicGCRImageID.Address()) if err != nil { nt.T.Fatal(err) } + + tenant := "tenant-a" + nt.T.Logf("Update RootSync to sync %s from a public OCI image in GCR", tenant) + rs = nt.RootSyncObjectOCI(rootSyncID.Name, publicGCRImageID.WithoutDigest(), tenant, publicGCRImageID.Digest) + rs.Spec.Oci.Auth = configsync.AuthNone + nt.Must(nt.KubeClient.Apply(rs)) + nt.Must(nt.WatchForAllSyncs()) kustomizecomponents.ValidateAllTenants(nt, string(declared.RootScope), "../base", tenant) } func TestSwitchFromGitToOciCentralized(t *testing.T) { namespace := testNs + rootSyncID := nomostest.DefaultRootSyncID repoSyncID := core.RepoSyncID(configsync.RepoSyncName, namespace) nt := nomostest.New(t, nomostesting.SyncSource, ntopts.RequireOCIProvider, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ntopts.SyncWithGitSource(repoSyncID), // bookinfo image contains RoleBinding // bookinfo repo contains ServiceAccount ntopts.RepoSyncPermissions(policy.RBACAdmin(), policy.CoreAdmin()), ) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) repoSyncKey := repoSyncID.ObjectKey repoSyncGitRepo := nt.SyncSourceGitRepository(repoSyncID) @@ -154,27 +156,16 @@ func TestSwitchFromGitToOciCentralized(t *testing.T) { // Switch from Git to OCI nt.T.Log("Update the RepoSync object to sync from OCI") - repoSyncOCI := nt.RepoSyncObjectOCI(repoSyncKey, image) + repoSyncOCI := nt.RepoSyncObjectOCI(repoSyncKey, image.OCIImageID().WithoutDigest(), "", image.Digest) nt.Must(rootSyncGitRepo.Add(repoSyncPath, repoSyncOCI)) nt.Must(rootSyncGitRepo.CommitAndPush("configure RepoSync to sync from OCI in the root repository")) - if err := nt.WatchForAllSyncs( - nomostest.WithRepoSha1Func(imageDigestFuncByDigest(image.Digest)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - repoSyncKey: ".", - })); err != nil { - nt.T.Fatal(err) - } - if err := nt.Validate(configsync.RepoSyncName, namespace, &v1beta1.RepoSync{}, isSourceType(configsync.OciSource)); err != nil { - nt.T.Error(err) - } - if err := nt.Validate(bookinfoRole.Name, namespace, &rbacv1.Role{}, - testpredicates.HasAnnotation(metadata.ResourceManagerKey, namespace)); err != nil { - nt.T.Error(err) - } - if err := nt.ValidateNotFound(bookinfoSA.Name, namespace, &corev1.ServiceAccount{}); err != nil { - nt.T.Error(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.WithRepoSha1Func(imageDigestFuncByDigest(image.Digest)))) + nt.Must(nt.Validate(configsync.RepoSyncName, namespace, &v1beta1.RepoSync{}, isSourceType(configsync.OciSource))) + nt.Must(nt.Validate(bookinfoRole.Name, namespace, &rbacv1.Role{}, + testpredicates.HasAnnotation(metadata.ResourceManagerKey, namespace))) + nt.Must(nt.ValidateNotFound(bookinfoSA.Name, namespace, &corev1.ServiceAccount{})) } func TestSwitchFromGitToOciDelegated(t *testing.T) { @@ -218,27 +209,17 @@ func TestSwitchFromGitToOciDelegated(t *testing.T) { } // Switch from Git to OCI - repoSyncOCI := nt.RepoSyncObjectOCI(repoSyncKey, image) + repoSyncOCI := nt.RepoSyncObjectOCI(repoSyncKey, image.OCIImageID().WithoutDigest(), "", image.Digest) nt.T.Log("Manually update the RepoSync object to sync from OCI") - if err := nt.KubeClient.Apply(repoSyncOCI); err != nil { - nt.T.Fatal(err) - } - if err := nt.Validate(configsync.RepoSyncName, namespace, &v1beta1.RepoSync{}, isSourceType(configsync.OciSource)); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.KubeClient.Apply(repoSyncOCI)) + + nt.Must(nt.Validate(configsync.RepoSyncName, namespace, &v1beta1.RepoSync{}, isSourceType(configsync.OciSource))) nt.T.Log("Verify the namespace objects are synced") - err = nt.WatchForSync(kinds.RepoSyncV1Beta1(), configsync.RepoSyncName, namespace, - imageDigestFuncByDigest(image.Digest), nomostest.RepoSyncHasStatusSyncCommit, nil) - if err != nil { - nt.T.Fatal(err) - } - if err := nt.Validate(bookinfoRole.Name, namespace, &rbacv1.Role{}, - testpredicates.HasAnnotation(metadata.ResourceManagerKey, namespace)); err != nil { - nt.T.Fatal(err) - } - if err := nt.ValidateNotFound(bookinfoSA.Name, namespace, &corev1.ServiceAccount{}); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForSync(kinds.RepoSyncV1Beta1(), configsync.RepoSyncName, namespace, + imageDigestFuncByDigest(image.Digest), nomostest.RepoSyncHasStatusSyncCommit, nil)) + nt.Must(nt.Validate(bookinfoRole.Name, namespace, &rbacv1.Role{}, + testpredicates.HasAnnotation(metadata.ResourceManagerKey, namespace))) + nt.Must(nt.ValidateNotFound(bookinfoSA.Name, namespace, &corev1.ServiceAccount{})) // Invalid cases rs := k8sobjects.RepoSyncObjectV1Beta1(repoSyncID.Namespace, repoSyncID.Name) @@ -269,59 +250,48 @@ func isSourceType(sourceType configsync.SourceType) testpredicates.Predicate { } func TestOciSyncWithDigest(t *testing.T) { - rootSyncNN := nomostest.RootSyncNN(configsync.RootSyncName) + rootSyncID := nomostest.DefaultRootSyncID + rootSyncKey := rootSyncID.ObjectKey nt := nomostest.New(t, nomostesting.SyncSource, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ntopts.RequireOCIProvider, ) var err error // OCI image will only contain the bookinfo-admin role bookinfoRole := k8sobjects.RoleObject(core.Name("bookinfo-admin")) - image, err := nt.BuildAndPushOCIImage(rootSyncNN, registryproviders.ImageInputObjects(nt.Scheme, bookinfoRole)) + image, err := nt.BuildAndPushOCIImage(rootSyncKey, registryproviders.ImageInputObjects(nt.Scheme, bookinfoRole)) if err != nil { nt.T.Fatal(err) } nt.T.Log("Create RootSync with OCI image") - rootSyncOCI := nt.RootSyncObjectOCI(rootSyncNN.Name, image) + rootSyncOCI := nt.RootSyncObjectOCI(rootSyncKey.Name, image.OCIImageID().WithoutDigest(), "", image.Digest) rootSyncOCI.Spec.Oci.Period = metav1.Duration{Duration: 5 * time.Second} nt.Must(nt.KubeClient.Apply(rootSyncOCI)) nt.Must(nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(imageDigestFuncByDigest(image.Digest)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - rootSyncNN: ".", - }), - )) + nomostest.WithRootSha1Func(imageDigestFuncByDigest(image.Digest)))) // Delete the remote image nt.T.Log("Delete image from remote registry") nt.Must(image.Delete()) // RootSync should fail because image can no longer be pulled nt.T.Log("Wait for RootSync to have source error") - nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), configsync.RootSyncName, configsync.ControllerNamespace, + nt.Must(nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), rootSyncID.Name, configsync.ControllerNamespace, []testpredicates.Predicate{testpredicates.RootSyncHasSourceError(status.SourceErrorCode, "failed to pull image")})) - nt.WaitForRootSyncSourceError(configsync.RootSyncName, status.SourceErrorCode, "failed to pull image") + nt.WaitForRootSyncSourceError(rootSyncID.Name, status.SourceErrorCode, "failed to pull image") // Specify image with digest - image, err = nt.BuildAndPushOCIImage(rootSyncNN, registryproviders.ImageInputObjects(nt.Scheme, bookinfoRole)) - if err != nil { - nt.T.Fatal(err) - } - imageWithDigest, err := image.RemoteAddressWithDigest() + image, err = nt.BuildAndPushOCIImage(rootSyncKey, registryproviders.ImageInputObjects(nt.Scheme, bookinfoRole)) if err != nil { nt.T.Fatal(err) } nt.T.Log("Apply RootSync with fully specified OCI image digest") - rootSyncOCI = nt.RootSyncObjectOCI(rootSyncNN.Name, image) - rootSyncOCI.Spec.Oci.Image = imageWithDigest + // TODO: Test for just tag, tag+digest, and just digest independently + rootSyncOCI = nt.RootSyncObjectOCI(rootSyncKey.Name, image.OCIImageID().WithoutTag(), "", image.Digest) rootSyncOCI.Spec.Oci.Period = metav1.Duration{Duration: 5 * time.Second} nt.Must(nt.KubeClient.Apply(rootSyncOCI)) nt.Must(nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(imageDigestFuncByDigest(image.Digest)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - rootSyncNN: ".", - }), - )) + nomostest.WithRootSha1Func(imageDigestFuncByDigest(image.Digest)))) nt.T.Log("Check for log message in oci-sync container") - out, err := nt.Shell.Kubectl("logs", fmt.Sprintf("deployment/%s", core.RootReconcilerName(rootSyncNN.Name)), + out, err := nt.Shell.Kubectl("logs", fmt.Sprintf("deployment/%s", core.RootReconcilerName(rootSyncKey.Name)), "-n", configsync.ControllerNamespace, "-c", reconcilermanager.OciSync) if err != nil { nt.T.Fatal(err) @@ -334,7 +304,7 @@ func TestOciSyncWithDigest(t *testing.T) { nt.Must(image.Delete()) nt.T.Log("Make sure RootSync remains healthy for 30 seconds") // RootSync should remain healthy as long as the Pod isn't restarted (could be flaky) - err = nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), configsync.RootSyncName, configsync.ControllerNamespace, + err = nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), rootSyncID.Name, configsync.ControllerNamespace, []testpredicates.Predicate{testpredicates.RootSyncHasSourceError(status.SourceErrorCode, "failed to pull image")}, testwatcher.WatchTimeout(30*time.Second)) // wait for source error to occur (it shouldn't) if err == nil { @@ -394,6 +364,7 @@ func testDigestUpdate(nt *nomostest.NT, image string) { // This allows reading the image digest without pulling the whole image. // Requires a sha256 image digest. func getImageDigest(nt *nomostest.NT, imageName string) (string, error) { + nt.T.Logf("Pulling image to get digest: %s", imageName) args := []string{ "gcloud", "container", "images", "describe", imageName, diff --git a/e2e/testcases/otel_collector_test.go b/e2e/testcases/otel_collector_test.go index a09fcb067..9fab90648 100644 --- a/e2e/testcases/otel_collector_test.go +++ b/e2e/testcases/otel_collector_test.go @@ -28,7 +28,6 @@ import ( "google.golang.org/api/iterator" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" "kpt.dev/configsync/e2e" "kpt.dev/configsync/e2e/nomostest" "kpt.dev/configsync/e2e/nomostest/iam" @@ -111,12 +110,13 @@ var GCMMetricTypes = []string{ // - e2e-test-metric-writer GSA with roles/monitoring.metricWriter IAM // - roles/iam.workloadIdentityUser on config-management-monitoring/default for e2e-test-metric-writer func TestOtelCollectorDeployment(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.Reconciliation1, ntopts.RequireGKE(t), - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) nt.T.Cleanup(func() { if t.Failed() { nt.PodLogs("config-management-monitoring", csmetrics.OtelCollectorName, "", false) @@ -190,12 +190,8 @@ func TestOtelCollectorDeployment(t *testing.T) { nt.T.Log("Update RootSync to sync from the kustomize-components directory") rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) nt.MustMergePatch(rs, `{"spec": {"git": {"dir": "kustomize-components"}}}`) - syncDirMap := map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: "kustomize-components", - } - if err := nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(syncDirMap)); err != nil { - nt.T.Fatal(err) - } + nomostest.SetExpectedSyncPath(nt, rootSyncID, "kustomize-components") + nt.Must(nt.WatchForAllSyncs()) // retry for 2 minutes until metric is accessible from GCM _, err = retry.Retry(120*time.Second, func() error { diff --git a/e2e/testcases/override_log_level_test.go b/e2e/testcases/override_log_level_test.go index 2aa605632..1bc7bd1f7 100644 --- a/e2e/testcases/override_log_level_test.go +++ b/e2e/testcases/override_log_level_test.go @@ -18,7 +18,6 @@ import ( "strings" "testing" - "k8s.io/apimachinery/pkg/types" "kpt.dev/configsync/e2e/nomostest" "kpt.dev/configsync/e2e/nomostest/ntopts" nomostesting "kpt.dev/configsync/e2e/nomostest/testing" @@ -33,12 +32,12 @@ import ( ) func TestOverrideRootSyncLogLevel(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.OverrideAPI, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured)) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured)) + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) - rootSyncName := nomostest.RootSyncNN(configsync.RootSyncName) - rootReconcilerName := core.RootReconcilerObjectKey(rootSyncName.Name) + rootReconcilerName := core.RootReconcilerObjectKey(rootSyncID.Name) rootSyncV1 := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) // add kustomize to enable hydration controller container in root-sync @@ -48,13 +47,9 @@ func TestOverrideRootSyncLogLevel(t *testing.T) { nt.T.Log("Update RootSync to sync from the kustomize-components directory") nt.MustMergePatch(rootSyncV1, `{"spec": {"git": {"dir": "kustomize-components"}}}`) - syncDirMap := map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: "kustomize-components", - } + nomostest.SetExpectedSyncPath(nt, rootSyncID, "kustomize-components") - if err := nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(syncDirMap)); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) // validate initial container log level value err := nt.Watcher.WatchObject(kinds.Deployment(), @@ -71,10 +66,7 @@ func TestOverrideRootSyncLogLevel(t *testing.T) { if err != nil { nt.T.Fatal(err) } - - if err := nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(syncDirMap)); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) // apply override to one container and validate the others are unaffected nt.MustMergePatch(rootSyncV1, `{"spec": {"override": {"logLevels": [{"containerName": "reconciler", "logLevel": 3}]}}}`) @@ -90,9 +82,7 @@ func TestOverrideRootSyncLogLevel(t *testing.T) { if err != nil { nt.T.Fatal(err) } - if err := nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(syncDirMap)); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) // apply override to all containers and validate nt.MustMergePatch(rootSyncV1, `{"spec": {"override": {"logLevels": [{"containerName": "reconciler", "logLevel": 5}, {"containerName": "git-sync", "logLevel": 7}, {"containerName": "otel-agent", "logLevel": 0}, {"containerName": "hydration-controller", "logLevel": 9}]}}}`) @@ -124,10 +114,7 @@ func TestOverrideRootSyncLogLevel(t *testing.T) { if err != nil { nt.T.Fatal(err) } - - if err := nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(syncDirMap)); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) // try invalid log level value maxError := "logLevel in body should be less than or equal to 10" diff --git a/e2e/testcases/policy_dir_test.go b/e2e/testcases/policy_dir_test.go index cda5ef589..1c630bbf5 100755 --- a/e2e/testcases/policy_dir_test.go +++ b/e2e/testcases/policy_dir_test.go @@ -17,25 +17,26 @@ package e2e import ( "testing" - "k8s.io/apimachinery/pkg/types" "kpt.dev/configsync/e2e/nomostest" nomostesting "kpt.dev/configsync/e2e/nomostest/testing" "kpt.dev/configsync/pkg/api/configsync" "kpt.dev/configsync/pkg/importer/analyzer/validation/system" + "kpt.dev/configsync/pkg/reconcilermanager/controllers" "kpt.dev/configsync/pkg/status" ) func TestMissingRepoErrorWithHierarchicalFormat(t *testing.T) { nt := nomostest.New(t, nomostesting.SyncSource) - nomostest.SetPolicyDir(nt, configsync.RootSyncName, "") + nomostest.SetRootSyncGitDir(nt, configsync.RootSyncName, "") nt.WaitForRootSyncSourceError(configsync.RootSyncName, system.MissingRepoErrorCode, "") } func TestPolicyDirUnset(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.SyncSource) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) // There are 6 cluster-scoped objects under `../../examples/acme/cluster`. // // Copying the whole `../../examples/acme/cluster` dir would cause the Config Sync mono-repo mode CI job to fail, @@ -50,30 +51,24 @@ func TestPolicyDirUnset(t *testing.T) { nt.Must(rootSyncGitRepo.Copy("../../examples/acme/namespaces", ".")) nt.Must(rootSyncGitRepo.Copy("../../examples/acme/system", ".")) nt.Must(rootSyncGitRepo.CommitAndPush("Initialize the root directory")) - if err := nt.WatchForAllSyncs(); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) - nomostest.SetPolicyDir(nt, configsync.RootSyncName, "") - err := nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{nomostest.DefaultRootRepoNamespacedName: "."})) - if err != nil { - nt.T.Fatal(err) - } + nomostest.SetRootSyncGitDir(nt, rootSyncID.Name, "") + nomostest.SetExpectedSyncPath(nt, rootSyncID, controllers.DefaultSyncDir) + nt.Must(nt.WatchForAllSyncs()) } func TestInvalidPolicyDir(t *testing.T) { nt := nomostest.New(t, nomostesting.SyncSource) nt.T.Log("Break the policydir in the repo") - nomostest.SetPolicyDir(nt, configsync.RootSyncName, "some-nonexistent-policydir") + nomostest.SetRootSyncGitDir(nt, configsync.RootSyncName, "some-nonexistent-policydir") nt.T.Log("Expect an error to be present in status.source.errors") nt.WaitForRootSyncSourceError(configsync.RootSyncName, status.SourceErrorCode, "") nt.T.Log("Fix the policydir in the repo") - nomostest.SetPolicyDir(nt, configsync.RootSyncName, "acme") + nomostest.SetRootSyncGitDir(nt, configsync.RootSyncName, "acme") nt.T.Log("Expect repo to recover from the error in source message") - if err := nt.WatchForAllSyncs(); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) } diff --git a/e2e/testcases/private_cert_secret_test.go b/e2e/testcases/private_cert_secret_test.go index 7a4afb1eb..ea167992b 100644 --- a/e2e/testcases/private_cert_secret_test.go +++ b/e2e/testcases/private_cert_secret_test.go @@ -24,7 +24,6 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "kpt.dev/configsync/e2e/nomostest" "kpt.dev/configsync/e2e/nomostest/ntopts" "kpt.dev/configsync/e2e/nomostest/policy" @@ -404,37 +403,31 @@ func TestCACertSecretWatch(t *testing.T) { // TestOCICACertSecretRefRootRepo can run only run on KinD clusters. // It tests RootSyncs can pull from OCI images using a CA certificate. func TestOCICACertSecretRefRootRepo(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.SyncSource, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ntopts.RequireLocalOCIProvider) caCertSecret := nomostest.PublicCertSecretName(nomostest.RegistrySyncSource) - rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) - image, err := nt.BuildAndPushOCIImage(nomostest.RootSyncNN(configsync.RootSyncName), registryproviders.ImageInputObjects(nt.Scheme, k8sobjects.NamespaceObject("foo-ns"))) + image, err := nt.BuildAndPushOCIImage(rootSyncID.ObjectKey, + registryproviders.ImageInputObjects(nt.Scheme, k8sobjects.NamespaceObject("foo-ns"))) if err != nil { nt.T.Fatal(err) } - imageURL, err := image.RemoteAddressWithTag() - if err != nil { - nt.T.Fatalf("OCIImage.RemoteAddressWithTag: %v", err) - } nt.T.Log("Set the RootSync to sync the OCI image without providing a CA cert") - nt.MustMergePatch(rs, fmt.Sprintf(`{"spec": {"sourceType": "%s", "oci": {"image": "%s", "auth": "none"}, "git": null}}`, - configsync.OciSource, imageURL)) - nt.WaitForRootSyncSourceError(configsync.RootSyncName, status.SourceErrorCode, "tls: failed to verify certificate: x509: certificate signed by unknown authority") + rs := nt.RootSyncObjectOCI(rootSyncID.Name, image.OCIImageID().WithoutDigest(), "", image.Digest) + rs.Spec.Oci.Auth = configsync.AuthNone + rs.Spec.Oci.CACertSecretRef = nil + nt.Must(nt.KubeClient.Apply(rs)) + + nt.WaitForRootSyncSourceError(rootSyncID.Name, status.SourceErrorCode, "tls: failed to verify certificate: x509: certificate signed by unknown authority") nt.T.Log("Add caCertSecretRef to RootSync") nt.MustMergePatch(rs, caCertSecretPatch(configsync.OciSource, caCertSecret)) - err = nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(imageDigestFuncByDigest(image.Digest)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: ".", - })) - if err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.WithRootSha1Func(imageDigestFuncByDigest(image.Digest)))) } // TestOCICACertSecretRefNamespaceRepo can run only run on KinD clusters. @@ -452,7 +445,6 @@ func TestOCICACertSecretRefNamespaceRepo(t *testing.T) { caCertSecret := nomostest.PublicCertSecretName(nomostest.RegistrySyncSource) - rs := nomostest.RepoSyncObjectV1Beta1FromNonRootRepo(nt, repoSyncKey) upsertedSecret := controllers.ReconcilerResourceName(repoSyncReconcilerName, caCertSecret) cm := k8sobjects.ConfigMapObject(core.Name("foo-cm"), core.Namespace(repoSyncID.Namespace)) @@ -460,17 +452,12 @@ func TestOCICACertSecretRefNamespaceRepo(t *testing.T) { if err != nil { nt.T.Fatal(err) } - imageURL, err := image.RemoteAddressWithTag() - if err != nil { - nt.T.Fatalf("OCIImage.RemoteAddressWithTag: %v", err) - } nt.T.Log("Set the RepoSync to sync the OCI image without providing a CA cert") - rs.Spec.SourceType = configsync.OciSource - rs.Spec.Oci = &v1beta1.Oci{ - Image: imageURL, - Auth: "none", - } + gitSource := nt.SyncSources[repoSyncID] // backup git source expectation + rs := nt.RepoSyncObjectOCI(repoSyncKey, image.OCIImageID().WithoutDigest(), "", image.Digest) + rs.Spec.Oci.Auth = configsync.AuthNone + rs.Spec.Oci.CACertSecretRef = nil nt.Must(rootSyncGitRepo.Add(nomostest.StructuredNSPath(repoSyncID.Namespace, repoSyncID.Name), rs)) nt.Must(rootSyncGitRepo.CommitAndPush("Set the RepoSync to use OCI without providing CA cert")) @@ -480,81 +467,56 @@ func TestOCICACertSecretRefNamespaceRepo(t *testing.T) { rs.Spec.Oci.CACertSecretRef = &v1beta1.SecretReference{Name: caCertSecret} nt.Must(rootSyncGitRepo.Add(nomostest.StructuredNSPath(repoSyncID.Namespace, repoSyncID.Name), rs)) nt.Must(rootSyncGitRepo.CommitAndPush("Set the CA cert for the RepoSync")) - err = nt.WatchForAllSyncs( - nomostest.WithRepoSha1Func(imageDigestFuncByDigest(image.Digest)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - repoSyncKey: ".", - })) - if err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.WithRepoSha1Func(imageDigestFuncByDigest(image.Digest)))) nt.T.Log("Verify the ConfigMap was created") - if err := nt.Validate(cm.Name, cm.Namespace, &corev1.ConfigMap{}); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.Validate(cm.Name, cm.Namespace, &corev1.ConfigMap{})) nt.T.Log("Verify the upserted Secret was created") - if err := nt.Validate(upsertedSecret, configsync.ControllerNamespace, &corev1.Secret{}); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.Validate(upsertedSecret, configsync.ControllerNamespace, &corev1.Secret{})) nt.T.Log("Set the RepoSync to sync from git") - rs.Spec.SourceType = configsync.GitSource - nt.Must(rootSyncGitRepo.Add( - nomostest.StructuredNSPath(repoSyncID.Namespace, repoSyncID.Name), rs)) + nt.SyncSources[repoSyncID] = gitSource // revert source expectation to git + rs = nomostest.RepoSyncObjectV1Beta1FromNonRootRepo(nt, repoSyncID.ObjectKey) + nt.Must(rootSyncGitRepo.Add(nomostest.StructuredNSPath(repoSyncID.Namespace, repoSyncID.Name), rs)) nt.Must(rootSyncGitRepo.CommitAndPush("Set the RepoSync to sync from Git")) - if err := nt.WatchForAllSyncs(); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) nt.T.Log("Verify the ConfigMap was pruned") - if err := nt.ValidateNotFound(cm.Name, cm.Namespace, &corev1.ConfigMap{}); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.ValidateNotFound(cm.Name, cm.Namespace, &corev1.ConfigMap{})) nt.T.Log("Verify the upserted Secret was garbage collected") - if err := nt.ValidateNotFound(upsertedSecret, configsync.ControllerNamespace, &corev1.Secret{}); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.ValidateNotFound(upsertedSecret, configsync.ControllerNamespace, &corev1.Secret{})) } // TestHelmCACertSecretRefRootRepo can run only run on KinD clusters. // It tests RootSyncs can pull from OCI images using a CA certificate. func TestHelmCACertSecretRefRootRepo(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.SyncSource, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ntopts.RequireLocalHelmProvider) caCertSecret := nomostest.PublicCertSecretName(nomostest.RegistrySyncSource) - rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) - chart, err := nt.BuildAndPushHelmPackage(nomostest.RootSyncNN(configsync.RootSyncName), + chart, err := nt.BuildAndPushHelmPackage(rootSyncID.ObjectKey, registryproviders.HelmChartObjects(nt.Scheme, k8sobjects.NamespaceObject("foo-ns"))) if err != nil { nt.T.Fatal(err) } - chartRepoURL, err := chart.Provider.RepositoryRemoteURL() - if err != nil { - nt.T.Fatalf("HelmProvider.RepositoryRemoteURL: %v", err) - } nt.T.Log("Set the RootSync to sync the Helm package without providing a CA cert") - nt.MustMergePatch(rs, fmt.Sprintf(`{"spec": {"sourceType": "%s", "helm": {"repo": "%s", "chart": "%s", "version": "%s", "auth": "none", "period": "15s"}, "git": null}}`, - configsync.HelmSource, chartRepoURL, chart.Name, chart.Version)) - nt.WaitForRootSyncSourceError(configsync.RootSyncName, status.SourceErrorCode, "tls: failed to verify certificate: x509: certificate signed by unknown authority") + rs := nt.RootSyncObjectHelm(rootSyncID.Name, chart.HelmChartID) + rs.Spec.Helm.Auth = configsync.AuthNone + rs.Spec.Helm.CACertSecretRef = nil + rs.Spec.Helm.Period = metav1.Duration{Duration: 15 * time.Second} + nt.Must(nt.KubeClient.Apply(rs)) + + nt.WaitForRootSyncSourceError(rootSyncID.Name, status.SourceErrorCode, "tls: failed to verify certificate: x509: certificate signed by unknown authority") nt.T.Log("Add caCertSecretRef to RootSync") nt.MustMergePatch(rs, caCertSecretPatch(configsync.HelmSource, caCertSecret)) - err = nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(nomostest.HelmChartVersionShaFn(chart.Version)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: chart.Name, - })) - if err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) nt.T.Log("Verify the Namespace was created") - if err := nt.Validate("foo-ns", "", &corev1.Namespace{}); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.Validate("foo-ns", "", &corev1.Namespace{})) } // TestHelmCACertSecretRefNamespaceRepo can run only run on KinD clusters. @@ -571,7 +533,6 @@ func TestHelmCACertSecretRefNamespaceRepo(t *testing.T) { caCertSecret := nomostest.PublicCertSecretName(nomostest.RegistrySyncSource) repoSyncKey := repoSyncID.ObjectKey - rs := nomostest.RepoSyncObjectV1Beta1FromNonRootRepo(nt, repoSyncKey) upsertedSecret := controllers.ReconcilerResourceName( core.NsReconcilerName(repoSyncKey.Namespace, repoSyncKey.Name), caCertSecret) @@ -580,41 +541,23 @@ func TestHelmCACertSecretRefNamespaceRepo(t *testing.T) { if err != nil { nt.T.Fatal(err) } - chartRepoURL, err := chart.Provider.RepositoryRemoteURL() - if err != nil { - nt.T.Fatalf("HelmProvider.RepositoryRemoteURL: %v", err) - } nt.T.Log("Set the RepoSync to sync the Helm package without providing a CA cert") - rs.Spec.SourceType = configsync.HelmSource - rs.Spec.Helm = &v1beta1.HelmRepoSync{ - HelmBase: v1beta1.HelmBase{ - Repo: chartRepoURL, - Chart: chart.Name, - Version: chart.Version, - Auth: "none", - Period: metav1.Duration{Duration: 15 * time.Second}, - }, - } - nt.Must(rootSyncGitRepo.Add( - nomostest.StructuredNSPath(repoSyncKey.Namespace, repoSyncKey.Name), rs)) + gitSource := nt.SyncSources[repoSyncID] // backup git source expectation + rs := nt.RepoSyncObjectHelm(repoSyncKey, chart.HelmChartID) + rs.Spec.Helm.Auth = configsync.AuthNone + rs.Spec.Helm.CACertSecretRef = nil + rs.Spec.Helm.Period = metav1.Duration{Duration: 15 * time.Second} + nt.Must(rootSyncGitRepo.Add(nomostest.StructuredNSPath(repoSyncKey.Namespace, repoSyncKey.Name), rs)) nt.Must(rootSyncGitRepo.CommitAndPush("Set the RepoSync to use Helm without providing CA cert")) nt.WaitForRepoSyncSourceError(repoSyncKey.Namespace, repoSyncKey.Name, status.SourceErrorCode, "tls: failed to verify certificate: x509: certificate signed by unknown authority") nt.T.Log("Add caCertSecretRef to RepoSync") rs.Spec.Helm.CACertSecretRef = &v1beta1.SecretReference{Name: caCertSecret} - nt.Must(rootSyncGitRepo.Add( - nomostest.StructuredNSPath(repoSyncKey.Namespace, repoSyncKey.Name), rs)) + nt.Must(rootSyncGitRepo.Add(nomostest.StructuredNSPath(repoSyncKey.Namespace, repoSyncKey.Name), rs)) nt.Must(rootSyncGitRepo.CommitAndPush("Set the CA cert for the RepoSync")) - err = nt.WatchForAllSyncs( - nomostest.WithRepoSha1Func(nomostest.HelmChartVersionShaFn(chart.Version)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - repoSyncKey: chart.Name, - })) - if err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) nt.T.Log("Verify the ConfigMap was created") if err := nt.Validate(cm.Name, cm.Namespace, &corev1.ConfigMap{}); err != nil { nt.T.Fatal(err) @@ -625,9 +568,9 @@ func TestHelmCACertSecretRefNamespaceRepo(t *testing.T) { } nt.T.Log("Set the RepoSync to sync from git") - rs.Spec.SourceType = configsync.GitSource - nt.Must(rootSyncGitRepo.Add( - nomostest.StructuredNSPath(repoSyncKey.Namespace, repoSyncKey.Name), rs)) + nt.SyncSources[repoSyncID] = gitSource // revert source expectation to git + rs = nomostest.RepoSyncObjectV1Beta1FromNonRootRepo(nt, repoSyncID.ObjectKey) + nt.Must(rootSyncGitRepo.Add(nomostest.StructuredNSPath(repoSyncKey.Namespace, repoSyncKey.Name), rs)) nt.Must(rootSyncGitRepo.CommitAndPush("Set the RepoSync to sync from Git")) if err := nt.WatchForAllSyncs(); err != nil { diff --git a/e2e/testcases/profiling_test.go b/e2e/testcases/profiling_test.go index 4678d2270..ffa1eab4c 100644 --- a/e2e/testcases/profiling_test.go +++ b/e2e/testcases/profiling_test.go @@ -449,18 +449,18 @@ func watchForSyncedAndReconciled(nt *nomostest.NT, rsRefs []rSyncRef) error { tg.Go(func() error { return nt.WatchForSync(reRefPtr.GroupVersionKind, reRefPtr.Name, reRefPtr.Namespace, reRefPtr.CommitFunc, nomostest.RootSyncHasStatusSyncCommit, - &nomostest.SyncDirPredicatePair{ - Dir: reRefPtr.SyncPath, - Predicate: nomostest.RootSyncHasStatusSyncDirectory, + &nomostest.SyncPathPredicatePair{ + Path: reRefPtr.SyncPath, + Predicate: nomostest.RootSyncHasStatusSyncPath, }) }) case configsync.RepoSyncKind: tg.Go(func() error { return nt.WatchForSync(reRefPtr.GroupVersionKind, reRefPtr.Name, reRefPtr.Namespace, reRefPtr.CommitFunc, nomostest.RepoSyncHasStatusSyncCommit, - &nomostest.SyncDirPredicatePair{ - Dir: reRefPtr.SyncPath, - Predicate: nomostest.RepoSyncHasStatusSyncDirectory, + &nomostest.SyncPathPredicatePair{ + Path: reRefPtr.SyncPath, + Predicate: nomostest.RepoSyncHasStatusSyncPath, }) }) default: diff --git a/e2e/testcases/proxy_test.go b/e2e/testcases/proxy_test.go index e372b8a7e..4bf11de73 100644 --- a/e2e/testcases/proxy_test.go +++ b/e2e/testcases/proxy_test.go @@ -19,13 +19,10 @@ import ( "testing" appsv1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/types" "kpt.dev/configsync/e2e/nomostest" nomostesting "kpt.dev/configsync/e2e/nomostest/testing" "kpt.dev/configsync/e2e/nomostest/testpredicates" - "kpt.dev/configsync/pkg/api/configmanagement" "kpt.dev/configsync/pkg/api/configsync" - "kpt.dev/configsync/pkg/api/configsync/v1beta1" "kpt.dev/configsync/pkg/core/k8sobjects" "kpt.dev/configsync/pkg/kinds" "kpt.dev/configsync/pkg/reconcilermanager/controllers" @@ -33,6 +30,7 @@ import ( ) func TestSyncingThroughAProxy(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.SyncSource) nt.T.Logf("Set up the tiny proxy service and Override the RootSync object with proxy setting") @@ -50,13 +48,13 @@ func TestSyncingThroughAProxy(t *testing.T) { nt.T.Fatal(err) } nt.T.Log("Verify the NoOpProxyError") - rs := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) + rs := k8sobjects.RootSyncObjectV1Beta1(rootSyncID.Name) nt.WaitForRootSyncStalledError(rs.Name, "Validation", `KNV1061: RootSyncs which specify spec.git.proxy must also specify spec.git.auth as one of "none", "cookiefile" or "token"`) nt.T.Log("Set auth type to cookiefile") nt.MustMergePatch(rs, `{"spec": {"git": {"auth": "cookiefile"}}}`) nt.T.Log("Verify the secretRef error") - if err = nomostest.SetupFakeSSHCreds(nt, rs.Kind, nomostest.RootSyncNN(rs.Name), configsync.AuthCookieFile, controllers.GitCredentialVolume); err != nil { + if err = nomostest.SetupFakeSSHCreds(nt, rootSyncID.Kind, rootSyncID.ObjectKey, configsync.AuthCookieFile, controllers.GitCredentialVolume); err != nil { nt.T.Fatal(err) } nt.WaitForRootSyncStalledError(rs.Name, "Validation", `git secretType was set as "cookiefile" but cookie_file key is not present`) @@ -68,19 +66,12 @@ func TestSyncingThroughAProxy(t *testing.T) { nt.T.Log("Set auth type to none") nt.MustMergePatch(rs, `{"spec": {"git": {"auth": "none", "secretRef": {"name":""}}}}`) + nt.T.Log("Verify no errors") - rs = &v1beta1.RootSync{} - if err = nt.KubeClient.Get("root-sync", configmanagement.ControllerNamespace, rs); err != nil { - nt.T.Fatal(err) - } - err = nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(nomostest.RemoteRootRepoSha1Fn), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: "hierarchical-format/config", - })) - if err != nil { - nt.T.Fatal(err) - } + // Sync dir value set by testdata/proxy/proxy-enabled.yaml + nomostest.SetExpectedSyncPath(nt, rootSyncID, "hierarchical-format/config") + nt.Must(nt.WatchForAllSyncs( + nomostest.WithRootSha1Func(nomostest.RemoteRootRepoSha1Fn))) } func hasReadyReplicas(replicas int32) testpredicates.Predicate { diff --git a/e2e/testcases/reconciler_manager_test.go b/e2e/testcases/reconciler_manager_test.go index 87806a3ce..c6aa246b0 100644 --- a/e2e/testcases/reconciler_manager_test.go +++ b/e2e/testcases/reconciler_manager_test.go @@ -1261,12 +1261,10 @@ func TestReconcilerManagerRootSyncCRDMissing(t *testing.T) { // Change RepoSync sync dir to trigger reconciler-manager to update the reconciler repoSync := k8sobjects.RepoSyncObjectV1Beta1(repoSyncKey.Namespace, repoSyncKey.Name) nt.MustMergePatch(repoSync, fmt.Sprintf(`{"spec":{"git":{"dir":%q}}}`, repoSyncDir2)) + nomostest.SetExpectedSyncPath(nt, repoSyncID, repoSyncDir2) nt.Must(nt.WatchForAllSyncs( nomostest.SkipReadyCheck(), // Skip ready check because it requires the RootSync CRD to exist. - nomostest.RepoSyncOnly(), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - repoSyncKey: repoSyncDir2, - }))) + nomostest.RepoSyncOnly())) nt.Must(nt.Validate(saName2, repoSyncNS, &corev1.ServiceAccount{})) nt.Must(nomostest.InstallRootSyncCRD(nt)) @@ -1284,10 +1282,6 @@ func TestReconcilerManagerRootSyncCRDMissing(t *testing.T) { rootSync.Status = v1beta1.RootSyncStatus{} // Re-create RootSync with same spec as before nt.Must(nt.KubeClient.Create(rootSync)) - nt.Must(nt.WatchForAllSyncs( - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - rootSyncKey: rootSyncDir1, - repoSyncKey: repoSyncDir2, - }))) + nt.Must(nt.WatchForAllSyncs()) nt.Must(nt.Validate(nsName1, "", &corev1.Namespace{})) } diff --git a/e2e/testcases/root_sync_test.go b/e2e/testcases/root_sync_test.go index 20b7feb9d..fac2be89a 100644 --- a/e2e/testcases/root_sync_test.go +++ b/e2e/testcases/root_sync_test.go @@ -22,7 +22,6 @@ import ( "go.uber.org/multierr" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "kpt.dev/configsync/e2e/nomostest" "kpt.dev/configsync/e2e/nomostest/gitproviders" "kpt.dev/configsync/e2e/nomostest/metrics" @@ -87,9 +86,10 @@ func TestDeleteRootSyncAndRootSyncV1Alpha1(t *testing.T) { } func TestUpdateRootSyncGitDirectory(t *testing.T) { - rootSyncNN := nomostest.RootSyncNN(configsync.RootSyncName) + rootSyncID := nomostest.DefaultRootSyncID + rootSyncKey := rootSyncID.ObjectKey nt := nomostest.New(t, nomostesting.SyncSource) - rootSyncGitRepo := nt.SyncSourceGitRepository(nomostest.DefaultRootSyncID) + rootSyncGitRepo := nt.SyncSourceGitRepository(rootSyncID) // Validate RootSync is present. var rs v1beta1.RootSync @@ -133,22 +133,19 @@ func TestUpdateRootSyncGitDirectory(t *testing.T) { nt.T.Errorf("%s present after deletion: %v", fooNS, err) } - nt.MetricsExpectations.AddObjectApply(configsync.RootSyncKind, rootSyncNN, auditNSObj) + nt.MetricsExpectations.AddObjectApply(configsync.RootSyncKind, rootSyncKey, auditNSObj) err = nomostest.ValidateStandardMetricsForRootSync(nt, metrics.Summary{ - Sync: rootSyncNN, + Sync: rootSyncKey, }) if err != nil { nt.T.Fatal(err) } // Update RootSync. - nomostest.SetPolicyDir(nt, configsync.RootSyncName, fooDir) - syncDirectoryMap := map[types.NamespacedName]string{rootSyncNN: fooDir} - err = nt.WatchForAllSyncs(nomostest.WithSyncDirectoryMap(syncDirectoryMap)) - if err != nil { - nt.T.Fatal(err) - } + nomostest.SetRootSyncGitDir(nt, configsync.RootSyncName, fooDir) + nomostest.SetExpectedSyncPath(nt, rootSyncID, fooDir) + nt.Must(nt.WatchForAllSyncs()) // Validate namespace 'shipping' created with the correct sourcePath annotation. if err := nt.Validate(fooNS, "", fooNSObj, @@ -166,11 +163,11 @@ func TestUpdateRootSyncGitDirectory(t *testing.T) { } nt.MetricsExpectations.Reset() // Not using the default PolicyDir - nt.MetricsExpectations.AddObjectDelete(configsync.RootSyncKind, rootSyncNN, auditNSObj) - nt.MetricsExpectations.AddObjectApply(configsync.RootSyncKind, rootSyncNN, fooNSObj) + nt.MetricsExpectations.AddObjectDelete(configsync.RootSyncKind, rootSyncKey, auditNSObj) + nt.MetricsExpectations.AddObjectApply(configsync.RootSyncKind, rootSyncKey, fooNSObj) err = nomostest.ValidateStandardMetricsForRootSync(nt, metrics.Summary{ - Sync: rootSyncNN, + Sync: rootSyncKey, }) if err != nil { nt.T.Fatal(err) @@ -247,7 +244,7 @@ func TestUpdateRootSyncGitBranch(t *testing.T) { } // Set branch to "test-branch" - nomostest.SetGitBranch(nt, configsync.RootSyncName, branchB) + nomostest.SetRootSyncGitBranch(nt, configsync.RootSyncName, branchB) // Checkout 'test-branch' branch to get the correct HEAD commit sha1. nt.Must(rootSyncGitRepo.CheckoutBranch(branchB)) @@ -279,7 +276,7 @@ func TestUpdateRootSyncGitBranch(t *testing.T) { } // Set branch to "main" - nomostest.SetGitBranch(nt, configsync.RootSyncName, branchA) + nomostest.SetRootSyncGitBranch(nt, configsync.RootSyncName, branchA) // Checkout back to 'main' branch to get the correct HEAD commit sha1. nt.Must(rootSyncGitRepo.CheckoutBranch(branchA)) diff --git a/e2e/testcases/stress_test.go b/e2e/testcases/stress_test.go index 95c3c9b9e..b7e6249f5 100644 --- a/e2e/testcases/stress_test.go +++ b/e2e/testcases/stress_test.go @@ -38,12 +38,10 @@ import ( "kpt.dev/configsync/e2e/nomostest/iam" "kpt.dev/configsync/e2e/nomostest/ntopts" "kpt.dev/configsync/e2e/nomostest/registryproviders" - "kpt.dev/configsync/e2e/nomostest/syncsource" nomostesting "kpt.dev/configsync/e2e/nomostest/testing" "kpt.dev/configsync/e2e/nomostest/testpredicates" "kpt.dev/configsync/e2e/nomostest/testresourcegroup" "kpt.dev/configsync/e2e/nomostest/workloadidentity" - "kpt.dev/configsync/pkg/api/configmanagement" "kpt.dev/configsync/pkg/api/configsync" "kpt.dev/configsync/pkg/api/configsync/v1beta1" "kpt.dev/configsync/pkg/api/kpt.dev/v1alpha1" @@ -219,8 +217,9 @@ func TestStressFrequentGitCommits(t *testing.T) { // This test creates a RootSync pointed at https://github.com/config-sync-examples/crontab-crs // This repository contains 13,000+ objects, which takes a long time to reconcile func TestStressLargeRequest(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.Reconciliation1, ntopts.StressTest, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ntopts.WithReconcileTimeout(configsync.DefaultReconcileTimeout)) crdName := "crontabs.stable.example.com" @@ -236,7 +235,7 @@ func TestStressLargeRequest(t *testing.T) { nt.T.Fatal(err) } - rootSync := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName) + rootSync := k8sobjects.RootSyncObjectV1Beta1(rootSyncID.Name) reconcilerOverride := v1beta1.ContainerResourcesSpec{ ContainerName: reconcilermanager.Reconciler, MemoryLimit: resource.MustParse("1500Mi"), @@ -264,9 +263,10 @@ func TestStressLargeRequest(t *testing.T) { if err := nt.KubeClient.Apply(rootSync); err != nil { nt.T.Fatal(err) } + nomostest.SetExpectedSyncPath(nt, rootSyncID, "configs") nt.T.Logf("Verify that the source errors are truncated") - err = nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), "root-sync", configmanagement.ControllerNamespace, + err = nt.Watcher.WatchObject(kinds.RootSyncV1Beta1(), rootSyncID.Name, rootSyncID.Namespace, []testpredicates.Predicate{truncateSourceErrors()}) if err != nil { nt.T.Fatal(err) @@ -284,14 +284,8 @@ func TestStressLargeRequest(t *testing.T) { } nt.T.Logf("Wait for the sync to complete") - err = nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(nomostest.RemoteRootRepoSha1Fn), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: "configs", - })) - if err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.WithRootSha1Func(nomostest.RemoteRootRepoSha1Fn))) } // TestStress100CRDs applies 100 CRDs and validates that syncing still works. @@ -476,9 +470,10 @@ func TestStressMemoryUsageGit(t *testing.T) { // 6. IAM for the GSA to read from the Artifact Registry repo // 7. IAM for the test runner to write to Artifact Registry repo func TestStressMemoryUsageOCI(t *testing.T) { + rootSyncID := nomostest.DefaultRootSyncID nt := nomostest.New(t, nomostesting.WorkloadIdentity, ntopts.StressTest, ntopts.RequireOCIProvider, - ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured), + ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured), ntopts.WithReconcileTimeout(configsync.DefaultReconcileTimeout)) if err := workloadidentity.ValidateEnabled(nt); err != nil { @@ -514,28 +509,20 @@ func TestStressMemoryUsageOCI(t *testing.T) { } } - image, err := nt.BuildAndPushOCIImage(nomostest.RootSyncNN(configsync.RootSyncName), + image, err := nt.BuildAndPushOCIImage(rootSyncID.ObjectKey, registryproviders.ImageInputObjects(nt.Scheme, packageObjs...)) if err != nil { nt.T.Fatal(err) } nt.T.Log("Update RootSync to sync from the OCI image in Artifact Registry") - rs := nt.RootSyncObjectOCI(configsync.RootSyncName, image) - if err := nt.KubeClient.Apply(rs); err != nil { - nt.T.Fatal(err) - } + rs := nt.RootSyncObjectOCI(rootSyncID.Name, image.OCIImageID().WithoutDigest(), "", image.Digest) + nt.Must(nt.KubeClient.Apply(rs)) // Validate that the resources sync without the reconciler running out of // memory, getting OOMKilled, and crash looping. - err = nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(imageDigestFuncByDigest(image.Digest)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: ".", - })) - if err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.WithRootSha1Func(imageDigestFuncByDigest(image.Digest)))) nt.T.Logf("Verify the number of Anvil objects") for i := 1; i <= crdCount; i++ { @@ -551,21 +538,15 @@ func TestStressMemoryUsageOCI(t *testing.T) { } nt.T.Log("Remove all files and publish an empty OCI image") - emptyImage, err := nt.BuildAndPushOCIImage(nomostest.RootSyncNN(configsync.RootSyncName)) + emptyImage, err := nt.BuildAndPushOCIImage(rootSyncID.ObjectKey) if err != nil { nt.T.Fatal(err) } // Validate that the resources sync without the reconciler running out of // memory, getting OOMKilled, and crash looping. - err = nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(imageDigestFuncByDigest(emptyImage.Digest)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - nomostest.DefaultRootRepoNamespacedName: ".", - })) - if err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs( + nomostest.WithRootSha1Func(imageDigestFuncByDigest(emptyImage.Digest)))) } // TestStressMemoryUsageHelm applies 100 CRDs and then 50 objects for each @@ -649,10 +630,7 @@ func TestStressMemoryUsageHelm(t *testing.T) { emptyChart.Name, emptyChart.Version)) // Update the expected helm chart - nt.SyncSources[rootSyncID] = &syncsource.HelmSyncSource{ - ChartID: emptyChart.HelmChartID, - } - + nomostest.SetExpectedHelmSource(nt, rootSyncID, emptyChart.HelmChartID) // Validate that the resources sync without the reconciler running out of // memory, getting OOMKilled, and crash looping. nt.Must(nt.WatchForAllSyncs(nomostest.WithTimeout(5 * time.Minute))) diff --git a/e2e/testcases/workload_identity_test.go b/e2e/testcases/workload_identity_test.go index 9ed2ba5a1..a7ab455cd 100644 --- a/e2e/testcases/workload_identity_test.go +++ b/e2e/testcases/workload_identity_test.go @@ -197,21 +197,21 @@ func TestWorkloadIdentity(t *testing.T) { fleetWITest: false, crossProject: false, rootSrcCfg: sourceConfig{ - chart: privateCoreDNSHelmChart, - version: privateCoreDNSHelmChartVersion, - commitFn: nomostest.HelmChartVersionShaFn(privateCoreDNSHelmChartVersion)}, + chart: privateCoreDNSHelmChart, + version: privateCoreDNSHelmChartVersion, + }, nsSrcCfg: sourceConfig{ - chart: privateNSHelmChart, - version: "0.1.0", - commitFn: nomostest.HelmChartVersionShaFn("0.1.0")}, + chart: privateNSHelmChart, + version: "0.1.0", + }, newRootSrcCfg: sourceConfig{ - chart: privateSimpleHelmChart, - version: privateSimpleHelmChartVersion, - commitFn: nomostest.HelmChartVersionShaFn(privateSimpleHelmChartVersion)}, + chart: privateSimpleHelmChart, + version: privateSimpleHelmChartVersion, + }, newNSSrcCfg: sourceConfig{ - chart: "simple-ns-chart", - version: "1.0.0", - commitFn: nomostest.HelmChartVersionShaFn("1.0.0")}, + chart: "simple-ns-chart", + version: "1.0.0", + }, sourceType: configsync.HelmSource, gsaEmail: gsaARReaderEmail(), testKSAMigration: true, @@ -222,21 +222,21 @@ func TestWorkloadIdentity(t *testing.T) { fleetWITest: true, crossProject: false, rootSrcCfg: sourceConfig{ - chart: privateCoreDNSHelmChart, - version: privateCoreDNSHelmChartVersion, - commitFn: nomostest.HelmChartVersionShaFn(privateCoreDNSHelmChartVersion)}, + chart: privateCoreDNSHelmChart, + version: privateCoreDNSHelmChartVersion, + }, nsSrcCfg: sourceConfig{ - chart: privateNSHelmChart, - version: "0.1.0", - commitFn: nomostest.HelmChartVersionShaFn("0.1.0")}, + chart: privateNSHelmChart, + version: "0.1.0", + }, newRootSrcCfg: sourceConfig{ - chart: privateSimpleHelmChart, - version: privateSimpleHelmChartVersion, - commitFn: nomostest.HelmChartVersionShaFn(privateSimpleHelmChartVersion)}, + chart: privateSimpleHelmChart, + version: privateSimpleHelmChartVersion, + }, newNSSrcCfg: sourceConfig{ - chart: "simple-ns-chart", - version: "1.0.0", - commitFn: nomostest.HelmChartVersionShaFn("1.0.0")}, + chart: "simple-ns-chart", + version: "1.0.0", + }, sourceType: configsync.HelmSource, gsaEmail: gsaARReaderEmail(), testKSAMigration: true, @@ -247,21 +247,21 @@ func TestWorkloadIdentity(t *testing.T) { fleetWITest: true, crossProject: true, rootSrcCfg: sourceConfig{ - chart: privateCoreDNSHelmChart, - version: privateCoreDNSHelmChartVersion, - commitFn: nomostest.HelmChartVersionShaFn(privateCoreDNSHelmChartVersion)}, + chart: privateCoreDNSHelmChart, + version: privateCoreDNSHelmChartVersion, + }, nsSrcCfg: sourceConfig{ - chart: privateNSHelmChart, - version: "0.1.0", - commitFn: nomostest.HelmChartVersionShaFn("0.1.0")}, + chart: privateNSHelmChart, + version: "0.1.0", + }, newRootSrcCfg: sourceConfig{ - chart: privateSimpleHelmChart, - version: privateSimpleHelmChartVersion, - commitFn: nomostest.HelmChartVersionShaFn(privateSimpleHelmChartVersion)}, + chart: privateSimpleHelmChart, + version: privateSimpleHelmChartVersion, + }, newNSSrcCfg: sourceConfig{ - chart: "simple-ns-chart", - version: "1.0.0", - commitFn: nomostest.HelmChartVersionShaFn("1.0.0")}, + chart: "simple-ns-chart", + version: "1.0.0", + }, sourceType: configsync.HelmSource, gsaEmail: gsaARReaderEmail(), testKSAMigration: true, @@ -371,11 +371,11 @@ func TestWorkloadIdentity(t *testing.T) { case configsync.OciSource: if tc.requireOCIGAR { // OCI provider is AR - rootMeta, err = updateRootSyncWithOCISourceConfig(nt, rootSyncKey, tc.rootSrcCfg) + rootMeta, err = updateRootSyncWithOCISourceConfig(nt, rootSyncID, tc.rootSrcCfg) if err != nil { nt.T.Fatal(err) } - nsMeta, err = updateRepoSyncWithOCISourceConfig(nt, repoSyncKey, tc.nsSrcCfg) + nsMeta, err = updateRepoSyncWithOCISourceConfig(nt, repoSyncID, tc.nsSrcCfg) if err != nil { nt.T.Fatal(err) } @@ -392,7 +392,7 @@ func TestWorkloadIdentity(t *testing.T) { } }`, configsync.OciSource, tc.rootSrcCfg.repo, tc.rootSrcCfg.dir, tc.gsaEmail)) rootMeta = rsyncValidateMeta{ - rsRef: rootSyncKey, + rsID: rootSyncID, sha1Func: tc.rootSrcCfg.commitFn, syncDir: tc.rootSrcCfg.dir, } @@ -408,7 +408,7 @@ func TestWorkloadIdentity(t *testing.T) { } }`, configsync.OciSource, tc.nsSrcCfg.repo, tc.nsSrcCfg.dir, tc.gsaEmail)) nsMeta = rsyncValidateMeta{ - rsRef: repoSyncKey, + rsID: repoSyncID, sha1Func: tc.nsSrcCfg.commitFn, syncDir: tc.nsSrcCfg.dir, } @@ -457,43 +457,27 @@ func TestWorkloadIdentity(t *testing.T) { switch tc.sourceType { case configsync.GitSource, configsync.OciSource: - if err = nt.WatchForAllSyncs( + nomostest.SetExpectedSyncPath(nt, rootMeta.rsID, rootMeta.syncDir) + nomostest.SetExpectedSyncPath(nt, nsMeta.rsID, nsMeta.syncDir) + nt.Must(nt.WatchForAllSyncs( nomostest.WithRootSha1Func(rootMeta.sha1Func), - nomostest.WithRepoSha1Func(nsMeta.sha1Func), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - rootMeta.rsRef: rootMeta.syncDir, - nsMeta.rsRef: nsMeta.syncDir})); err != nil { - nt.T.Fatal(err) - } + nomostest.WithRepoSha1Func(nsMeta.sha1Func))) kustomizecomponents.ValidateAllTenants(nt, string(declared.RootScope), "base", "tenant-a", "tenant-b", "tenant-c") - kustomizecomponents.ValidateTenant(nt, nsMeta.rsRef.Namespace, "test-ns", "base") + kustomizecomponents.ValidateTenant(nt, nsMeta.rsID.Namespace, "test-ns", "base") case configsync.HelmSource: - if err = nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(nomostest.HelmChartVersionShaFn(rootChart.Version)), - nomostest.WithRepoSha1Func(nomostest.HelmChartVersionShaFn(nsChart.Version)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - rootSyncKey: rootChart.Name, - repoSyncKey: nsChart.Name})); err != nil { - nt.T.Fatal(err) - } - if err = nt.Validate(truncateStringByLength(fmt.Sprintf("%s-%s", rootChart.Name, rootChart.Name), 63), + nt.Must(nt.WatchForAllSyncs()) + nt.Must(nt.Validate(truncateStringByLength(fmt.Sprintf("%s-%s", rootChart.Name, rootChart.Name), 63), "default", &appsv1.Deployment{}, - testpredicates.IsManagedBy(nt.Scheme, declared.RootScope, rootSyncKey.Name)); err != nil { - nt.T.Fatal(err) - } - if err = nt.Validate(truncateStringByLength(nsChart.Name, 63), + testpredicates.IsManagedBy(nt.Scheme, declared.RootScope, rootSyncKey.Name))) + nt.Must(nt.Validate(truncateStringByLength(nsChart.Name, 63), repoSyncKey.Namespace, &appsv1.Deployment{}, - testpredicates.IsManagedBy(nt.Scheme, declared.Scope(repoSyncKey.Namespace), repoSyncKey.Name)); err != nil { - nt.T.Fatal(err) - } + testpredicates.IsManagedBy(nt.Scheme, declared.Scope(repoSyncKey.Namespace), repoSyncKey.Name))) } // Migrate from gcpserviceaccount to k8sserviceaccount if tc.testKSAMigration { - if err := migrateFromGSAtoKSA(nt, tc.fleetWITest, tc.sourceType, rootSyncKey, repoSyncKey, tc.newRootSrcCfg, tc.newNSSrcCfg); err != nil { - nt.T.Fatal(err) - } + nt.Must(migrateFromGSAtoKSA(nt, tc.fleetWITest, tc.sourceType, rootSyncID, repoSyncID, tc.newRootSrcCfg, tc.newNSSrcCfg)) } }) } @@ -540,17 +524,16 @@ func updateRepoSyncWithHelmSourceConfig(nt *nomostest.NT, rsRef types.Namespaced return chart, nil } -func updateRootSyncWithOCISourceConfig(nt *nomostest.NT, rsRef types.NamespacedName, sc sourceConfig, mutators ...rootSyncMutator) (rsyncValidateMeta, error) { - meta := rsyncValidateMeta{rsRef: rsRef, syncDir: sc.dir} - image, err := nt.BuildAndPushOCIImage(rsRef, +func updateRootSyncWithOCISourceConfig(nt *nomostest.NT, rsID core.ID, sc sourceConfig, mutators ...rootSyncMutator) (rsyncValidateMeta, error) { + meta := rsyncValidateMeta{rsID: rsID, syncDir: sc.dir} + image, err := nt.BuildAndPushOCIImage(rsID.ObjectKey, registryproviders.ImageSourcePackage(sc.pkg), registryproviders.ImageVersion(sc.version)) if err != nil { return meta, fmt.Errorf("pushing oci image: %w", err) } nt.T.Log("Update RootSync to sync from an OCI image") - rootSyncOCI := nt.RootSyncObjectOCI(configsync.RootSyncName, image) - rootSyncOCI.Spec.Oci.Dir = sc.dir + rootSyncOCI := nt.RootSyncObjectOCI(configsync.RootSyncName, image.OCIImageID().WithoutDigest(), sc.dir, image.Digest) for _, mutator := range mutators { mutator(rootSyncOCI) } @@ -561,17 +544,16 @@ func updateRootSyncWithOCISourceConfig(nt *nomostest.NT, rsRef types.NamespacedN return meta, nil } -func updateRepoSyncWithOCISourceConfig(nt *nomostest.NT, rsRef types.NamespacedName, sc sourceConfig, mutators ...repoSyncMutator) (rsyncValidateMeta, error) { - meta := rsyncValidateMeta{rsRef: rsRef, syncDir: sc.dir} - image, err := nt.BuildAndPushOCIImage(rsRef, +func updateRepoSyncWithOCISourceConfig(nt *nomostest.NT, rsID core.ID, sc sourceConfig, mutators ...repoSyncMutator) (rsyncValidateMeta, error) { + meta := rsyncValidateMeta{rsID: rsID, syncDir: sc.dir} + image, err := nt.BuildAndPushOCIImage(rsID.ObjectKey, registryproviders.ImageSourcePackage(sc.pkg), registryproviders.ImageVersion(sc.version)) if err != nil { return meta, fmt.Errorf("pushing oci image: %w", err) } nt.T.Log("Update RepoSync to sync from an OCI image") - repoSyncOCI := nt.RepoSyncObjectOCI(rsRef, image) - repoSyncOCI.Spec.Oci.Dir = sc.dir + repoSyncOCI := nt.RepoSyncObjectOCI(rsID.ObjectKey, image.OCIImageID().WithoutDigest(), sc.dir, image.Digest) for _, mutator := range mutators { mutator(repoSyncOCI) } @@ -583,7 +565,7 @@ func updateRepoSyncWithOCISourceConfig(nt *nomostest.NT, rsRef types.NamespacedN } type rsyncValidateMeta struct { - rsRef types.NamespacedName + rsID core.ID sha1Func nomostest.Sha1Func syncDir string } @@ -598,8 +580,11 @@ func updateRSyncWithGitSourceConfig(nt *nomostest.NT, rs client.Object, repo *gi } } }`, sc.dir)) - rsRef := client.ObjectKey{Name: rs.GetName(), Namespace: rs.GetNamespace()} - return rsyncValidateMeta{rsRef: rsRef, sha1Func: sc.commitFn, syncDir: sc.dir} + id, err := kinds.LookupID(rs, nt.Scheme) + if err != nil { + nt.T.Fatalf("invalid object ID: %v", err) + } + return rsyncValidateMeta{rsID: id, sha1Func: sc.commitFn, syncDir: sc.dir} } func truncateStringByLength(s string, l int) string { @@ -611,34 +596,33 @@ func truncateStringByLength(s string, l int) string { // migrateFromGSAtoKSA tests the scenario of migrating from impersonating a GSA // to leveraging KSA+WI (a.k.a, BYOID/Ubermint). -func migrateFromGSAtoKSA(nt *nomostest.NT, fleetWITest bool, sourceType configsync.SourceType, rsRef, nsRef types.NamespacedName, rootSC, nsSC sourceConfig) error { +func migrateFromGSAtoKSA(nt *nomostest.NT, fleetWITest bool, sourceType configsync.SourceType, rsID, nsID core.ID, rootSC, nsSC sourceConfig) error { nt.T.Log("Update RootSync auth type from gcpserviceaccount to k8sserviceaccount") var err error var rootMeta, nsMeta rsyncValidateMeta - var rootChart, nsChart *registryproviders.HelmPackage // Change the source config to guarantee new resources can be reconciled with k8sserviceaccount switch sourceType { case configsync.HelmSource: - rootChart, err = updateRootSyncWithHelmSourceConfig(nt, rsRef, rootSC, func(rs *v1beta1.RootSync) { + _, err = updateRootSyncWithHelmSourceConfig(nt, rsID.ObjectKey, rootSC, func(rs *v1beta1.RootSync) { rs.Spec.Helm.Auth = configsync.AuthK8sServiceAccount }) if err != nil { nt.T.Fatal(err) } - nsChart, err = updateRepoSyncWithHelmSourceConfig(nt, nsRef, nsSC, func(rs *v1beta1.RepoSync) { + _, err = updateRepoSyncWithHelmSourceConfig(nt, nsID.ObjectKey, nsSC, func(rs *v1beta1.RepoSync) { rs.Spec.Helm.Auth = configsync.AuthK8sServiceAccount }) if err != nil { nt.T.Fatal(err) } case configsync.OciSource: - rootMeta, err = updateRootSyncWithOCISourceConfig(nt, rsRef, rootSC, func(rs *v1beta1.RootSync) { + rootMeta, err = updateRootSyncWithOCISourceConfig(nt, rsID, rootSC, func(rs *v1beta1.RootSync) { rs.Spec.Oci.Auth = configsync.AuthK8sServiceAccount }) if err != nil { nt.T.Fatal(err) } - nsMeta, err = updateRepoSyncWithOCISourceConfig(nt, nsRef, nsSC, func(rs *v1beta1.RepoSync) { + nsMeta, err = updateRepoSyncWithOCISourceConfig(nt, nsID, nsSC, func(rs *v1beta1.RepoSync) { rs.Spec.Oci.Auth = configsync.AuthK8sServiceAccount }) if err != nil { @@ -647,8 +631,8 @@ func migrateFromGSAtoKSA(nt *nomostest.NT, fleetWITest bool, sourceType configsy } // Validations - rootReconcilerName := core.RootReconcilerName(rsRef.Name) - nsReconcilerName := core.NsReconcilerName(nsRef.Namespace, nsRef.Name) + rootReconcilerName := core.RootReconcilerName(rsID.Name) + nsReconcilerName := core.NsReconcilerName(nsID.Namespace, nsID.Name) nt.T.Log("Validate the GSA annotation is removed from the RSync's service accounts") tg := taskgroup.New() tg.Go(func() error { @@ -690,14 +674,11 @@ func migrateFromGSAtoKSA(nt *nomostest.NT, fleetWITest bool, sourceType configsy switch sourceType { case configsync.GitSource, configsync.OciSource: - if err = nt.WatchForAllSyncs( + nomostest.SetExpectedSyncPath(nt, rootMeta.rsID, rootMeta.syncDir) + nomostest.SetExpectedSyncPath(nt, nsMeta.rsID, nsMeta.syncDir) + nt.Must(nt.WatchForAllSyncs( nomostest.WithRootSha1Func(rootMeta.sha1Func), - nomostest.WithRepoSha1Func(nsMeta.sha1Func), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - rootMeta.rsRef: rootMeta.syncDir, - nsMeta.rsRef: nsMeta.syncDir})); err != nil { - nt.T.Fatal(err) - } + nomostest.WithRepoSha1Func(nsMeta.sha1Func))) kustomizecomponents.ValidateAllTenants(nt, string(declared.RootScope), "../base", "tenant-a") if err := nt.ValidateNotFound("tenant-b", "", &corev1.Namespace{}); err != nil { return err @@ -705,29 +686,16 @@ func migrateFromGSAtoKSA(nt *nomostest.NT, fleetWITest bool, sourceType configsy if err := nt.ValidateNotFound("tenant-c", "", &corev1.Namespace{}); err != nil { return err } - kustomizecomponents.ValidateTenant(nt, nsMeta.rsRef.Namespace, "test-ns", "../base") + kustomizecomponents.ValidateTenant(nt, nsMeta.rsID.Namespace, "test-ns", "../base") case configsync.HelmSource: - if err = nt.WatchForAllSyncs( - nomostest.WithRootSha1Func(nomostest.HelmChartVersionShaFn(rootChart.Version)), - nomostest.WithRepoSha1Func(nomostest.HelmChartVersionShaFn(nsChart.Version)), - nomostest.WithSyncDirectoryMap(map[types.NamespacedName]string{ - rsRef: rootChart.Name, - nsRef: nsChart.Name})); err != nil { - nt.T.Fatal(err) - } - if err = nt.Validate("deploy-ns", "ns", &appsv1.Deployment{}, - testpredicates.IsManagedBy(nt.Scheme, declared.RootScope, rsRef.Name)); err != nil { - nt.T.Fatal(err) - } - if err = nt.Validate("deploy-default", "default", &appsv1.Deployment{}, - testpredicates.IsManagedBy(nt.Scheme, declared.RootScope, rsRef.Name)); err != nil { - nt.T.Fatal(err) - } - if err = nt.Validate("repo-sync-deployment", testNs, &appsv1.Deployment{}, - testpredicates.IsManagedBy(nt.Scheme, declared.Scope(nsRef.Namespace), nsRef.Name)); err != nil { - nt.T.Fatal(err) - } + nt.Must(nt.WatchForAllSyncs()) + nt.Must(nt.Validate("deploy-ns", "ns", &appsv1.Deployment{}, + testpredicates.IsManagedBy(nt.Scheme, declared.RootScope, rsID.Name))) + nt.Must(nt.Validate("deploy-default", "default", &appsv1.Deployment{}, + testpredicates.IsManagedBy(nt.Scheme, declared.RootScope, rsID.Name))) + nt.Must(nt.Validate("repo-sync-deployment", testNs, &appsv1.Deployment{}, + testpredicates.IsManagedBy(nt.Scheme, declared.Scope(nsID.Namespace), nsID.Name))) } return nil } diff --git a/pkg/core/id.go b/pkg/core/id.go index 1d5a694a0..d656b2f85 100644 --- a/pkg/core/id.go +++ b/pkg/core/id.go @@ -32,10 +32,12 @@ type ID struct { } // IDOf converts an Object to its ID. +// +// TODO: Replace usage with LookupID, unless explicitly Unstructured. func IDOf(o client.Object) ID { return ID{ GroupKind: o.GetObjectKind().GroupVersionKind().GroupKind(), - ObjectKey: client.ObjectKey{Namespace: o.GetNamespace(), Name: o.GetName()}, + ObjectKey: client.ObjectKeyFromObject(o), } } diff --git a/pkg/kinds/lookup.go b/pkg/kinds/lookup.go index 275458dd1..c3d024220 100644 --- a/pkg/kinds/lookup.go +++ b/pkg/kinds/lookup.go @@ -19,6 +19,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "kpt.dev/configsync/pkg/core" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" ) @@ -31,3 +33,17 @@ func Lookup(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionKind } return gvk, nil } + +// LookupID returns the object's ID. If the GK isn't already populated, the +// Scheme is used to look it up by object type. +func LookupID(obj client.Object, scheme *runtime.Scheme) (core.ID, error) { + id := core.IDOf(obj) + if id.GroupKind.Empty() { + gvk, err := Lookup(obj, scheme) + if err != nil { + return id, err + } + id.GroupKind = gvk.GroupKind() + } + return id, nil +} diff --git a/pkg/syncer/syncertest/fake/common.go b/pkg/syncer/syncertest/fake/common.go index 778edaf2e..3c9e0ab91 100644 --- a/pkg/syncer/syncertest/fake/common.go +++ b/pkg/syncer/syncertest/fake/common.go @@ -61,20 +61,6 @@ func toTypedClientObject(obj client.Object, scheme *runtime.Scheme) (client.Obje return cObj, nil } -// lookupObjectID returns the object's ID. -// If the GK isn't set, the Scheme is used to look it up by object type. -func lookupObjectID(obj client.Object, scheme *runtime.Scheme) (core.ID, error) { - id := core.IDOf(obj) - if id.GroupKind.Empty() { - gvk, err := kinds.Lookup(obj, scheme) - if err != nil { - return id, err - } - id.GroupKind = gvk.GroupKind() - } - return id, nil -} - // matchesListFilters returns true if the object matches the constraints // specified by the ListOptions: Namespace, LabelSelector, and FieldSelector. func matchesListFilters(obj runtime.Object, opts *client.ListOptions, scheme *runtime.Scheme) (bool, error) { diff --git a/pkg/syncer/syncertest/fake/storage.go b/pkg/syncer/syncertest/fake/storage.go index 9132b3dfe..324de3aa0 100644 --- a/pkg/syncer/syncertest/fake/storage.go +++ b/pkg/syncer/syncertest/fake/storage.go @@ -154,7 +154,7 @@ func (ms *MemoryStorage) TestPut(obj client.Object) error { ms.lock.Lock() defer ms.lock.Unlock() - id, err := lookupObjectID(obj, ms.scheme) + id, err := kinds.LookupID(obj, ms.scheme) if err != nil { return err } @@ -173,7 +173,7 @@ func (ms *MemoryStorage) TestPutAll(objs ...client.Object) error { defer ms.lock.Unlock() for _, obj := range objs { - id, err := lookupObjectID(obj, ms.scheme) + id, err := kinds.LookupID(obj, ms.scheme) if err != nil { return err } @@ -470,7 +470,7 @@ func (ms *MemoryStorage) Create(ctx context.Context, obj client.Object, opts *cl return err } - id, err := lookupObjectID(tObj, ms.scheme) + id, err := kinds.LookupID(tObj, ms.scheme) if err != nil { return err } @@ -529,7 +529,7 @@ func (ms *MemoryStorage) deleteWithoutLock(ctx context.Context, obj client.Objec return err } - id, err := lookupObjectID(obj, ms.scheme) + id, err := kinds.LookupID(obj, ms.scheme) if err != nil { return err } @@ -717,7 +717,7 @@ func (ms *MemoryStorage) updateWithoutLock(ctx context.Context, obj client.Objec return err } - id, err := lookupObjectID(obj, ms.scheme) + id, err := kinds.LookupID(obj, ms.scheme) if err != nil { return err } @@ -832,7 +832,7 @@ func (ms *MemoryStorage) Patch(ctx context.Context, obj client.Object, patch cli return fmt.Errorf("failed to build patch: %w", err) } - id, err := lookupObjectID(obj, ms.scheme) + id, err := kinds.LookupID(obj, ms.scheme) if err != nil { return err } diff --git a/pkg/syncer/syncertest/fake/subresource_storage.go b/pkg/syncer/syncertest/fake/subresource_storage.go index a53f97698..fad99f2be 100644 --- a/pkg/syncer/syncertest/fake/subresource_storage.go +++ b/pkg/syncer/syncertest/fake/subresource_storage.go @@ -58,7 +58,7 @@ func (ss *SubresourceStorage) Update(ctx context.Context, obj client.Object, opt return err } - id, err := lookupObjectID(obj, ss.Storage.scheme) + id, err := kinds.LookupID(obj, ss.Storage.scheme) if err != nil { return err }