From 437abd7590e6f86b24aed705e45345bcaa3fcf4f Mon Sep 17 00:00:00 2001 From: Jing Qi Date: Wed, 28 Jun 2023 11:24:50 +0800 Subject: [PATCH] Add appstudio redhat nstemplatetier Signed-off-by: Jing Qi --- test/e2e/nstemplatetier_test.go | 2 +- testsupport/tiers/checks.go | 157 +++++++++++++++++++++++++++++++- testsupport/wait/member.go | 26 ++++++ 3 files changed, 183 insertions(+), 2 deletions(-) diff --git a/test/e2e/nstemplatetier_test.go b/test/e2e/nstemplatetier_test.go index adae4c754..c16d5637e 100644 --- a/test/e2e/nstemplatetier_test.go +++ b/test/e2e/nstemplatetier_test.go @@ -43,7 +43,7 @@ func TestNSTemplateTiers(t *testing.T) { Resources() // all tiers to check - keep the base as the last one, it will verify downgrade back to the default tier at the end of the test - tiersToCheck := []string{"advanced", "baseextendedidling", "baselarge", "test", "appstudio", "appstudio-env", "base1ns", "base1nsnoidling", "base1ns6didler", "base"} + tiersToCheck := []string{"advanced", "baseextendedidling", "baselarge", "test", "appstudio", "appstudio-redhat", "appstudio-env", "base1ns", "base1nsnoidling", "base1ns6didler", "base"} // when the tiers are created during the startup then we can verify them allTiers := &toolchainv1alpha1.NSTemplateTierList{} diff --git a/testsupport/tiers/checks.go b/testsupport/tiers/checks.go index 08e6734b2..b2720fd7a 100644 --- a/testsupport/tiers/checks.go +++ b/testsupport/tiers/checks.go @@ -8,6 +8,7 @@ import ( "testing" toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1" + esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" "github.com/codeready-toolchain/toolchain-e2e/testsupport/wait" "github.com/davecgh/go-spew/spew" @@ -27,6 +28,7 @@ const ( // tier names advanced = "advanced" appstudio = "appstudio" + appstudioRedhat = "appstudio-redhat" appstudioEnv = "appstudio-env" base = "base" base1ns = "base1ns" @@ -78,6 +80,9 @@ func NewChecksForTier(tier *toolchainv1alpha1.NSTemplateTier) (TierChecks, error case appstudio: return &appstudioTierChecks{tierName: appstudio}, nil + case appstudioRedhat: + return &appstudioRedhatTierChecks{tierName: appstudio}, nil + case appstudioEnv: return &appstudioEnvTierChecks{tierName: appstudioEnv}, nil @@ -382,6 +387,12 @@ func commonNetworkPolicyChecks() []namespaceObjectsCheck { } } +func externalSecretChecks() []namespaceObjectsCheck { + return []namespaceObjectsCheck{ + externalSecretSnykSharedToken(), + } +} + type advancedTierChecks struct { baseTierChecks } @@ -458,7 +469,7 @@ func (a *appstudioTierChecks) GetNamespaceObjectChecks(_ string) []namespaceObje pipelineRunnerRoleBinding(), } - checks = append(checks, append(commonNetworkPolicyChecks(), networkPolicyAllowFromCRW(), numberOfNetworkPolicies(6))...) + checks = append(checks, append(commonNetworkPolicyChecks(), externalSecretChecks(), networkPolicyAllowFromCRW(), numberOfNetworkPolicies(6))...) return checks } @@ -531,6 +542,107 @@ func (a *appstudioTierChecks) GetClusterObjectChecks() []clusterObjectsCheck { pipelineRunnerClusterRole()) } +type appstudioRedhatTierChecks struct { + tierName string +} + +func (a *appstudioRedhatTierChecks) GetNamespaceObjectChecks(_ string) []namespaceObjectsCheck { + checks := []namespaceObjectsCheck{ + resourceQuotaComputeDeploy("20", "32Gi", "1750m", "32Gi"), + resourceQuotaComputeBuild("60", "64Gi", "6", "32Gi"), + resourceQuotaStorage("50Gi", "50Gi", "50Gi", "12"), + limitRange("2", "2Gi", "10m", "256Mi"), + numberOfLimitRanges(1), + toolchainSaReadRole(), + memberOperatorSaReadRoleBinding(), + gitOpsServiceLabel(), + appstudioWorkSpaceNameLabel(), + redhatInternalTenantLabel(), + environment("development"), + resourceQuotaAppstudioCrds("512", "512", "512"), + resourceQuotaAppstudioCrdsBuild("512"), + resourceQuotaAppstudioCrdsGitops("512", "512", "512", "512", "512"), + resourceQuotaAppstudioCrdsIntegration("512", "512", "512"), + resourceQuotaAppstudioCrdsRelease("512", "512", "512", "512", "512"), + resourceQuotaAppstudioCrdsEnterpriseContract("512"), + resourceQuotaAppstudioCrdsSPI("512", "512", "512", "512", "512"), + pipelineServiceAccount(), + pipelineRunnerRoleBinding(), + } + + checks = append(checks, append(commonNetworkPolicyChecks(), networkPolicyAllowFromCRW(), numberOfNetworkPolicies(6))...) + return checks +} + +func (a *appstudioRedhatTierChecks) GetSpaceRoleChecks(spaceRoles map[string][]string) ([]spaceRoleObjectsCheck, error) { + checks := []spaceRoleObjectsCheck{} + roles := 0 + rolebindings := 0 + for role, usernames := range spaceRoles { + switch role { + case "admin": + checks = append(checks, appstudioUserActionsRole()) + roles++ + for _, userName := range usernames { + checks = append(checks, + appstudioUserActionsRoleBinding(userName, "admin"), + appstudioViewRoleBinding(userName), + ) + rolebindings += 2 + } + case "maintainer": + checks = append(checks, appstudioMaintainerUserActionsRole()) + roles++ + for _, userName := range usernames { + checks = append(checks, + appstudioUserActionsRoleBinding(userName, "maintainer"), + appstudioViewRoleBinding(userName), + ) + rolebindings += 2 + } + case "contributor": + checks = append(checks, appstudioContributorUserActionsRole()) + roles++ + for _, userName := range usernames { + checks = append(checks, + appstudioUserActionsRoleBinding(userName, "contributor"), + appstudioViewRoleBinding(userName), + ) + rolebindings += 2 + } + default: + return nil, fmt.Errorf("unexpected template name: '%s'", role) + } + } + // also count the roles, rolebindings and service accounts + checks = append(checks, + numberOfToolchainRoles(roles+1), // +1 for `toolchain-sa-read` + numberOfToolchainRoleBindings(rolebindings+2), // +2 for `member-operator-sa-read` and `appstudio-pipelines-runner-rolebinding` + ) + return checks, nil +} + +func (a *appstudioRedhatTierChecks) GetExpectedTemplateRefs(t *testing.T, hostAwait *wait.HostAwaitility) TemplateRefs { + templateRefs := GetTemplateRefs(t, hostAwait, a.tierName) + verifyNsTypes(t, a.tierName, templateRefs, "tenant") + return templateRefs +} + +func (a *appstudioRedhatTierChecks) GetClusterObjectChecks() []clusterObjectsCheck { + return clusterObjectsChecks( + clusterResourceQuotaDeployments("150"), + clusterResourceQuotaReplicas(), + clusterResourceQuotaRoutes(), + clusterResourceQuotaJobs(), + clusterResourceQuotaServices(), + clusterResourceQuotaBuildConfig(), + clusterResourceQuotaSecrets(), + clusterResourceQuotaConfigMap(), + numberOfClusterResourceQuotas(8), + idlers(0, ""), + pipelineRunnerClusterRole()) +} + type appstudioEnvTierChecks struct { tierName string } @@ -955,6 +1067,38 @@ func limitRange(cpuLimit, memoryLimit, cpuRequest, memoryRequest string) namespa } } +func externalSecretSnykSharedToken() namespaceObjectsCheck { + return func(t *testing.T, ns *corev1.Namespace, memberAwait *wait.MemberAwaitility, userName string) { + es, err := memberAwait.WaitForExternalSecret(t, ns, "snyk-shared-token") + require.NoError(t, err) + assert.Equal(t, toolchainv1alpha1.ProviderLabelValue, es.ObjectMeta.Labels[toolchainv1alpha1.ProviderLabelKey]) + expected := &esv1beta1.ExternalSecret{ + Annotations: map[string]string{ + "argocd.argoproj.io/sync-options": "SkipDryRunOnMissingResource=true", + "argocd.argoproj.io/sync-wave": "-1", + }, + }, + Spec: esv1beta1.ExternalSecretSpec{ + DataFrom: []esv1beta1.ExternalSecretDataFromRemoteRef{ + { + Extract: &esv1beta1.ExternalSecretDataRemoteRef{ + Key: "snyk-shared-secret", + }, + }, + }, + SecretStoreRef: esv1beta1.SecretStoreRef{ + Name: "appsre-redhat-tenant-vault", + }, + Target: esv1beta1.ExternalSecretTarget{ + Name: "snyk-secret", + }, + }, + } + + assert.Equal(t, expected.Spec, es.Spec) + } +} + func networkPolicySameNamespace() namespaceObjectsCheck { return func(t *testing.T, ns *corev1.Namespace, memberAwait *wait.MemberAwaitility, userName string) { np, err := memberAwait.WaitForNetworkPolicy(t, ns, "allow-same-namespace") @@ -1452,6 +1596,17 @@ func appstudioWorkSpaceNameLabel() namespaceObjectsCheck { } } +func redhatInternalTenantLabel() namespaceObjectsCheck { + return func(t *testing.T, ns *corev1.Namespace, memberAwait *wait.MemberAwaitility, owner string) { + + labelWaitCriterion := []wait.LabelWaitCriterion{} + labelWaitCriterion = append(labelWaitCriterion, wait.UntilObjectHasLabel("redhat-internal-tenent/label", "true")) + + _, err := memberAwait.WaitForNamespaceWithName(t, ns.Name, labelWaitCriterion...) + require.NoError(t, err) + } +} + func environment(name string) namespaceObjectsCheck { return func(t *testing.T, ns *corev1.Namespace, memberAwait *wait.MemberAwaitility, owner string) { _, err := memberAwait.WaitForEnvironment(t, ns.Name, name, toolchainLabelsWaitCriterion(owner)...) diff --git a/testsupport/wait/member.go b/testsupport/wait/member.go index 5999f46e2..3aed03779 100644 --- a/testsupport/wait/member.go +++ b/testsupport/wait/member.go @@ -12,6 +12,7 @@ import ( "github.com/codeready-toolchain/toolchain-common/pkg/cluster" "github.com/codeready-toolchain/toolchain-common/pkg/test" appstudiov1 "github.com/codeready-toolchain/toolchain-e2e/testsupport/appstudio/api/v1alpha1" + esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" "github.com/davecgh/go-spew/spew" "github.com/ghodss/yaml" quotav1 "github.com/openshift/api/quota/v1" @@ -936,6 +937,31 @@ func (a *MemberAwaitility) WaitForNetworkPolicy(t *testing.T, namespace *corev1. return np, err } +// WaitForExternalSecret waits until a ExternalSecret with the given name exists in the given namespace +func (a *MemberAwaitility) WaitForExternalSecret(t *testing.T, namespace *corev1.Namespace, name string) (*esv1beta1.ExternalSecret, error) { + t.Logf("waiting for ExternalSecret '%s' in namespace '%s'", name, namespace.Name) + es := &esv1beta1.ExternalSecret{} + err := wait.Poll(a.RetryInterval, a.Timeout, func() (done bool, err error) { + obj := &esv1beta1.ExternalSecret{} + if err := a.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace.Name, Name: name}, obj); err != nil { + if errors.IsNotFound(err) { + allESs := &esv1beta1.ExternalSecretList{} + if err := a.Client.List(context.TODO(), allESs, client.InNamespace(namespace)); err != nil { + return false, err + } + return false, nil + } + return false, err + } + es = obj + return true, nil + }) + if err != nil { + t.Logf("failed to wait for ExternalSecret '%s' in namespace '%s'", name, namespace.Name) + } + return es, err +} + // WaitForRole waits until a Role with the given name exists in the given namespace func (a *MemberAwaitility) WaitForRole(t *testing.T, namespace *corev1.Namespace, name string, criteria ...LabelWaitCriterion) (*rbacv1.Role, error) { t.Logf("waiting for Role '%s' in namespace '%s'", name, namespace.Name)