diff --git a/config/tests/samples/create/harness.go b/config/tests/samples/create/harness.go index 121a1fbd9b..2829ae44c6 100644 --- a/config/tests/samples/create/harness.go +++ b/config/tests/samples/create/harness.go @@ -25,6 +25,7 @@ import ( "time" exportparameters "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/cli/cmd/export/parameters" + "google.golang.org/api/cloudresourcemanager/v1" cloudresourcemanagerv1 "google.golang.org/api/cloudresourcemanager/v1" "google.golang.org/api/option" @@ -37,6 +38,7 @@ import ( "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/logging" "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test" testenvironment "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/environment" + testgcp "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/gcp" testwebhook "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/webhook" cnrmwebhook "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/webhook" "golang.org/x/oauth2" @@ -64,6 +66,8 @@ type Harness struct { *testing.T Ctx context.Context + Project testgcp.GCPProject + client client.Client restConfig *rest.Config @@ -248,6 +252,42 @@ func NewHarness(t *testing.T, ctx context.Context) *Harness { t.Fatalf("E2E_GCP_TARGET=%q not supported", targetGCP) } + if os.Getenv("E2E_GCP_TARGET") == "mock" { + // Some fixed-value fake org-ids for testing. + // We used fixed values so that the output is predictable (for golden testing) + testgcp.TestFolderID.Set("123451001") + testgcp.TestFolder2ID.Set("123451002") + testgcp.TestOrgID.Set("123450001") + testgcp.TestBillingAccountID.Set("123456-777777-000001") + testgcp.IAMIntegrationTestsOrganizationID.Set("123450002") + testgcp.IAMIntegrationTestsBillingAccountID.Set("123456-777777-000002") + testgcp.TestAttachedClusterName.Set("xks-cluster") + + crm := h.getCloudResourceManagerClient(kccConfig.HTTPClient) + req := &cloudresourcemanager.Project{ + ProjectId: "mock-project", + } + op, err := crm.Projects.Create(req).Context(ctx).Do() + if err != nil { + t.Fatalf("error creating project: %v", err) + } + if !op.Done { + t.Fatalf("expected mock create project operation to be done immediately") + } + found, err := crm.Projects.Get(req.ProjectId).Context(ctx).Do() + if err != nil { + t.Fatalf("error reading created project: %v", err) + } + project := testgcp.GCPProject{ + ProjectID: found.ProjectId, + ProjectNumber: found.ProjectNumber, + } + testgcp.TestKCCAttachedClusterProject.Set("mock-project") + h.Project = project + } else { + h.Project = testgcp.GetDefaultProject(t) + } + // Log DCL requests if artifacts := os.Getenv("ARTIFACTS"); artifacts != "" { outputDir := filepath.Join(artifacts, "http-logs") @@ -346,8 +386,8 @@ func (h *Harness) ExportParams() exportparameters.Parameters { return exportParams } -func (h *Harness) GetCloudResourceManagerClient() *cloudresourcemanagerv1.Service { - s, err := cloudresourcemanagerv1.NewService(h.Ctx, option.WithHTTPClient(h.kccConfig.HTTPClient)) +func (h *Harness) getCloudResourceManagerClient(httpClient *http.Client) *cloudresourcemanagerv1.Service { + s, err := cloudresourcemanagerv1.NewService(h.Ctx, option.WithHTTPClient(httpClient)) if err != nil { h.Fatalf("error building cloudresourcemanagerv1 client: %v", err) } diff --git a/config/tests/samples/create/samples.go b/config/tests/samples/create/samples.go index 633a2a611f..a106df09a7 100644 --- a/config/tests/samples/create/samples.go +++ b/config/tests/samples/create/samples.go @@ -38,6 +38,7 @@ import ( "github.com/ghodss/yaml" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -64,8 +65,8 @@ func networksInSampleCount(sample Sample) int { return count } -func SetupNamespacesAndApplyDefaults(t *Harness, samples []Sample, project testgcp.GCPProject) { - namespaceNames := getNamespaces(samples) +func SetupNamespacesAndApplyDefaults(t *Harness, resources []*unstructured.Unstructured, project testgcp.GCPProject) { + namespaceNames := getNamespaces(resources) setupNamespaces(t, namespaceNames, project) } @@ -75,18 +76,12 @@ func setupNamespaces(t *Harness, namespaces []string, project testgcp.GCPProject } } -func getNamespaces(samples []Sample) []string { - namespaces := make(map[string]bool) - for _, sample := range samples { - for _, unstruct := range sample.Resources { - namespaces[unstruct.GetNamespace()] = true - } - } - results := make([]string, 0, len(namespaces)) - for k := range namespaces { - results = append(results, k) +func getNamespaces(resources []*unstructured.Unstructured) []string { + namespaces := sets.NewString() + for _, unstruct := range resources { + namespaces.Insert(unstruct.GetNamespace()) } - return results + return namespaces.List() } type CreateDeleteTestOptions struct { diff --git a/config/tests/samples/create/samples_test.go b/config/tests/samples/create/samples_test.go index 779a03efeb..bb96b2273c 100644 --- a/config/tests/samples/create/samples_test.go +++ b/config/tests/samples/create/samples_test.go @@ -257,7 +257,7 @@ func TestAll(t *testing.T) { ctx := context.TODO() h := NewHarnessWithManager(t, ctx, mgr) - SetupNamespacesAndApplyDefaults(h, []Sample{s}, project) + SetupNamespacesAndApplyDefaults(h, s.Resources, project) networkCount := int64(networksInSampleCount(s)) if networkCount > 0 { diff --git a/tests/e2e/unified_test.go b/tests/e2e/unified_test.go index b6a7d4d8f8..564b9b6191 100644 --- a/tests/e2e/unified_test.go +++ b/tests/e2e/unified_test.go @@ -28,7 +28,6 @@ import ( "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/resourcefixture" testvariable "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/resourcefixture/variable" testyaml "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/yaml" - "google.golang.org/api/cloudresourcemanager/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -46,57 +45,25 @@ func TestAllInSeries(t *testing.T) { cancel() }) - testHarness := create.NewHarness(t, ctx) - - var project testgcp.GCPProject - if os.Getenv("E2E_GCP_TARGET") == "mock" { - // Some fixed-value fake org-ids for testing. - // We used fixed values so that the output is predictable (for golden testing) - testgcp.TestFolderID.Set("123451001") - testgcp.TestFolder2ID.Set("123451002") - testgcp.TestOrgID.Set("123450001") - testgcp.TestBillingAccountID.Set("123456-777777-000001") - testgcp.IAMIntegrationTestsOrganizationID.Set("123450002") - testgcp.IAMIntegrationTestsBillingAccountID.Set("123456-777777-000002") - testgcp.TestAttachedClusterName.Set("xks-cluster") - - crm := testHarness.GetCloudResourceManagerClient() - req := &cloudresourcemanager.Project{ - ProjectId: "mock-project", - } - op, err := crm.Projects.Create(req).Context(testHarness.Ctx).Do() - if err != nil { - testHarness.Fatalf("error creating project: %v", err) - } - if !op.Done { - testHarness.Fatalf("expected mock create project operation to be done immediately") - } - found, err := crm.Projects.Get(req.ProjectId).Context(testHarness.Ctx).Do() - if err != nil { - testHarness.Fatalf("error reading created project: %v", err) - } - project = testgcp.GCPProject{ - ProjectID: found.ProjectId, - ProjectNumber: found.ProjectNumber, - } - testgcp.TestKCCAttachedClusterProject.Set("mock-project") - } else { - project = testgcp.GetDefaultProject(t) - } - t.Run("samples", func(t *testing.T) { - samples := create.LoadAllSamples(t, project) + samples := create.ListAllSamples(t) - for _, s := range samples { - s := s + for _, sampleKey := range samples { + sampleKey := sampleKey // TODO(b/259496928): Randomize the resource names for parallel execution when/if needed. - t.Run(s.Name, func(t *testing.T) { - create.MaybeSkip(t, s.Name, s.Resources) + t.Run(sampleKey.Name, func(t *testing.T) { + // Quickly load the sample with a dummy project, just to see if we should skip it + { + dummySample := create.LoadSample(t, sampleKey, testgcp.GCPProject{ProjectID: "test-skip", ProjectNumber: 123456789}) + create.MaybeSkip(t, sampleKey.Name, dummySample.Resources) + } - h := testHarness.ForSubtest(t) + h := create.NewHarness(t, ctx) + project := h.Project + s := create.LoadSample(t, sampleKey, project) - create.SetupNamespacesAndApplyDefaults(h, []create.Sample{s}, project) + create.SetupNamespacesAndApplyDefaults(h, s.Resources, project) // Hack: set project-id because mockkubeapiserver does not support webhooks for _, u := range s.Resources { @@ -119,37 +86,44 @@ func TestAllInSeries(t *testing.T) { fixture := fixture // TODO(b/259496928): Randomize the resource names for parallel execution when/if needed. - testID := testvariable.NewUniqueId() + t.Run(fixture.Name, func(t *testing.T) { + uniqueID := testvariable.NewUniqueId() + + loadFixture := func(project testgcp.GCPProject) (*unstructured.Unstructured, create.CreateDeleteTestOptions) { + primaryResource := bytesToUnstructured(t, fixture.Create, uniqueID, project) - s := create.Sample{ - Name: fixture.Name, - } + opt := create.CreateDeleteTestOptions{CleanupResources: true} + opt.Create = append(opt.Create, primaryResource) - createResource := bytesToUnstructured(t, fixture.Create, testID, project) - s.Resources = append(s.Resources, createResource) + if fixture.Dependencies != nil { + dependencyYamls := testyaml.SplitYAML(t, fixture.Dependencies) + for _, dependBytes := range dependencyYamls { + depUnstruct := bytesToUnstructured(t, dependBytes, uniqueID, project) + opt.Create = append(opt.Create, depUnstruct) + } + } - exportResources := []*unstructured.Unstructured{createResource} + if fixture.Update != nil { + u := bytesToUnstructured(t, fixture.Update, uniqueID, project) + opt.Updates = append(opt.Updates, u) + } + return primaryResource, opt + } - if fixture.Dependencies != nil { - dependencyYamls := testyaml.SplitYAML(t, fixture.Dependencies) - for _, dependBytes := range dependencyYamls { - depUnstruct := bytesToUnstructured(t, dependBytes, testID, project) - s.Resources = append(s.Resources, depUnstruct) + // Quickly load the fixture with a dummy project, just to see if we should skip it + { + _, opt := loadFixture(testgcp.GCPProject{ProjectID: "test-skip", ProjectNumber: 123456789}) + create.MaybeSkip(t, fixture.Name, opt.Create) } - } - opt := create.CreateDeleteTestOptions{Create: s.Resources, CleanupResources: true} - if fixture.Update != nil { - u := bytesToUnstructured(t, fixture.Update, testID, project) - opt.Updates = append(opt.Updates, u) - } + h := create.NewHarness(t, ctx) + project := h.Project - t.Run(s.Name, func(t *testing.T) { - create.MaybeSkip(t, s.Name, s.Resources) + primaryResource, opt := loadFixture(project) - h := testHarness.ForSubtest(t) + exportResources := []*unstructured.Unstructured{primaryResource} - create.SetupNamespacesAndApplyDefaults(h, []create.Sample{s}, project) + create.SetupNamespacesAndApplyDefaults(h, opt.Create, project) opt.CleanupResources = false // We delete explicitly below create.RunCreateDeleteTest(h, opt) @@ -186,7 +160,7 @@ func TestAllInSeries(t *testing.T) { h.CompareGoldenFile(expectedPath, string(output), h.IgnoreComments, h.ReplaceString(project.ProjectID, "example-project-id")) } - create.DeleteResources(h, s.Resources) + create.DeleteResources(h, opt.Create) }) } })