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
286 changes: 158 additions & 128 deletions test/e2e/quick_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"

Check failure on line 30 in test/e2e/quick_start.go

View workflow job for this annotation

GitHub Actions / lint (test)

import "k8s.io/apimachinery/pkg/api/errors" imported without alias but must be with alias "apierrors" according to config (importas)
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"

"sigs.k8s.io/cluster-api/test/e2e/internal/log"
"sigs.k8s.io/cluster-api/test/framework"
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
"sigs.k8s.io/cluster-api/util"
Expand Down Expand Up @@ -111,156 +114,183 @@
clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult
)

BeforeEach(func() {
Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName)
input = inputGetter()
Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName)
Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName)
Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName)
Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName)
for i := 0; i < 20; i++ {

Check failure on line 117 in test/e2e/quick_start.go

View workflow job for this annotation

GitHub Actions / lint (test)

for loop can be changed to use an integer range (Go 1.22+) (intrange)

Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersion))
BeforeEach(func() {
Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName)
input = inputGetter()
Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName)
Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName)
Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName)
Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName)

if input.ExtensionServiceNamespace != "" && input.ExtensionServiceName != "" {
if input.ExtensionConfigName == "" {
input.ExtensionConfigName = specName
Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersion))

if input.ExtensionServiceNamespace != "" && input.ExtensionServiceName != "" {
if input.ExtensionConfigName == "" {
input.ExtensionConfigName = specName
}
}
}

// Setup a Namespace where to host objects for this spec and create a watcher for the namespace events.
namespace, cancelWatches = framework.SetupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder, input.PostNamespaceCreated)
// Setup a Namespace where to host objects for this spec and create a watcher for the namespace events.
namespace, cancelWatches = framework.SetupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder, input.PostNamespaceCreated)

if input.DeployClusterClassInSeparateNamespace {
clusterClassNamespace = framework.CreateNamespace(ctx, framework.CreateNamespaceInput{Creator: input.BootstrapClusterProxy.GetClient(), Name: fmt.Sprintf("%s-clusterclass", namespace.Name)}, "40s", "10s")
Expect(clusterClassNamespace).ToNot(BeNil(), "Failed to create namespace")
}
if input.DeployClusterClassInSeparateNamespace {
clusterClassNamespace = framework.CreateNamespace(ctx, framework.CreateNamespaceInput{Creator: input.BootstrapClusterProxy.GetClient(), Name: fmt.Sprintf("%s-clusterclass", namespace.Name)}, "40s", "10s")
Expect(clusterClassNamespace).ToNot(BeNil(), "Failed to create namespace")
}

clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult)
})
clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult)
})

It("Should create a workload cluster", func() {
By("Creating a workload cluster")
It("Should create a workload cluster", func() {
By("Creating a workload cluster")

infrastructureProvider := clusterctl.DefaultInfrastructureProvider
if input.InfrastructureProvider != nil {
infrastructureProvider = *input.InfrastructureProvider
}
infrastructureProvider := clusterctl.DefaultInfrastructureProvider
if input.InfrastructureProvider != nil {
infrastructureProvider = *input.InfrastructureProvider
}

flavor := clusterctl.DefaultFlavor
if input.Flavor != nil {
flavor = *input.Flavor
}
flavor := clusterctl.DefaultFlavor
if input.Flavor != nil {
flavor = *input.Flavor
}

controlPlaneMachineCount := ptr.To[int64](1)
if input.ControlPlaneMachineCount != nil {
controlPlaneMachineCount = input.ControlPlaneMachineCount
}
controlPlaneMachineCount := ptr.To[int64](1)
if input.ControlPlaneMachineCount != nil {
controlPlaneMachineCount = input.ControlPlaneMachineCount
}

workerMachineCount := ptr.To[int64](1)
if input.WorkerMachineCount != nil {
workerMachineCount = input.WorkerMachineCount
}
workerMachineCount := ptr.To[int64](1)
if input.WorkerMachineCount != nil {
workerMachineCount = input.WorkerMachineCount
}

clusterName := fmt.Sprintf("%s-%s", specName, util.RandomString(6))
if input.ClusterName != nil {
clusterName = *input.ClusterName
}
clusterName := fmt.Sprintf("%s-%s", specName, util.RandomString(6))
if input.ClusterName != nil {
clusterName = *input.ClusterName
}

if input.ExtensionServiceNamespace != "" && input.ExtensionServiceName != "" {
// NOTE: test extension is already deployed in the management cluster. If for any reason in future we want
// to make this test more self-contained this test should be modified in order to create an additional
// management cluster; also the E2E test configuration should be modified introducing something like
// optional:true allowing to define which providers should not be installed by default in
// a management cluster.
By("Deploy Test Extension ExtensionConfig")

// In this test we are defaulting all handlers to non-blocking because we don't expect the handlers to block the
// cluster lifecycle by default. Setting defaultAllHandlersToBlocking to false enforces that the test-extension
// automatically creates the ConfigMap with non-blocking preloaded responses.
defaultAllHandlersToBlocking := false
// select on the current namespace
// This is necessary so in CI this test doesn't influence other tests by enabling lifecycle hooks
// in other test namespaces.
namespaces := []string{namespace.Name}
if input.DeployClusterClassInSeparateNamespace {
// Add the ClusterClass namespace, if the ClusterClass is deployed in a separate namespace.
namespaces = append(namespaces, clusterClassNamespace.Name)
}
extensionConfig := extensionConfig(input.ExtensionConfigName, input.ExtensionServiceNamespace, input.ExtensionServiceName, defaultAllHandlersToBlocking, namespaces...)
Expect(input.BootstrapClusterProxy.GetClient().Create(ctx,
extensionConfig)).
To(Succeed(), "Failed to create the ExtensionConfig")
}

variables := map[string]string{
// This is used to template the name of the ExtensionConfig into the ClusterClass.
"EXTENSION_CONFIG_NAME": input.ExtensionConfigName,
}
maps.Copy(variables, input.ClusterctlVariables)

if input.ExtensionServiceNamespace != "" && input.ExtensionServiceName != "" {
// NOTE: test extension is already deployed in the management cluster. If for any reason in future we want
// to make this test more self-contained this test should be modified in order to create an additional
// management cluster; also the E2E test configuration should be modified introducing something like
// optional:true allowing to define which providers should not be installed by default in
// a management cluster.
By("Deploy Test Extension ExtensionConfig")

// In this test we are defaulting all handlers to non-blocking because we don't expect the handlers to block the
// cluster lifecycle by default. Setting defaultAllHandlersToBlocking to false enforces that the test-extension
// automatically creates the ConfigMap with non-blocking preloaded responses.
defaultAllHandlersToBlocking := false
// select on the current namespace
// This is necessary so in CI this test doesn't influence other tests by enabling lifecycle hooks
// in other test namespaces.
namespaces := []string{namespace.Name}
if input.DeployClusterClassInSeparateNamespace {
// Add the ClusterClass namespace, if the ClusterClass is deployed in a separate namespace.
namespaces = append(namespaces, clusterClassNamespace.Name)
variables["CLUSTER_CLASS_NAMESPACE"] = clusterClassNamespace.Name
By("Creating a cluster referencing a ClusterClass from another namespace")
}
extensionConfig := extensionConfig(input.ExtensionConfigName, input.ExtensionServiceNamespace, input.ExtensionServiceName, defaultAllHandlersToBlocking, namespaces...)
Expect(input.BootstrapClusterProxy.GetClient().Create(ctx,
extensionConfig)).
To(Succeed(), "Failed to create the ExtensionConfig")
}

variables := map[string]string{
// This is used to template the name of the ExtensionConfig into the ClusterClass.
"EXTENSION_CONFIG_NAME": input.ExtensionConfigName,
}
maps.Copy(variables, input.ClusterctlVariables)
clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{
ClusterProxy: input.BootstrapClusterProxy,
ConfigCluster: clusterctl.ConfigClusterInput{
LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()),
ClusterctlConfigPath: input.ClusterctlConfigPath,
ClusterctlVariables: variables,
KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(),
InfrastructureProvider: infrastructureProvider,
Flavor: flavor,
Namespace: namespace.Name,
ClusterName: clusterName,
KubernetesVersion: input.E2EConfig.MustGetVariable(KubernetesVersion),
ControlPlaneMachineCount: controlPlaneMachineCount,
WorkerMachineCount: workerMachineCount,
},
ControlPlaneWaiters: input.ControlPlaneWaiters,
WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"),
WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"),
WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
PostMachinesProvisioned: func() {
if input.PostMachinesProvisioned != nil {
input.PostMachinesProvisioned(input.BootstrapClusterProxy, namespace.Name, clusterName)
}
},
}, clusterResources)

Byf("Verify Cluster Available condition is true")
framework.VerifyClusterAvailable(ctx, framework.VerifyClusterAvailableInput{
Getter: input.BootstrapClusterProxy.GetClient(),
Name: clusterResources.Cluster.Name,
Namespace: clusterResources.Cluster.Namespace,
})

Byf("Verify Machines Ready condition is true")
framework.VerifyMachinesReady(ctx, framework.VerifyMachinesReadyInput{
Lister: input.BootstrapClusterProxy.GetClient(),
Name: clusterResources.Cluster.Name,
Namespace: clusterResources.Cluster.Namespace,
})

By("PASSED!")

if input.DeployClusterClassInSeparateNamespace {
variables["CLUSTER_CLASS_NAMESPACE"] = clusterClassNamespace.Name
By("Creating a cluster referencing a ClusterClass from another namespace")
}
})

Check failure on line 253 in test/e2e/quick_start.go

View workflow job for this annotation

GitHub Actions / lint (test)

unnecessary trailing newline (whitespace)

clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{
ClusterProxy: input.BootstrapClusterProxy,
ConfigCluster: clusterctl.ConfigClusterInput{
LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()),
ClusterctlConfigPath: input.ClusterctlConfigPath,
ClusterctlVariables: variables,
KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(),
InfrastructureProvider: infrastructureProvider,
Flavor: flavor,
Namespace: namespace.Name,
ClusterName: clusterName,
KubernetesVersion: input.E2EConfig.MustGetVariable(KubernetesVersion),
ControlPlaneMachineCount: controlPlaneMachineCount,
WorkerMachineCount: workerMachineCount,
},
ControlPlaneWaiters: input.ControlPlaneWaiters,
WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"),
WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"),
WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
PostMachinesProvisioned: func() {
if input.PostMachinesProvisioned != nil {
input.PostMachinesProvisioned(input.BootstrapClusterProxy, namespace.Name, clusterName)
AfterEach(func() {
// Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself.
framework.DumpSpecResourcesAndCleanup(ctx, specName, input.BootstrapClusterProxy, input.ClusterctlConfigPath, input.ArtifactFolder, namespace, cancelWatches, clusterResources.Cluster, input.E2EConfig.GetIntervals, input.SkipCleanup)
if !input.SkipCleanup {
if input.ExtensionServiceNamespace != "" && input.ExtensionServiceName != "" {
Eventually(func() error {
return input.BootstrapClusterProxy.GetClient().Delete(ctx, extensionConfig(input.ExtensionConfigName, input.ExtensionServiceNamespace, input.ExtensionServiceName, true))
}, 10*time.Second, 1*time.Second).Should(Succeed(), "Deleting ExtensionConfig failed")
}
if input.DeployClusterClassInSeparateNamespace {
DeleteNamespace11(ctx, framework.DeleteNamespaceInput{
Deleter: input.BootstrapClusterProxy.GetClient(),
Name: clusterClassNamespace.Name,
})
}
},
}, clusterResources)

Byf("Verify Cluster Available condition is true")
framework.VerifyClusterAvailable(ctx, framework.VerifyClusterAvailableInput{
Getter: input.BootstrapClusterProxy.GetClient(),
Name: clusterResources.Cluster.Name,
Namespace: clusterResources.Cluster.Namespace,
}
})

Byf("Verify Machines Ready condition is true")
framework.VerifyMachinesReady(ctx, framework.VerifyMachinesReadyInput{
Lister: input.BootstrapClusterProxy.GetClient(),
Name: clusterResources.Cluster.Name,
Namespace: clusterResources.Cluster.Namespace,
})
}

Check failure on line 273 in test/e2e/quick_start.go

View workflow job for this annotation

GitHub Actions / lint (test)

unnecessary trailing newline (whitespace)
}

By("PASSED!")
})
// DeleteNamespace is used to delete namespace object.

Check failure on line 276 in test/e2e/quick_start.go

View workflow job for this annotation

GitHub Actions / lint (test)

exported: comment on exported function DeleteNamespace11 should be of the form "DeleteNamespace11 ..." (revive)
func DeleteNamespace11(ctx context.Context, input framework.DeleteNamespaceInput, intervals ...interface{}) {
Expect(ctx).NotTo(BeNil(), "ctx is required for DeleteNamespace")
Expect(input.Deleter).NotTo(BeNil(), "input.Deleter is required for DeleteNamespace")
Expect(input.Name).NotTo(BeEmpty(), "input.Name is required for DeleteNamespace")
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: input.Name,
},
}
log.Logf("Deleting namespace %s", input.Name)
Eventually(func() error {
err := input.Deleter.Delete(ctx, ns)
if err != nil && !errors.IsNotFound(err) {

Check failure on line 289 in test/e2e/quick_start.go

View workflow job for this annotation

GitHub Actions / lint (test)

unnecessary leading newline (whitespace)

return err

AfterEach(func() {
// Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself.
framework.DumpSpecResourcesAndCleanup(ctx, specName, input.BootstrapClusterProxy, input.ClusterctlConfigPath, input.ArtifactFolder, namespace, cancelWatches, clusterResources.Cluster, input.E2EConfig.GetIntervals, input.SkipCleanup)
if !input.SkipCleanup {
if input.ExtensionServiceNamespace != "" && input.ExtensionServiceName != "" {
Eventually(func() error {
return input.BootstrapClusterProxy.GetClient().Delete(ctx, extensionConfig(input.ExtensionConfigName, input.ExtensionServiceNamespace, input.ExtensionServiceName, true))
}, 10*time.Second, 1*time.Second).Should(Succeed(), "Deleting ExtensionConfig failed")
}
if input.DeployClusterClassInSeparateNamespace {
framework.DeleteNamespace(ctx, framework.DeleteNamespaceInput{
Deleter: input.BootstrapClusterProxy.GetClient(),
Name: clusterClassNamespace.Name,
})
}
}
})
return nil
}, intervals...).Should(Succeed(), "Failed to delete namespace %s", input.Name)
}
4 changes: 2 additions & 2 deletions test/e2e/quick_start_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
"sigs.k8s.io/cluster-api/test/framework/kubetest"
)

var _ = Describe("When following the Cluster API quick-start", func() {
var _ = Describe("When following the Cluster API quick-start11 [PR-Blocking] [ClusterClass]", Label("PR-Blocking", "ClusterClass"), func() {
QuickStartSpec(ctx, func() QuickStartSpecInput {
return QuickStartSpecInput{
E2EConfig: e2eConfig,
Expand Down Expand Up @@ -75,7 +75,7 @@ var _ = Describe("When following the Cluster API quick-start", func() {
})
})

var _ = Describe("When following the Cluster API quick-start with ClusterClass [PR-Blocking] [ClusterClass]", Label("PR-Blocking", "ClusterClass"), func() {
var _ = Describe("When following the Cluster API quick-start with ClusterClass ", func() {
QuickStartSpec(ctx, func() QuickStartSpecInput {
return QuickStartSpecInput{
E2EConfig: e2eConfig,
Expand Down
Loading