Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions e2e-tests/tests/tier0/mta_871_multiple_crbs_split_apply_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package e2e

import (
"log"
"path/filepath"

"github.com/konveyor/crane/e2e-tests/config"
. "github.com/konveyor/crane/e2e-tests/framework"
"github.com/konveyor/crane/e2e-tests/utils"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Namespace-admin cluster-level migration", func() {
It("[MTA-871]Should migrate workload with one CR and two CRBs using split apply", Label("tier0"), func() {
appName := "simple-nginx-nopv"
namespace := "simple-nginx-nopv"
serviceName := "my-" + appName
scenario := NewMigrationScenario(
appName,
namespace,
config.K8sDeployBin,
config.CraneBin,
config.SourceContext,
config.TargetContext,
)
srcAppNonAdmin := scenario.SrcAppNonAdmin
tgtAppNonAdmin := scenario.TgtAppNonAdmin

srcAppNonAdmin.ExtraVars = map[string]any{
"non_admin_user": "true",
}
tgtAppNonAdmin.ExtraVars = map[string]any{
"non_admin_user": "true",
}

kubectlSrc := scenario.KubectlSrc
kubectlTgt := scenario.KubectlTgt
deniedResources := []string{"clusterrolebindings.yaml"}
if !kubectlSrc.IsOpenShift() {
deniedResources = append(deniedResources, "clusterroles.yaml")
}
paths, err := NewScenarioPaths("crane-na1-*")
Expect(err).NotTo(HaveOccurred())
NonAdminRunner := scenario.CraneNonAdmin
adminRunner := scenario.Crane

exportOpts := ExportOptions{Namespace: srcAppNonAdmin.Namespace, ExportDir: paths.ExportDir}
transformOpts := TransformOptions{ExportDir: paths.ExportDir, TransformDir: paths.TransformDir}
applyOpts := ApplyOptions{TransformDir: paths.TransformDir,
OutputDir: paths.OutputDir}
cr := ClusterRole{Name: "crane-cluster-role", Verb: "get,list,watch", Resource: "pods"}
firstCrb := ClusterRoleBinding{Name: "first-crb", ClusterRoleName: cr.Name}
secondCrb := ClusterRoleBinding{Name: "second-crb", ClusterRoleName: cr.Name}
firstSa := ServiceAccount{Name: "first-nginx-sa", Namespace: namespace}
secondSa := ServiceAccount{Name: "second-nginx-sa", Namespace: namespace}
clusterResourcesMatch := []utils.ResourceMatch{
{Kind: "ClusterRoleBinding", Name: firstCrb.Name},
{Kind: "ClusterRoleBinding", Name: secondCrb.Name},
{Kind: "ClusterRole", Name: cr.Name},
}
By("Granting namespace-admin permissions to non-admin user on source and target")
kubectlSrcNonAdmin, kubectlTgtNonAdmin, rbacCleanup, err := SetupActiveKubectlRunners(scenario, namespace)
Expect(err).NotTo(HaveOccurred())

DeferCleanup(func() {
By("Delete test namespace on source and target (wait for completion)")
for _, k := range []KubectlRunner{scenario.KubectlSrc, scenario.KubectlTgt} {
if _, err := k.Run("delete", "namespace", namespace, "--ignore-not-found=true", "--wait=true"); err != nil {
log.Printf("cleanup: failed to delete namespace %q on context %q: %v", namespace, k.Context, err)
}
}
})
DeferCleanup(rbacCleanup)
DeferCleanup(func() {
if err := ResourceCleanup(
[]KubectlRunner{kubectlSrc, kubectlTgt}, []Resource{firstCrb, secondCrb, cr, firstSa, secondSa}); err != nil {
log.Printf("Resources cleanup: %v", err)
}
if err := CleanupScenario(paths.TempDir, srcAppNonAdmin, tgtAppNonAdmin); err != nil {
log.Printf("Scenario cleanup: %v", err)
}

})

By("Deploying app as namespace-admin on source cluster")
Expect(PrepareSourceApp(srcAppNonAdmin, kubectlSrcNonAdmin)).NotTo(HaveOccurred())

By("Creating first Service-Account on namespace")
Expect(firstSa.Create(kubectlSrc)).NotTo(HaveOccurred())

By("Creating second Service-Account on namespace")
Expect(secondSa.Create(kubectlSrc)).NotTo(HaveOccurred())

By("Creating ClusterRole")
Expect(cr.Create(kubectlSrc)).NotTo(HaveOccurred())

By("Creating the first crb ClusterRoleBinding")
Expect(firstCrb.Create(kubectlSrc)).NotTo(HaveOccurred())

By("Creating the second crb ClusterRoleBinding")
Expect(secondCrb.Create(kubectlSrc)).NotTo(HaveOccurred())

By("first crb: Bind Relevant Service-Account to cluster role")
Expect(firstCrb.AddSubject(kubectlSrc, firstSa)).NotTo(HaveOccurred())

By("second crb: Bind Relevant Service-Account to cluster role")
Expect(secondCrb.AddSubject(kubectlSrc, secondSa)).NotTo(HaveOccurred())

By("Waiting for source pods and endpoints to drain")
WaitForSourceQuiesce(kubectlSrcNonAdmin, namespace, "app="+appName, serviceName)

By("Namespace admin phase: Running crane export, transform, apply as namespace-admin")
Expect(RunCranePipelineWithChecks(NonAdminRunner, exportOpts, transformOpts, applyOpts)).NotTo(HaveOccurred())

By("Namespace admin phase: Verifying expected cluster-resource failures for the current platform")
Expect(utils.AssertFilesExist(filepath.Join(paths.ExportDir, "failures", namespace), deniedResources)).NotTo(HaveOccurred())

By("Namespace admin phase: Verifying no cluster resources in output _cluster directory")
Expect(utils.AssertNoKindsInOutput(paths.OutputDir, []string{"ClusterRole", "ClusterRoleBinding"})).NotTo(HaveOccurred())

By("Namespace admin phase: Applying namespace resources to target as namespace-admin")
Expect(kubectlTgtNonAdmin.ApplyDir(filepath.Join(paths.OutputDir, "resources", namespace))).NotTo(HaveOccurred())

By("Cluster admin phase: Running crane export, transform, apply as cluster-admin")
//we reuse the same setup so we need to override for the second pipeline run
exportOpts.Overwrite = true
transformOpts.Overwrite = true
applyOpts.Overwrite = true
Expect(RunCranePipelineWithChecks(adminRunner, exportOpts, transformOpts, applyOpts)).NotTo(HaveOccurred())

By("Cluster admin phase: Verifying cluster resources in output _cluster directory after cluster Admin phase")
allPresented, err := utils.AssertResourcesExist(filepath.Join(paths.OutputDir, "resources", "_cluster"), clusterResourcesMatch)
Expect(err).NotTo(HaveOccurred())
Expect(allPresented).To(BeTrue())

By("Cluster admin phase: Applying cluster resources to target as cluster-admin")
Expect(kubectlTgt.ApplyDir(filepath.Join(paths.OutputDir, "resources", "_cluster"))).NotTo(HaveOccurred())
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Expect(ValidateClusterRBAC(kubectlTgt, []ExpectedClusterRoleBinding{
{ClusterRoleBindingName: firstCrb.Name, ClusterRoleName: cr.Name, SubjectName: firstSa.Name},
{ClusterRoleBindingName: secondCrb.Name, ClusterRoleName: cr.Name, SubjectName: secondSa.Name},
})).NotTo(HaveOccurred())
})

})
150 changes: 150 additions & 0 deletions e2e-tests/tests/tier0/mta_872_crd_split_apply_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package e2e

import (
"log"
"path/filepath"

"github.com/konveyor/crane/e2e-tests/config"
. "github.com/konveyor/crane/e2e-tests/framework"
"github.com/konveyor/crane/e2e-tests/utils"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Namespace-admin cluster-level migration", func() {
It("[MTA-872] Should migrate CRD + CR with split apply: cluster-admin applies CRD, namespace-admin applies CR", Label("tier0"), func() {
appName := "simple-nginx-nopv"
namespace := "simple-nginx-nopv"
serviceName := "my-" + appName
scenario := NewMigrationScenario(
appName,
namespace,
config.K8sDeployBin,
config.CraneBin,
config.SourceContext,
config.TargetContext,
)
srcAppNonAdmin := scenario.SrcAppNonAdmin
tgtAppNonAdmin := scenario.TgtAppNonAdmin

srcAppNonAdmin.ExtraVars = map[string]any{
"non_admin_user": "true",
}
tgtAppNonAdmin.ExtraVars = map[string]any{
"non_admin_user": "true",
}

kubectlSrc := scenario.KubectlSrc
kubectlTgt := scenario.KubectlTgt
crdYAML, err := utils.ReadTestdataFile("widget_crd.yaml")
Expect(err).NotTo(HaveOccurred())
crYAML, err := utils.ReadTestdataFile("widget_cr.yaml")
Expect(err).NotTo(HaveOccurred())

crd := CustomResourceDefinition{
Name: "widgets.crane-e2e.example.com",
YAML: crdYAML,
}
cr := CustomResource{
Name: "test-widget",
Namespace: namespace,
Kind: "Widget",
YAML: crYAML,
Resource: "widgets",
}
tgtNameSpace := Namespace{Name: namespace}
paths, err := NewScenarioPaths("crane-*")
Expect(err).NotTo(HaveOccurred())

runner := scenario.Crane

exportOpts := ExportOptions{Namespace: srcAppNonAdmin.Namespace, ExportDir: paths.ExportDir}
transformOpts := TransformOptions{ExportDir: paths.ExportDir, TransformDir: paths.TransformDir}
applyOpts := ApplyOptions{TransformDir: paths.TransformDir,
OutputDir: paths.OutputDir}

By("Granting namespace-admin permissions to non-admin user on source and target")
kubectlSrcNonAdmin, kubectlTgtNonAdmin, rbacCleanup, err := SetupActiveKubectlRunners(scenario, namespace)
Expect(err).NotTo(HaveOccurred())

DeferCleanup(rbacCleanup)

DeferCleanup(func() {
if err := ResourceCleanup(
Comment thread
RanWurmbrand marked this conversation as resolved.
[]KubectlRunner{kubectlSrc, kubectlTgt}, []Resource{cr, crd, tgtNameSpace}); err != nil {
log.Printf("Resources cleanup: %v", err)
}
if err := CleanupScenario(paths.TempDir, srcAppNonAdmin, tgtAppNonAdmin); err != nil {
log.Printf("Scenario cleanup: %v", err)
}

})
By("Deploying app as namespace-admin on source cluster")
err = PrepareSourceApp(srcAppNonAdmin, kubectlSrcNonAdmin)
Expect(err).NotTo(HaveOccurred())

By("Creating Widget CRD as cluster-admin")
Expect(crd.Create(kubectlSrc)).NotTo(HaveOccurred())

By("Waiting for CRD to be established")
Expect(crd.WaitForEstablished(kubectlSrc)).NotTo(HaveOccurred())

By("Creating Widget custom resource as cluster-admin")
Expect(cr.Create(kubectlSrc)).NotTo(HaveOccurred())

By("Waiting for source pods and endpoints to drain")
WaitForSourceQuiesce(kubectlSrc, namespace, "app="+appName, serviceName)

By("Running crane export, transform, apply as cluster-admin")
Expect(RunCranePipelineWithChecks(runner, exportOpts, transformOpts, applyOpts)).NotTo(HaveOccurred())

By("Verifying CRD exists in export _cluster directory")
isCrdPresented, err := utils.AssertResourcesExist(filepath.Join(paths.ExportDir, "resources", namespace, "_cluster"),
[]utils.ResourceMatch{
{Kind: "CustomResourceDefinition", Name: crd.Name},
})
Expect(err).NotTo(HaveOccurred())
Expect(isCrdPresented).To(BeTrue())
Comment thread
coderabbitai[bot] marked this conversation as resolved.

By("Verifying Widget CR exists in namespace export directory")
isCrPresented, err := utils.AssertResourcesExist(filepath.Join(paths.ExportDir, "resources", namespace),
[]utils.ResourceMatch{
{Kind: cr.Kind, Name: cr.Name, Scope: namespace},
})
Expect(err).NotTo(HaveOccurred())
Expect(isCrPresented).To(BeTrue())

By("Creating namespace on target cluster")
Expect(tgtNameSpace.Create(kubectlTgt)).NotTo(HaveOccurred())

By("Applying CRD to target as cluster-admin")
Expect(kubectlTgt.ApplyDir(filepath.Join(paths.OutputDir, "resources", "_cluster"))).NotTo(HaveOccurred())

By("Waiting for CRD to be established on target")
Expect(crd.WaitForEstablished(kubectlTgt)).NotTo(HaveOccurred())

By("Granting namespace-admin permission to manage widgets on target")
_, err = kubectlTgt.Run("create", "role", "widget-admin", "-n", namespace,
"--verb=*", "--resource=widgets.crane-e2e.example.com")
Expect(err).NotTo(HaveOccurred())
_, err = kubectlTgt.Run("create", "rolebinding", "widget-admin-binding", "-n", namespace,
"--role=widget-admin", "--user=dev")
Expect(err).NotTo(HaveOccurred())

By("Applying namespace resources to target as namespace-admin")
Expect(kubectlTgtNonAdmin.ApplyDir(filepath.Join(paths.OutputDir, "resources", namespace))).NotTo(HaveOccurred())

By("Verifying Widget CR exists on target")
_, err = kubectlTgtNonAdmin.Run("get", "widget", "test-widget", "-n", namespace)
Expect(err).NotTo(HaveOccurred())

By("Verifying Widget CR has correct spec values on target")
Expect(cr.AssertField(kubectlTgtNonAdmin, "{.spec.color}", "blue")).NotTo(HaveOccurred())

By("Scaling target deployment and validating app")
Expect(kubectlTgtNonAdmin.ScaleDeployment(namespace, appName, 1)).NotTo(HaveOccurred())
Eventually(tgtAppNonAdmin.Validate, "2m", "10s").NotTo(HaveOccurred())

})

})
Loading
Loading