Skip to content

Commit e270e91

Browse files
Merge pull request #537 from rccrdpccl/improve-machine-handling
ACM-30119: improve controlplane machine handling
2 parents 2806c36 + 709c5e0 commit e270e91

6 files changed

Lines changed: 97 additions & 43 deletions

File tree

controlplane-components.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,14 @@ rules:
14111411
- list
14121412
- update
14131413
- watch
1414+
- apiGroups:
1415+
- apiextensions.k8s.io
1416+
resources:
1417+
- customresourcedefinitions
1418+
verbs:
1419+
- get
1420+
- list
1421+
- watch
14141422
- apiGroups:
14151423
- bootstrap.cluster.x-k8s.io
14161424
resources:

controlplane/config/rbac/role.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ rules:
2727
- list
2828
- update
2929
- watch
30+
- apiGroups:
31+
- apiextensions.k8s.io
32+
resources:
33+
- customresourcedefinitions
34+
verbs:
35+
- get
36+
- list
37+
- watch
3038
- apiGroups:
3139
- bootstrap.cluster.x-k8s.io
3240
resources:

controlplane/internal/controller/openshiftassistedcontrolplane_controller.go

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import (
4444
"k8s.io/apimachinery/pkg/api/equality"
4545
apierrors "k8s.io/apimachinery/pkg/api/errors"
4646
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
47+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
4748
"k8s.io/apimachinery/pkg/runtime"
4849
"k8s.io/apimachinery/pkg/types"
4950
kerrors "k8s.io/apimachinery/pkg/util/errors"
@@ -78,6 +79,7 @@ type OpenshiftAssistedControlPlaneReconciler struct {
7879

7980
var minVersion = semver.MustParse(minOpenShiftVersion)
8081

82+
// +kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch
8183
// +kubebuilder:rbac:groups=bootstrap.cluster.x-k8s.io,resources=openshiftassistedconfigs,verbs=get;list;watch;create;update;patch;delete
8284
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=*,verbs=get;list;watch;create;update;patch;delete
8385
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machinedeployments,verbs=get;list;watch
@@ -447,8 +449,8 @@ func (r *OpenshiftAssistedControlPlaneReconciler) computeDesiredMachine(oacp *co
447449
desiredMachine.Spec.Deletion = oacp.Spec.MachineTemplate.Deletion
448450
desiredMachine.Spec.FailureDomain = failureDomain
449451

450-
// Note: by setting the ownerRef on creation we signal to the Machine controller that this is not a stand-alone Machine.
451-
_ = controllerutil.SetOwnerReference(oacp, desiredMachine, r.Scheme)
452+
// Note: by setting the controller ownerRef on creation we signal to the Machine controller that this is not a stand-alone Machine.
453+
_ = controllerutil.SetControllerReference(oacp, desiredMachine, r.Scheme)
452454

453455
// Set the in-place mutable fields.
454456
// When we create a new Machine we will just create the Machine with those fields.
@@ -533,7 +535,6 @@ func (r *OpenshiftAssistedControlPlaneReconciler) reconcileReplicas(ctx context.
533535
numMachines := machines.Len()
534536
desiredReplicas := int(oacp.Spec.Replicas)
535537
machinesToCreate := desiredReplicas - numMachines
536-
var errs []error
537538
if machinesToCreate > 0 {
538539
fd, err := failuredomains.NextFailureDomainForScaleUp(ctx, cluster, machines)
539540
if err != nil {
@@ -560,12 +561,12 @@ func (r *OpenshiftAssistedControlPlaneReconciler) reconcileReplicas(ctx context.
560561
log.V(logutil.DebugLevel).Info("updating replica status", "oacp", oacp, "machines", machines)
561562

562563
r.updateReplicaStatus(ctx, oacp, machines)
563-
return kerrors.NewAggregate(errs)
564+
return nil
564565
}
565566

566567
func (r *OpenshiftAssistedControlPlaneReconciler) scaleUpControlPlane(ctx context.Context, oacp *controlplanev1alpha3.OpenshiftAssistedControlPlane, cluster *clusterv1.Cluster, failureDomain string) (*clusterv1.Machine, error) {
567568
name := names.SimpleNameGenerator.GenerateName(oacp.Name + "-")
568-
machine, err := r.generateMachine(ctx, oacp, name, cluster, failureDomain)
569+
machine, infraObj, err := r.generateMachine(ctx, oacp, name, cluster, failureDomain)
569570
if err != nil {
570571
return nil, err
571572
}
@@ -574,6 +575,9 @@ func (r *OpenshiftAssistedControlPlaneReconciler) scaleUpControlPlane(ctx contex
574575
if err := r.Create(ctx, bootstrapConfig); err != nil {
575576
setConditionFalse(oacp, controlplanev1alpha3.MachinesCreatedCondition, controlplanev1alpha3.BootstrapTemplateCloningFailedReason,
576577
"error creating bootstrap config: %v", err)
578+
if deleteInfraErr := r.Delete(ctx, infraObj); deleteInfraErr != nil {
579+
err = errors.Join(err, deleteInfraErr)
580+
}
577581
return nil, err
578582
}
579583
machine.Spec.Bootstrap.ConfigRef = clusterv1.ContractVersionedObjectReference{
@@ -587,9 +591,8 @@ func (r *OpenshiftAssistedControlPlaneReconciler) scaleUpControlPlane(ctx contex
587591
if deleteBootstrapErr := r.Delete(ctx, bootstrapConfig); deleteBootstrapErr != nil {
588592
err = errors.Join(err, deleteBootstrapErr)
589593
}
590-
infraRefKey := &corev1.ObjectReference{Name: machine.Spec.InfrastructureRef.Name, Namespace: machine.Namespace}
591-
if deleteInfraRefErr := external.Delete(ctx, r.Client, infraRefKey); deleteInfraRefErr != nil {
592-
err = errors.Join(err, deleteInfraRefErr)
594+
if deleteInfraErr := r.Delete(ctx, infraObj); deleteInfraErr != nil {
595+
err = errors.Join(err, deleteInfraErr)
593596
}
594597
return nil, err
595598
}
@@ -697,18 +700,17 @@ func (r *OpenshiftAssistedControlPlaneReconciler) updateReplicaStatus(ctx contex
697700
}
698701
}
699702

700-
func (r *OpenshiftAssistedControlPlaneReconciler) generateMachine(ctx context.Context, oacp *controlplanev1alpha3.OpenshiftAssistedControlPlane, name string, cluster *clusterv1.Cluster, failureDomain string) (*clusterv1.Machine, error) {
701-
// Compute desired Machine
703+
func (r *OpenshiftAssistedControlPlaneReconciler) generateMachine(ctx context.Context, oacp *controlplanev1alpha3.OpenshiftAssistedControlPlane, name string, cluster *clusterv1.Cluster, failureDomain string) (*clusterv1.Machine, *unstructured.Unstructured, error) {
702704
machine := r.computeDesiredMachine(oacp, name, cluster, failureDomain)
703-
infraRef, err := r.computeInfraRef(ctx, oacp, machine.Name, cluster.Name)
705+
infraObj, infraRef, err := r.createInfraMachine(ctx, oacp, machine.Name, cluster.Name)
704706
if err != nil {
705-
return nil, err
707+
return nil, nil, err
706708
}
707709
machine.Spec.InfrastructureRef = infraRef
708-
return machine, nil
710+
return machine, infraObj, nil
709711
}
710712

711-
func (r *OpenshiftAssistedControlPlaneReconciler) computeInfraRef(ctx context.Context, oacp *controlplanev1alpha3.OpenshiftAssistedControlPlane, machineName, clusterName string) (clusterv1.ContractVersionedObjectReference, error) {
713+
func (r *OpenshiftAssistedControlPlaneReconciler) createInfraMachine(ctx context.Context, oacp *controlplanev1alpha3.OpenshiftAssistedControlPlane, machineName, clusterName string) (*unstructured.Unstructured, clusterv1.ContractVersionedObjectReference, error) {
712714
// Since the cloned resource should eventually have a controller ref for the Machine, we create an
713715
// OwnerReference here without the Controller field set
714716
infraCloneOwner := &metav1.OwnerReference{
@@ -718,13 +720,24 @@ func (r *OpenshiftAssistedControlPlaneReconciler) computeInfraRef(ctx context.Co
718720
UID: oacp.UID,
719721
}
720722

721-
// Convert ContractVersionedObjectReference to corev1.ObjectReference for external.CreateFromTemplate
722-
// The external package expects corev1.ObjectReference with APIVersion
723-
templateRef := contractVersionedRefToObjectRef(&oacp.Spec.MachineTemplate.InfrastructureRef, oacp.Namespace)
723+
// Fetch the infrastructure template using contract-based API version resolution
724+
// instead of hardcoding an API version
725+
template, err := external.GetObjectFromContractVersionedRef(ctx, r.Client, oacp.Spec.MachineTemplate.InfrastructureRef, oacp.Namespace)
726+
if err != nil {
727+
setConditionFalse(oacp, controlplanev1alpha3.MachinesCreatedCondition, controlplanev1alpha3.InfrastructureTemplateCloningFailedReason,
728+
"error fetching infrastructure template: %v", err)
729+
return nil, clusterv1.ContractVersionedObjectReference{}, err
730+
}
731+
732+
templateRef := &corev1.ObjectReference{
733+
APIVersion: template.GetAPIVersion(),
734+
Kind: template.GetKind(),
735+
Name: template.GetName(),
736+
Namespace: template.GetNamespace(),
737+
}
724738

725-
// Clone the infrastructure template
726-
_, infraRef, err := external.CreateFromTemplate(ctx, &external.CreateFromTemplateInput{
727-
Client: r.Client,
739+
infraMachine, err := external.GenerateTemplate(&external.GenerateTemplateInput{
740+
Template: template,
728741
TemplateRef: templateRef,
729742
Namespace: oacp.Namespace,
730743
Name: machineName,
@@ -734,12 +747,22 @@ func (r *OpenshiftAssistedControlPlaneReconciler) computeInfraRef(ctx context.Co
734747
Annotations: oacp.Spec.MachineTemplate.ObjectMeta.Annotations,
735748
})
736749
if err != nil {
737-
// Safe to return early here since no resources have been created yet.
738750
setConditionFalse(oacp, controlplanev1alpha3.MachinesCreatedCondition, controlplanev1alpha3.InfrastructureTemplateCloningFailedReason,
739-
"error creating infraenv: %v", err)
740-
return clusterv1.ContractVersionedObjectReference{}, err
751+
"error generating infrastructure clone: %v", err)
752+
return nil, clusterv1.ContractVersionedObjectReference{}, err
753+
}
754+
755+
if err := r.Create(ctx, infraMachine); err != nil {
756+
setConditionFalse(oacp, controlplanev1alpha3.MachinesCreatedCondition, controlplanev1alpha3.InfrastructureTemplateCloningFailedReason,
757+
"error creating infrastructure clone: %v", err)
758+
return nil, clusterv1.ContractVersionedObjectReference{}, err
741759
}
742-
return infraRef, nil
760+
761+
return infraMachine, clusterv1.ContractVersionedObjectReference{
762+
APIGroup: infraMachine.GroupVersionKind().Group,
763+
Kind: infraMachine.GetKind(),
764+
Name: infraMachine.GetName(),
765+
}, nil
743766
}
744767

745768
func (r *OpenshiftAssistedControlPlaneReconciler) generateOpenshiftAssistedConfig(oacp *controlplanev1alpha3.OpenshiftAssistedControlPlane, clusterName string, name string) *bootstrapv1alpha2.OpenshiftAssistedConfig {
@@ -828,24 +851,6 @@ func selectMachineForScaleDown(eligibleMachines collections.Machines, failureDom
828851
return machineToScaleDown, nil
829852
}
830853

831-
// contractVersionedRefToObjectRef converts ContractVersionedObjectReference (with APIGroup)
832-
// to corev1.ObjectReference (with APIVersion) for use with external.CreateFromTemplate.
833-
// Since ContractVersionedObjectReference only has APIGroup, we use the contract version convention
834-
// and default to v1beta1 for infrastructure providers.
835-
func contractVersionedRefToObjectRef(in *clusterv1.ContractVersionedObjectReference, namespace string) *corev1.ObjectReference {
836-
apiVersion := ""
837-
if in.APIGroup != "" {
838-
// Default to v1beta1 for infrastructure group, as that's the common convention
839-
apiVersion = in.APIGroup + "/v1beta1"
840-
}
841-
return &corev1.ObjectReference{
842-
Kind: in.Kind,
843-
Name: in.Name,
844-
Namespace: namespace,
845-
APIVersion: apiVersion,
846-
}
847-
}
848-
849854
// Condition helper functions for setting metav1.Condition (new format).
850855

851856
// setConditionTrue sets a condition to True.

controlplane/internal/controller/openshiftassistedcontrolplane_controller_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import (
4848

4949
testutils "github.com/openshift-assisted/cluster-api-provider-openshift-assisted/test/utils"
5050
corev1 "k8s.io/api/core/v1"
51+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
5152
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5253
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
5354
)
@@ -597,6 +598,7 @@ var _ = Describe("Scale operations and machine updates", func() {
597598
k8sVersion := "1.30.0"
598599
mockKubernetesVersionDetector.EXPECT().GetKubernetesVersion(gomock.Any(), gomock.Any()).Return(&k8sVersion, nil).AnyTimes()
599600

601+
Expect(k8sClient.Create(ctx, getMetal3MachineTemplateCRD())).To(Succeed())
600602
machineTemplate := getMachineTemplate("infratemplate", namespace)
601603
Expect(k8sClient.Create(ctx, &machineTemplate)).To(Succeed())
602604

@@ -1062,7 +1064,6 @@ var _ = Describe("Scale operations and machine updates", func() {
10621064
})
10631065
})
10641066

1065-
// Create dummy machine template
10661067
func getMachineTemplate(name string, namespace string) metal3v1beta1.Metal3MachineTemplate {
10671068
return metal3v1beta1.Metal3MachineTemplate{
10681069
TypeMeta: metav1.TypeMeta{
@@ -1077,6 +1078,28 @@ func getMachineTemplate(name string, namespace string) metal3v1beta1.Metal3Machi
10771078
}
10781079
}
10791080

1081+
func getMetal3MachineTemplateCRD() *apiextensionsv1.CustomResourceDefinition {
1082+
return &apiextensionsv1.CustomResourceDefinition{
1083+
ObjectMeta: metav1.ObjectMeta{
1084+
Name: "metal3machinetemplates.infrastructure.cluster.x-k8s.io",
1085+
Labels: map[string]string{
1086+
"cluster.x-k8s.io/v1beta1": "v1beta1",
1087+
},
1088+
},
1089+
Spec: apiextensionsv1.CustomResourceDefinitionSpec{
1090+
Group: "infrastructure.cluster.x-k8s.io",
1091+
Names: apiextensionsv1.CustomResourceDefinitionNames{
1092+
Kind: "Metal3MachineTemplate",
1093+
Plural: "metal3machinetemplates",
1094+
},
1095+
Scope: apiextensionsv1.NamespaceScoped,
1096+
Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
1097+
{Name: "v1beta1", Served: true, Storage: true},
1098+
},
1099+
},
1100+
}
1101+
}
1102+
10801103
func checkReadyConditions(expectedReadyConditions []clusterv1.ConditionType, openshiftAssistedControlPlane *controlplanev1alpha3.OpenshiftAssistedControlPlane) {
10811104
for _, conditionType := range expectedReadyConditions {
10821105
By(fmt.Sprintf("checking condition %s ready", conditionType))

controlplane/internal/controller/suite_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
metal3v1beta1 "github.com/metal3-io/cluster-api-provider-metal3/api/v1beta1"
2323
bootstrapv1alpha2 "github.com/openshift-assisted/cluster-api-provider-openshift-assisted/bootstrap/api/v1alpha2"
2424

25+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2526
"k8s.io/apimachinery/pkg/runtime"
2627
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2728

@@ -63,5 +64,6 @@ var _ = BeforeSuite(func() {
6364
utilruntime.Must(hiveext.AddToScheme(testScheme))
6465
utilruntime.Must(metal3v1beta1.AddToScheme(testScheme))
6566
utilruntime.Must(bootstrapv1alpha2.AddToScheme(testScheme))
67+
utilruntime.Must(apiextensionsv1.AddToScheme(testScheme))
6668

6769
})

test/e2e/manifests/capcoa/controlplane_install.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,14 @@ rules:
14111411
- list
14121412
- update
14131413
- watch
1414+
- apiGroups:
1415+
- apiextensions.k8s.io
1416+
resources:
1417+
- customresourcedefinitions
1418+
verbs:
1419+
- get
1420+
- list
1421+
- watch
14141422
- apiGroups:
14151423
- bootstrap.cluster.x-k8s.io
14161424
resources:

0 commit comments

Comments
 (0)