diff --git a/cmd/clusterctl/client/cluster/objectgraph.go b/cmd/clusterctl/client/cluster/objectgraph.go index 0ea957e59478..2705a67c78fe 100644 --- a/cmd/clusterctl/client/cluster/objectgraph.go +++ b/cmd/clusterctl/client/cluster/objectgraph.go @@ -288,6 +288,9 @@ func (o *objectGraph) getDiscoveryTypes() error { if crd.Spec.Group == clusterv1.GroupVersion.Group && crd.Spec.Names.Kind == "Cluster" { forceMoveHierarchy = true } + if crd.Spec.Group == clusterv1.GroupVersion.Group && crd.Spec.Names.Kind == "ClusterClass" { + forceMoveHierarchy = true + } if crd.Spec.Group == addonsv1.GroupVersion.Group && crd.Spec.Names.Kind == "ClusterResourceSet" { forceMoveHierarchy = true } diff --git a/cmd/clusterctl/client/cluster/objectgraph_test.go b/cmd/clusterctl/client/cluster/objectgraph_test.go index afd7106aeaa6..d8bd4f3bd680 100644 --- a/cmd/clusterctl/client/cluster/objectgraph_test.go +++ b/cmd/clusterctl/client/cluster/objectgraph_test.go @@ -26,8 +26,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" "sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test" + "sigs.k8s.io/cluster-api/internal/builder" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -1318,6 +1320,289 @@ var objectGraphsTests = []struct { }, }, }, + { + name: "ClusterClass", + args: objectGraphTestArgs{ + objs: test.NewFakeClusterClass("ns1", "clusterclass1").Objs(), + }, + want: wantGraph{ + nodes: map[string]wantGraphItem{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1": { + forceMove: true, + forceMoveHierarchy: true, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureClusterTemplate, ns1/clusterclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "controlplane.cluster.x-k8s.io/v1beta1, Kind=GenericControlPlaneTemplate, ns1/clusterclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + }, + }, + }, + { + name: "ClusterClass with control plane infrastructure template", + args: objectGraphTestArgs{ + objs: func() []client.Object { + infrastructureMachineTemplate := builder.InfrastructureMachineTemplate("ns1", "inframachinetemplate1").Build() + return test.NewFakeClusterClass("ns1", "clusterclass1"). + WithControlPlaneInfrastructureTemplate(infrastructureMachineTemplate). + Objs() + }(), + }, + want: wantGraph{ + nodes: map[string]wantGraphItem{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1": { + forceMove: true, + forceMoveHierarchy: true, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureClusterTemplate, ns1/clusterclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "controlplane.cluster.x-k8s.io/v1beta1, Kind=GenericControlPlaneTemplate, ns1/clusterclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureMachineTemplate, ns1/inframachinetemplate1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + }, + }, + }, + { + name: "ClusterClass with worker machine deployment class", + args: objectGraphTestArgs{ + objs: func() []client.Object { + mdClass := test.NewFakeMachineDeploymentClass("ns1", "mdclass1") + return test.NewFakeClusterClass("ns1", "clusterclass1"). + WithWorkerMachineDeploymentClasses([]*test.FakeMachineDeploymentClass{mdClass}). + Objs() + }(), + }, + want: wantGraph{ + nodes: map[string]wantGraphItem{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1": { + forceMove: true, + forceMoveHierarchy: true, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureClusterTemplate, ns1/clusterclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "controlplane.cluster.x-k8s.io/v1beta1, Kind=GenericControlPlaneTemplate, ns1/clusterclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureMachineTemplate, ns1/mdclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "bootstrap.cluster.x-k8s.io/v1beta1, Kind=GenericBootstrapConfigTemplate, ns1/mdclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + }, + }, + }, + { + name: "ClusterClass with 2 worker machine deployment classes with shared templates", + args: objectGraphTestArgs{ + objs: func() []client.Object { + infraMachineTemplate := builder.InfrastructureMachineTemplate("ns1", "infamachinetemplate1").Build() + bootstrapTemplate := builder.BootstrapTemplate("ns1", "bootstraptemplate1").Build() + mdClass1 := test.NewFakeMachineDeploymentClass("ns1", "mdclass1"). + WithInfrastructureMachineTemplate(infraMachineTemplate). + WithBootstrapTemplate(bootstrapTemplate) + mdClass2 := test.NewFakeMachineDeploymentClass("ns1", "mdclass2"). + WithInfrastructureMachineTemplate(infraMachineTemplate). + WithBootstrapTemplate(bootstrapTemplate) + return test.NewFakeClusterClass("ns1", "clusterclass1"). + WithWorkerMachineDeploymentClasses([]*test.FakeMachineDeploymentClass{mdClass1, mdClass2}). + Objs() + }(), + }, + want: wantGraph{ + nodes: map[string]wantGraphItem{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1": { + forceMove: true, + forceMoveHierarchy: true, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureClusterTemplate, ns1/clusterclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "controlplane.cluster.x-k8s.io/v1beta1, Kind=GenericControlPlaneTemplate, ns1/clusterclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureMachineTemplate, ns1/infamachinetemplate1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "bootstrap.cluster.x-k8s.io/v1beta1, Kind=GenericBootstrapConfigTemplate, ns1/bootstraptemplate1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + }, + }, + }, + { + name: "Two ClusterClasses", + args: objectGraphTestArgs{ + objs: func() []client.Object { + objs := []client.Object{} + objs = append(objs, test.NewFakeClusterClass("ns1", "clusterclass1").Objs()...) + objs = append(objs, test.NewFakeClusterClass("ns1", "clusterclass2").Objs()...) + return objs + }(), + }, + want: wantGraph{ + nodes: map[string]wantGraphItem{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1": { + forceMove: true, + forceMoveHierarchy: true, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureClusterTemplate, ns1/clusterclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "controlplane.cluster.x-k8s.io/v1beta1, Kind=GenericControlPlaneTemplate, ns1/clusterclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass2": { + forceMove: true, + forceMoveHierarchy: true, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureClusterTemplate, ns1/clusterclass2": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass2", + }, + }, + "controlplane.cluster.x-k8s.io/v1beta1, Kind=GenericControlPlaneTemplate, ns1/clusterclass2": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass2", + }, + }, + }, + }, + }, + { + name: "Two ClusterClasses with shared templates", + args: objectGraphTestArgs{ + objs: func() []client.Object { + objs := []client.Object{} + + infraMachineTemplate1 := builder.InfrastructureMachineTemplate("ns1", "infamachinetemplate1").Build() + bootstrapTemplate1 := builder.BootstrapTemplate("ns1", "bootstraptemplate1").Build() + + infraMachineTemplate2 := builder.InfrastructureMachineTemplate("ns1", "infamachinetemplate2").Build() + bootstrapTemplate2 := builder.BootstrapTemplate("ns1", "bootstraptemplate2").Build() + + controlPlaneTemplate := builder.ControlPlaneTemplate("ns1", "controlplanetemplate1").Build() + + // mdClass1 and mdClass2 share templates. + // mdClass3 does not share templates with any. + mdClass1 := test.NewFakeMachineDeploymentClass("ns1", "mdclass1"). + WithInfrastructureMachineTemplate(infraMachineTemplate1). + WithBootstrapTemplate(bootstrapTemplate1) + mdClass2 := test.NewFakeMachineDeploymentClass("ns1", "mdclass2"). + WithInfrastructureMachineTemplate(infraMachineTemplate1). + WithBootstrapTemplate(bootstrapTemplate1) + mdClass3 := test.NewFakeMachineDeploymentClass("ns1", "mdclass2"). + WithInfrastructureMachineTemplate(infraMachineTemplate2). + WithBootstrapTemplate(bootstrapTemplate2) + + // clusterclass1 and clusterclass2 share the same control plane template but have different + // infrastructure cluster templates. + // clusterclass1 and clusterclass2 share the templates defined in mdclass1. + // clusterclass1 and clusterclass2 do not share the templates in mdclass3. + objs = append(objs, test.NewFakeClusterClass("ns1", "clusterclass1"). + WithControlPlaneTemplate(controlPlaneTemplate). + WithWorkerMachineDeploymentClasses([]*test.FakeMachineDeploymentClass{mdClass1, mdClass2}). + Objs()...) + + objs = append(objs, test.NewFakeClusterClass("ns1", "clusterclass2"). + WithControlPlaneTemplate(controlPlaneTemplate). + WithWorkerMachineDeploymentClasses([]*test.FakeMachineDeploymentClass{mdClass1, mdClass3}). + Objs()...) + + // We need to deduplicate objects here as the clusterclasses share objects and + // setting up the test server panics if we try to create it with duplicate objects. + return deduplicateObjects(objs) + + }(), + }, + want: wantGraph{ + nodes: map[string]wantGraphItem{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1": { + forceMove: true, + forceMoveHierarchy: true, + }, + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass2": { + forceMove: true, + forceMoveHierarchy: true, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureClusterTemplate, ns1/clusterclass1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + }, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureClusterTemplate, ns1/clusterclass2": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass2", + }, + }, + "controlplane.cluster.x-k8s.io/v1beta1, Kind=GenericControlPlaneTemplate, ns1/controlplanetemplate1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass2", + }, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureMachineTemplate, ns1/infamachinetemplate1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass2", + }, + }, + "bootstrap.cluster.x-k8s.io/v1beta1, Kind=GenericBootstrapConfigTemplate, ns1/bootstraptemplate1": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass1", + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass2", + }, + }, + "infrastructure.cluster.x-k8s.io/v1beta1, Kind=GenericInfrastructureMachineTemplate, ns1/infamachinetemplate2": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass2", + }, + }, + "bootstrap.cluster.x-k8s.io/v1beta1, Kind=GenericBootstrapConfigTemplate, ns1/bootstraptemplate2": { + owners: []string{ + "cluster.x-k8s.io/v1beta1, Kind=ClusterClass, ns1/clusterclass2", + }, + }, + }, + }, + }, } func getDetachedObjectGraphWihObjs(objs []client.Object) (*objectGraph, error) { @@ -1333,7 +1618,7 @@ func getDetachedObjectGraphWihObjs(objs []client.Object) (*objectGraph, error) { // given that we are not relying on discovery while testing in "detached mode (without a fake client)" it is required to: for _, node := range graph.getNodes() { // enforce forceMoveHierarchy for Clusters, ClusterResourceSets, GenericClusterInfrastructureIdentity - if node.identity.Kind == "Cluster" || node.identity.Kind == "ClusterResourceSet" || node.identity.Kind == "GenericClusterInfrastructureIdentity" { + if node.identity.Kind == "Cluster" || node.identity.Kind == "ClusterClass" || node.identity.Kind == "ClusterResourceSet" || node.identity.Kind == "GenericClusterInfrastructureIdentity" { node.forceMove = true node.forceMoveHierarchy = true } @@ -2044,3 +2329,15 @@ func Test_objectGraph_setGlobalIdentityTenants(t *testing.T) { }) } } + +func deduplicateObjects(objs []client.Object) []client.Object { + res := []client.Object{} + uniqueObjectKeys := sets.NewString() + for _, o := range objs { + if !uniqueObjectKeys.Has(string(o.GetUID())) { + res = append(res, o) + uniqueObjectKeys.Insert(string(o.GetUID())) + } + } + return res +} diff --git a/cmd/clusterctl/internal/test/fake_objects.go b/cmd/clusterctl/internal/test/fake_objects.go index bfd9500585a6..4ff512d3e4e8 100644 --- a/cmd/clusterctl/internal/test/fake_objects.go +++ b/cmd/clusterctl/internal/test/fake_objects.go @@ -24,6 +24,7 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" @@ -33,7 +34,9 @@ import ( fakeinfrastructure "sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test/providers/infrastructure" addonsv1 "sigs.k8s.io/cluster-api/exp/addons/api/v1beta1" expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" + "sigs.k8s.io/cluster-api/internal/builder" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) type FakeCluster struct { @@ -1354,6 +1357,7 @@ func FakeCRDList() []*apiextensionsv1.CustomResourceDefinition { return []*apiextensionsv1.CustomResourceDefinition{ FakeNamespacedCustomResourceDefinition(clusterv1.GroupVersion.Group, "Cluster", version), + FakeNamespacedCustomResourceDefinition(clusterv1.GroupVersion.Group, "ClusterClass", version), FakeNamespacedCustomResourceDefinition(clusterv1.GroupVersion.Group, "Machine", version), FakeNamespacedCustomResourceDefinition(clusterv1.GroupVersion.Group, "MachineDeployment", version), FakeNamespacedCustomResourceDefinition(clusterv1.GroupVersion.Group, "MachineSet", version), @@ -1361,7 +1365,9 @@ func FakeCRDList() []*apiextensionsv1.CustomResourceDefinition { FakeNamespacedCustomResourceDefinition(addonsv1.GroupVersion.Group, "ClusterResourceSet", version), FakeNamespacedCustomResourceDefinition(addonsv1.GroupVersion.Group, "ClusterResourceSetBinding", version), FakeNamespacedCustomResourceDefinition(fakecontrolplane.GroupVersion.Group, "GenericControlPlane", version), + FakeNamespacedCustomResourceDefinition(fakecontrolplane.GroupVersion.Group, "GenericControlPlaneTemplate", version), FakeNamespacedCustomResourceDefinition(fakeinfrastructure.GroupVersion.Group, "GenericInfrastructureCluster", version), + FakeNamespacedCustomResourceDefinition(fakeinfrastructure.GroupVersion.Group, "GenericInfrastructureClusterTemplate", version), FakeNamespacedCustomResourceDefinition(fakeinfrastructure.GroupVersion.Group, "GenericInfrastructureMachine", version), FakeNamespacedCustomResourceDefinition(fakeinfrastructure.GroupVersion.Group, "GenericInfrastructureMachineTemplate", version), FakeNamespacedCustomResourceDefinition(fakebootstrap.GroupVersion.Group, "GenericBootstrapConfig", version), @@ -1371,3 +1377,136 @@ func FakeCRDList() []*apiextensionsv1.CustomResourceDefinition { clusterInfrastructureIdentityCRD, } } + +type FakeClusterClass struct { + namespace string + name string + infrastructureClusterTemplate *unstructured.Unstructured + controlPlaneTemplate *unstructured.Unstructured + controlPlaneInfrastructureMachineTemplate *unstructured.Unstructured + workerMachineDeploymentClasses []*FakeMachineDeploymentClass +} + +func NewFakeClusterClass(namespace, name string) *FakeClusterClass { + return &FakeClusterClass{ + namespace: namespace, + name: name, + } +} + +func (f *FakeClusterClass) WithInfrastructureClusterTemplate(tmpl *unstructured.Unstructured) *FakeClusterClass { + f.infrastructureClusterTemplate = tmpl + return f +} + +func (f *FakeClusterClass) WithControlPlaneTemplate(tmpl *unstructured.Unstructured) *FakeClusterClass { + f.controlPlaneTemplate = tmpl + return f +} + +func (f *FakeClusterClass) WithControlPlaneInfrastructureTemplate(tmpl *unstructured.Unstructured) *FakeClusterClass { + f.controlPlaneInfrastructureMachineTemplate = tmpl + return f +} + +func (f *FakeClusterClass) WithWorkerMachineDeploymentClasses(classes []*FakeMachineDeploymentClass) *FakeClusterClass { + f.workerMachineDeploymentClasses = classes + return f +} + +func (f *FakeClusterClass) Objs() []client.Object { + // objMap map where the key is the object to which the owner reference to the cluster class should be added + // and the value dictates if the onwner ref needs to be added. + // This map also dual functions as a way to de-duplicate and template objects that are reused. + objMap := map[client.Object]bool{} + + // If no infrastructure cluster template is provided create a generic infrastructure cluster template to use in the cluster class. + if f.infrastructureClusterTemplate == nil { + f.infrastructureClusterTemplate = builder.InfrastructureClusterTemplate(f.namespace, f.name).Build() + } + objMap[f.infrastructureClusterTemplate] = true + + // If no controlplane template is provided create a generic controlplane template to use in the cluster class. + if f.controlPlaneTemplate == nil { + f.controlPlaneTemplate = builder.ControlPlaneTemplate(f.namespace, f.name).Build() + } + objMap[f.controlPlaneTemplate] = true + + clusterClassBuilder := builder.ClusterClass(f.namespace, f.name). + WithInfrastructureClusterTemplate(f.infrastructureClusterTemplate). + WithControlPlaneTemplate(f.controlPlaneTemplate) + + if f.controlPlaneInfrastructureMachineTemplate != nil { + clusterClassBuilder.WithControlPlaneInfrastructureMachineTemplate(f.controlPlaneInfrastructureMachineTemplate) + objMap[f.controlPlaneInfrastructureMachineTemplate] = true + } + + if len(f.workerMachineDeploymentClasses) > 0 { + mdClasses := []clusterv1.MachineDeploymentClass{} + for _, fakeMDClass := range f.workerMachineDeploymentClasses { + mdClasses = append(mdClasses, *fakeMDClass.Obj()) + objMap[fakeMDClass.bootstrapTemplate] = true + objMap[fakeMDClass.infrastructureTemplate] = true + } + clusterClassBuilder.WithWorkerMachineDeploymentClasses(mdClasses) + } + + clusterClass := clusterClassBuilder.Build() + objMap[clusterClass] = false + + for o := range objMap { + setUID(o) + } + + for o, setOwnerReference := range objMap { + if setOwnerReference { + if err := controllerutil.SetOwnerReference(clusterClass, o, FakeScheme); err != nil { + panic(err) + } + } + } + + objs := []client.Object{} + for o := range objMap { + objs = append(objs, o) + } + return objs +} + +type FakeMachineDeploymentClass struct { + name string + namespace string + infrastructureTemplate *unstructured.Unstructured + bootstrapTemplate *unstructured.Unstructured +} + +func NewFakeMachineDeploymentClass(namespace, name string) *FakeMachineDeploymentClass { + return &FakeMachineDeploymentClass{ + namespace: namespace, + name: name, + } +} + +func (f *FakeMachineDeploymentClass) WithInfrastructureMachineTemplate(tmpl *unstructured.Unstructured) *FakeMachineDeploymentClass { + f.infrastructureTemplate = tmpl + return f +} + +func (f *FakeMachineDeploymentClass) WithBootstrapTemplate(tmpl *unstructured.Unstructured) *FakeMachineDeploymentClass { + f.bootstrapTemplate = tmpl + return f +} + +func (f *FakeMachineDeploymentClass) Obj() *clusterv1.MachineDeploymentClass { + if f.infrastructureTemplate == nil { + f.infrastructureTemplate = builder.InfrastructureMachineTemplate(f.namespace, f.name).Build() + } + if f.bootstrapTemplate == nil { + f.bootstrapTemplate = builder.BootstrapTemplate(f.namespace, f.name).Build() + } + + return builder.MachineDeploymentClass(f.namespace, f.name). + WithInfrastructureTemplate(f.infrastructureTemplate). + WithBootstrapTemplate(f.bootstrapTemplate). + Build() +} diff --git a/internal/builder/builders.go b/internal/builder/builders.go index 322936e5bac4..72f9358b1de8 100644 --- a/internal/builder/builders.go +++ b/internal/builder/builders.go @@ -313,8 +313,12 @@ func (m *MachineDeploymentClassBuilder) WithAnnotations(annotations map[string]s // Build creates a full MachineDeploymentClass object with the variables passed to the MachineDeploymentClassBuilder. func (m *MachineDeploymentClassBuilder) Build() *clusterv1.MachineDeploymentClass { + class := m.class + if class == "" { + class = m.name + } return &clusterv1.MachineDeploymentClass{ - Class: m.class, + Class: class, Template: clusterv1.MachineDeploymentClassTemplate{ Metadata: clusterv1.ObjectMeta{ Labels: m.labels,