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
3 changes: 0 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ replace (
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.35.3
k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.35.3
k8s.io/sample-controller => k8s.io/sample-controller v0.35.3

// cluster-api-provider-vsphere v1.14.0 dependends on a cluster-api placeholder
sigs.k8s.io/cluster-api => sigs.k8s.io/cluster-api v1.11.5
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -950,8 +950,8 @@ pkg.package-operator.run/boxcutter v0.13.1 h1:FvrSBnHWuf6Co+HPyxRSw2Y5mHSkUtteW8
pkg.package-operator.run/boxcutter v0.13.1/go.mod h1:rR2jd32uNt2eml7UvlYNCpCAYIpTALDzHxaftMfgkZo=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 h1:qPrZsv1cwQiFeieFlRqT627fVZ+tyfou/+S5S0H5ua0=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
sigs.k8s.io/cluster-api v1.11.5 h1:mKQAfB8+6l2uxtEvQ6Z5EIcTObdPNs3TL4DcJScchGo=
sigs.k8s.io/cluster-api v1.11.5/go.mod h1:n0d6BAo8s9+KRap8Wv/IllfmwnSN5XFTWNJuq2gKNlg=
sigs.k8s.io/cluster-api v1.12.3 h1:cuOl3fWXhlXFuQcyIH4C8i3ns8rLhtcnK+x00MVdKBs=
sigs.k8s.io/cluster-api v1.12.3/go.mod h1:EAiTJtf/8M5eBetPwumi6t8DJJ55Ln6Fkvh2OAa7PD4=
sigs.k8s.io/cluster-api-provider-aws/v2 v2.10.2 h1:jg4R4FGVWLU/oHMsFPoYQHCxyykvixWKKU3k+OuknlE=
sigs.k8s.io/cluster-api-provider-aws/v2 v2.10.2/go.mod h1:pHOUXfzTJ4G5xbvEaal6tK4KqGBI/Ij2Xug4hAR1LGE=
sigs.k8s.io/cluster-api-provider-azure v1.22.1 h1:BAzjKZR6Fzwu4oq0AwAeILT/FoyEzS/uw+tnAxoOpMo=
Expand Down
3 changes: 0 additions & 3 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ use (
)

replace (
// This is required to resolve a placeholder dep in CAPV
sigs.k8s.io/cluster-api => sigs.k8s.io/cluster-api v1.11.5

// Pin these specific provider commits until they are included in
// releases
sigs.k8s.io/cluster-api-provider-aws/v2 => sigs.k8s.io/cluster-api-provider-aws/v2 v2.10.1-0.20260311200936-f3440f3ea2f2
Expand Down
2 changes: 2 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1658,6 +1658,8 @@ sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.9.2/go.mod h1:BgPOvGEdPTyaIWREF
sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.4.1/go.mod h1:rEQnoF3pmD1kmAFQCwA/SqHiiftLFeMwdQt0gsuKWbM=
sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.8.4/go.mod h1:RgIi9n/PhULbvPjYZGsjP2zWJf1ZEd1qyA0CYUuSgcE=
sigs.k8s.io/cluster-api v1.10.2/go.mod h1:/b9Un5Imprib6S7ZOcJitC2ep/5wN72b0pXpMQFfbTw=
sigs.k8s.io/cluster-api v1.11.5 h1:mKQAfB8+6l2uxtEvQ6Z5EIcTObdPNs3TL4DcJScchGo=
sigs.k8s.io/cluster-api v1.11.5/go.mod h1:n0d6BAo8s9+KRap8Wv/IllfmwnSN5XFTWNJuq2gKNlg=
sigs.k8s.io/cluster-api-provider-aws/v2 v2.8.4/go.mod h1:ubZXYyieYt+I4jQFJ47J9Hq4W3zPernTWXlIfIZnB3Y=
sigs.k8s.io/cluster-api-provider-aws/v2 v2.10.1-0.20260311200936-f3440f3ea2f2 h1:lzlMuS1F9qub8hw+fm2oo8UzJmUGZ8t9lwf4m8So93E=
sigs.k8s.io/cluster-api-provider-aws/v2 v2.10.1-0.20260311200936-f3440f3ea2f2/go.mod h1:vbVa187QP4/y7/03JqhYBmou4xabCRnuRoHwhE2BgE8=
Expand Down
19 changes: 19 additions & 0 deletions pkg/conversion/capi2mapi/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"strings"

"github.com/openshift/cluster-capi-operator/pkg/util"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
Expand Down Expand Up @@ -133,6 +134,24 @@ func convertCAPIMachineAnnotationsToMAPIMachineSpecObjectMetaAnnotations(capiAnn
return mapiAnnotations
}

func convertCAPITaintsToMAPITaints(capiTaints []clusterv1.MachineTaint) []corev1.Taint {
if len(capiTaints) == 0 {
return nil
}

mapiTaints := make([]corev1.Taint, len(capiTaints))

for i, t := range capiTaints {
mapiTaints[i] = corev1.Taint{
Key: t.Key,
Value: t.Value,
Effect: t.Effect,
}
}

return mapiTaints
}

func convertCAPIAnnotationsToMAPIAnnotations(capiAnnotations map[string]string, machineAPIAnnotations map[string]string) map[string]string {
if len(capiAnnotations) == 0 && len(machineAPIAnnotations) == 0 {
return nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/conversion/capi2mapi/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ func fromCAPIMachineToMAPIMachine(capiMachine *clusterv1.Machine, additionalMach
},
ProviderID: providerID,
LifecycleHooks: lifecycleHooks,
Taints: convertCAPITaintsToMAPITaints(capiMachine.Spec.Taints),
// ProviderSpec: // ProviderSpec MUST NOT be populated here. It is added later by higher level fuctions.
Comment on lines +66 to 67

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Cluster API v1.12.3, is MachineTaintPropagationOnInitialization semantically distinct from MachineTaintPropagationAlways for Machine.spec.taints reconciliation?

💡 Result:

Yes, in Cluster API v1.12.3, MachineTaintPropagationOnInitialization and MachineTaintPropagationAlways are semantically distinct regarding the reconciliation of taints on a node [1]. The distinction lies in how the Cluster API controller manages the lifecycle of the taint after its initial application [1]: - MachineTaintPropagationAlways: The controller continuously reconciles the taint [1]. This ensures that the taint remains on the node if it is currently defined on the Machine [1]. If the taint is manually removed from the node, the controller will re-add it during the next reconciliation loop [1]. Similarly, if the taint is removed from the Machine spec, the controller will remove it from the node [1]. - MachineTaintPropagationOnInitialization: The controller ensures the taint is applied to the node only once during the initialization phase [1]. After this initial application, the controller ceases to manage the taint [1]. Consequently, if the taint is removed from the node, it will not be re-added by the controller, and if the taint is removed from the Machine spec, no action is taken on the node [1]. This behavior allows for different operational patterns, such as using OnInitialization for taints that are intended to be manually managed or removed after a one-time setup task (e.g., preparing a node with specific drivers) [1].

Citations:


🏁 Script executed:

# First, let's examine the file and the specific lines mentioned
fd 'machine.go' | grep 'conversion/capi2mapi'

Repository: openshift/cluster-capi-operator

Length of output: 111


🏁 Script executed:

# Once we find the file, let's view the lines in context
rg -A 15 -B 10 'convertCAPITaintsToMAPITaints' pkg/conversion/capi2mapi/machine.go

Repository: openshift/cluster-capi-operator

Length of output: 1542


🏁 Script executed:

# Let's find and examine the convertCAPITaintsToMAPITaints function definition
rg -A 20 'func convertCAPITaintsToMAPITaints' --type go

Repository: openshift/cluster-capi-operator

Length of output: 1371


🏁 Script executed:

# Check the MAPI Machine and Taint type definitions
fd 'types\.go|machine\.go' | xargs grep -l 'type Machine struct' | head -3

Repository: openshift/cluster-capi-operator

Length of output: 270


🏁 Script executed:

# Check if MAPI taint type has a Propagation field
rg 'type.*Taint.*struct' --type go -A 10 | head -50

Repository: openshift/cluster-capi-operator

Length of output: 5339


🏁 Script executed:

# View the full MachineTaint struct definition
rg -A 30 'type MachineTaint struct' vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/common_types.go

Repository: openshift/cluster-capi-operator

Length of output: 1974


🏁 Script executed:

# Also check what values MachineTaintPropagation can have
rg 'MachineTaintPropagation' vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/ -A 2 -B 2

Repository: openshift/cluster-capi-operator

Length of output: 3154


🏁 Script executed:

# Verify the location of the conversion call and its error handling context
cat -n pkg/conversion/capi2mapi/machine.go | sed -n '50,85p'

Repository: openshift/cluster-capi-operator

Length of output: 2370


Reject CAPI taints with OnInitialization propagation that MAPI cannot represent.

The conversion at line 66 silently drops taint propagation information, transforming OnInitialization taints into continuously reconciled Always taints. MAPI/Kubernetes taints lack propagation semantics; CAPI's OnInitialization (apply once, then stop managing) and Always (continuously reconcile) are fundamentally different behaviors.

Validate taint propagation early and surface an error for unsupported values:

Proposed fix
 func fromCAPIMachineToMAPIMachine(capiMachine *clusterv1.Machine, additionalMachineAPIMetadataLabels, additionalMachineAPIMetadataAnnotations map[string]string) (*mapiv1beta1.Machine, field.ErrorList) {
 	errs := field.ErrorList{}
+	for i, t := range capiMachine.Spec.Taints {
+		if t.Propagation != "" && t.Propagation != clusterv1.MachineTaintPropagationAlways {
+			errs = append(errs, field.Invalid(
+				field.NewPath("spec", "taints").Index(i).Child("propagation"),
+				t.Propagation,
+				"only Always taint propagation is supported for MAPI conversion",
+			))
+		}
+	}
 
 	lifecycleHooks, capiMachineNonHookAnnotations := convertCAPILifecycleHookAnnotationsToMAPILifecycleHooksAndAnnotations(capiMachine.Annotations)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/conversion/capi2mapi/machine.go` around lines 66 - 67, Detect and reject
any CAPI taint with Propagation == OnInitialization instead of silently
converting it to an Always taint: add validation before or inside
convertCAPITaintsToMAPITaints (or create validateCAPITaintPropagation called
from the caller that currently passes capiMachine.Spec.Taints) to iterate
capiMachine.Spec.Taints, if any taint.Propagation == OnInitialization return an
explicit error naming the offending taint (key/effect/propagation) so the caller
can fail conversion; do not map OnInitialization to an Always taint or drop
propagation silently. Ensure the conversion call site propagates this error up
instead of continuing.

// Taints: // TODO(OCPCLOUD-2861): Taint propagation from Machines to Nodes is not yet implemented in CAPI.
},
Status: mapiMachineStatus,
}
Expand Down
52 changes: 52 additions & 0 deletions pkg/conversion/capi2mapi/machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,59 @@ var _ = Describe("capi2mapi Machine conversion", func() {
Expect(machine.Annotations).ToNot(HaveKey(clusterv1.DeleteMachineAnnotation))
},
}),

Entry("With no taints on MAPI Machine converted from CAPI", capi2MAPIMachineConversionInput{
machineBuilder: capiMachineBase,
expectedErrors: []string{},
expectedWarnings: []string{},
assertion: func(machine *mapiv1beta1.Machine) {
Expect(machine.Spec.Taints).To(BeEmpty())
},
}),
)

Context("when the CAPI Machine has taints", func() {
It("should convert CAPI Machine taints to MAPI Machine taints", func() {
capiMachine := capiMachineBase.Build()
capiMachine.Spec.Taints = []clusterv1.MachineTaint{{
Key: "key1",
Value: "value1",
Effect: corev1.TaintEffectNoSchedule,
Propagation: clusterv1.MachineTaintPropagationAlways,
}}
machine, warns, err := FromMachineAndAWSMachineAndAWSCluster(
capiMachine,
capabuilder.AWSMachine().Build(),
capabuilder.AWSCluster().Build(),
).ToMachine()
Expect(err).To(BeNil())
Expect(warns).To(BeEmpty())
Expect(machine.Spec.Taints).To(ConsistOf(corev1.Taint{
Key: "key1",
Value: "value1",
Effect: corev1.TaintEffectNoSchedule,
}))
})

It("should convert multiple CAPI Machine taints to MAPI Machine taints, dropping propagation", func() {
capiMachine := capiMachineBase.Build()
capiMachine.Spec.Taints = []clusterv1.MachineTaint{
{Key: "key1", Value: "value1", Effect: corev1.TaintEffectNoSchedule, Propagation: clusterv1.MachineTaintPropagationAlways},
{Key: "key2", Value: "value2", Effect: corev1.TaintEffectNoExecute, Propagation: clusterv1.MachineTaintPropagationOnInitialization},
}
machine, warns, err := FromMachineAndAWSMachineAndAWSCluster(
capiMachine,
capabuilder.AWSMachine().Build(),
capabuilder.AWSCluster().Build(),
).ToMachine()
Expect(err).To(BeNil())
Expect(warns).To(BeEmpty())
Expect(machine.Spec.Taints).To(ConsistOf(
corev1.Taint{Key: "key1", Value: "value1", Effect: corev1.TaintEffectNoSchedule},
corev1.Taint{Key: "key2", Value: "value2", Effect: corev1.TaintEffectNoExecute},
))
})
})
})

var _ = Describe("capi2mapi Machine Status Conversion", func() {
Expand Down
1 change: 1 addition & 0 deletions pkg/conversion/mapi2capi/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func fromMAPIMachineToCAPIMachine(mapiMachine *mapiv1beta1.Machine, apiGroup, ki
Deletion: clusterv1.MachineDeletionSpec{
NodeDeletionTimeoutSeconds: ptr.To[int32](10), // Hardcode it to the CAPI default value until this is implemented in MAPI.
},
Taints: convertMAPITaintsToCAPITaints(mapiMachine.Spec.Taints),
},
Status: capiMachineStatus,
}
Expand Down
88 changes: 81 additions & 7 deletions pkg/conversion/mapi2capi/machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,23 @@ var _ = Describe("mapi2capi Machine conversion", func() {
infraBuilder configbuilder.InfrastructureBuilder
expectedErrors []string
expectedWarnings []string
assertion func(machine *mapiv1beta1.Machine)
assertion func(capiMachine *clusterv1.Machine)
}

var _ = DescribeTable("mapi2capi convert MAPI Machine to a CAPI Machine",
func(in mapi2CAPIMachineConversionInput) {
_, _, warns, err := FromAWSMachineAndInfra(
capiMachine, _, warns, err := FromAWSMachineAndInfra(
in.machineBuilder.Build(),
in.infraBuilder.Build(),
).ToMachineAndInfrastructureMachine()
Expect(err).To(matchers.ConsistOfMatchErrorSubstrings(in.expectedErrors),
"should match expected errors while converting MAPI Machine to CAPI Machine")
Expect(warns).To(matchers.ConsistOfSubstrings(in.expectedWarnings),
"should match expected warnings while converting MAPI Machine to CAPI Machine")

if in.assertion != nil {
in.assertion(capiMachine)
}
},

// Base Case.
Expand Down Expand Up @@ -121,25 +125,95 @@ var _ = Describe("mapi2capi Machine conversion", func() {
expectedWarnings: []string{},
}),

Entry("With unsupported spec.taints set", mapi2CAPIMachineConversionInput{
Entry("With spec.taints set", mapi2CAPIMachineConversionInput{
infraBuilder: infraBase,
machineBuilder: mapiMachineBase.WithTaints([]corev1.Taint{{
Key: "key1",
Value: "value1",
Effect: corev1.TaintEffectNoSchedule,
}}),
expectedErrors: []string{"spec.taints: Invalid value: [{\"key\":\"key1\",\"value\":\"value1\",\"effect\":\"NoSchedule\"}]: taints are not currently supported"},
expectedErrors: []string{},
expectedWarnings: []string{},
assertion: func(capiMachine *clusterv1.Machine) {
Expect(capiMachine.Spec.Taints).To(ConsistOf(clusterv1.MachineTaint{
Key: "key1",
Value: "value1",
Effect: corev1.TaintEffectNoSchedule,
Propagation: clusterv1.MachineTaintPropagationAlways,
}))
},
}),

Entry("With spec.taints set with NoExecute effect", mapi2CAPIMachineConversionInput{
infraBuilder: infraBase,
machineBuilder: mapiMachineBase.WithTaints([]corev1.Taint{{
Key: "key1",
Value: "value1",
Effect: corev1.TaintEffectNoExecute,
}}),
expectedErrors: []string{},
expectedWarnings: []string{},
assertion: func(capiMachine *clusterv1.Machine) {
Expect(capiMachine.Spec.Taints).To(ConsistOf(clusterv1.MachineTaint{
Key: "key1",
Value: "value1",
Effect: corev1.TaintEffectNoExecute,
Propagation: clusterv1.MachineTaintPropagationAlways,
}))
},
}),

Entry("With spec.taints set with PreferNoSchedule effect", mapi2CAPIMachineConversionInput{
infraBuilder: infraBase,
machineBuilder: mapiMachineBase.WithTaints([]corev1.Taint{{
Key: "key1",
Value: "value1",
Effect: corev1.TaintEffectPreferNoSchedule,
}}),
expectedErrors: []string{},
expectedWarnings: []string{},
assertion: func(capiMachine *clusterv1.Machine) {
Expect(capiMachine.Spec.Taints).To(ConsistOf(clusterv1.MachineTaint{
Key: "key1",
Value: "value1",
Effect: corev1.TaintEffectPreferNoSchedule,
Propagation: clusterv1.MachineTaintPropagationAlways,
}))
},
}),

Entry("With multiple spec.taints set", mapi2CAPIMachineConversionInput{
infraBuilder: infraBase,
machineBuilder: mapiMachineBase.WithTaints([]corev1.Taint{
{
Key: "key1",
Value: "value1",
Effect: corev1.TaintEffectNoSchedule,
},
{
Key: "key2",
Value: "value2",
Effect: corev1.TaintEffectNoExecute,
},
}),
expectedErrors: []string{},
expectedWarnings: []string{},
assertion: func(capiMachine *clusterv1.Machine) {
Expect(capiMachine.Spec.Taints).To(ConsistOf(
clusterv1.MachineTaint{Key: "key1", Value: "value1", Effect: corev1.TaintEffectNoSchedule, Propagation: clusterv1.MachineTaintPropagationAlways},
clusterv1.MachineTaint{Key: "key2", Value: "value2", Effect: corev1.TaintEffectNoExecute, Propagation: clusterv1.MachineTaintPropagationAlways},
))
},
}),

Entry("With delete-machine annotation", mapi2CAPIMachineConversionInput{
infraBuilder: infraBase,
machineBuilder: mapiMachineBase.WithAnnotations(map[string]string{util.MapiDeleteMachineAnnotation: "true"}),
expectedErrors: []string{},
expectedWarnings: []string{},
assertion: func(machine *mapiv1beta1.Machine) {
Expect(machine.Annotations).To(HaveKeyWithValue(clusterv1.DeleteMachineAnnotation, "true"))
Expect(machine.Annotations).ToNot(HaveKey(util.MapiDeleteMachineAnnotation))
assertion: func(capiMachine *clusterv1.Machine) {
Expect(capiMachine.Annotations).To(HaveKeyWithValue(clusterv1.DeleteMachineAnnotation, "true"))
Expect(capiMachine.Annotations).ToNot(HaveKey(util.MapiDeleteMachineAnnotation))
},
}),
)
Expand Down
26 changes: 25 additions & 1 deletion pkg/conversion/mapi2capi/machineset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,23 @@ var _ = Describe("mapi2capi MachineSet conversion", func() {
infraBuilder configbuilder.InfrastructureBuilder
expectedErrors []string
expectedWarnings []string
assertion func(capiMachineSet *clusterv1.MachineSet)
}

var _ = DescribeTable("mapi2capi convert MAPI MachineSet to CAPI MachineSet",
func(in mapi2CAPIMachinesetConversionInput) {
_, _, warns, err := FromAWSMachineSetAndInfra(
capiMachineSet, _, warns, err := FromAWSMachineSetAndInfra(
in.machineSetBuilder.Build(),
in.infraBuilder.Build(),
).ToMachineSetAndMachineTemplate()
Expect(err).To(matchers.ConsistOfMatchErrorSubstrings(in.expectedErrors),
"should match expected errors while converting MAPI MachineSet to CAPI MachineSet")
Expect(warns).To(matchers.ConsistOfSubstrings(in.expectedWarnings),
"should match expected warnings while converting MAPI MachineSet to CAPI MachineSet")

if in.assertion != nil {
in.assertion(capiMachineSet)
}
},

// Base Case
Expand Down Expand Up @@ -91,6 +96,25 @@ var _ = Describe("mapi2capi MachineSet conversion", func() {
expectedErrors: []string{"spec.metadata.namespace: Invalid value: \"test-namespace\": namespace is not supported"},
expectedWarnings: []string{},
}),

Entry("With spec.taints set", mapi2CAPIMachinesetConversionInput{
infraBuilder: infraBase,
machineSetBuilder: mapiMachineSetBase.WithTaints([]corev1.Taint{{
Key: "key1",
Value: "value1",
Effect: corev1.TaintEffectNoSchedule,
}}),
expectedErrors: []string{},
expectedWarnings: []string{},
assertion: func(capiMachineSet *clusterv1.MachineSet) {
Expect(capiMachineSet.Spec.Template.Spec.Taints).To(ConsistOf(clusterv1.MachineTaint{
Key: "key1",
Value: "value1",
Effect: corev1.TaintEffectNoSchedule,
Propagation: clusterv1.MachineTaintPropagationAlways,
}))
},
}),
)
})

Expand Down
34 changes: 22 additions & 12 deletions pkg/conversion/mapi2capi/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
mapiv1beta1 "github.com/openshift/api/machine/v1beta1"
"github.com/openshift/cluster-capi-operator/pkg/conversion/consts"
"github.com/openshift/cluster-capi-operator/pkg/util"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
Expand Down Expand Up @@ -149,6 +150,26 @@ func setMAPINodeLabelsToCAPINodeLabels(mapiNodeLabels map[string]string, capiMac
}
}

func convertMAPITaintsToCAPITaints(mapiTaints []corev1.Taint) []clusterv1.MachineTaint {
if len(mapiTaints) == 0 {
return nil
}

capiTaints := make([]clusterv1.MachineTaint, len(mapiTaints))

for i, t := range mapiTaints {
capiTaints[i] = clusterv1.MachineTaint{
Key: t.Key,
Value: t.Value,
Effect: t.Effect,
// MAPI taints are actively reconciled, which maps to Always propagation in CAPI.
Propagation: clusterv1.MachineTaintPropagationAlways,
}
}

return capiTaints
}

// setCAPILifecycleHookAnnotations sets the annotations that should be added to a CAPI Machine to represent the lifecycle hooks.
func setCAPILifecycleHookAnnotations(hooks mapiv1beta1.LifecycleHooks, capiMachine *clusterv1.Machine) {
lifecycleAnnotations := make(map[string]string)
Expand All @@ -172,18 +193,7 @@ func setCAPILifecycleHookAnnotations(hooks mapiv1beta1.LifecycleHooks, capiMachi

// handleUnsupportedMachineFields checks for fields that are not supported by CAPI and returns a list of errors.
func handleUnsupportedMachineFields(spec mapiv1beta1.MachineSpec) field.ErrorList {
var errs field.ErrorList

fldPath := field.NewPath("spec")

errs = append(errs, handleUnsupportedMAPIObjectMetaFields(fldPath.Child("metadata"), spec.ObjectMeta)...)

// TODO(OCPCLOUD-2861/2899): Taints are not supported by CAPI. add support for them via CAPI BootstrapConfig + minimal bootstrap controller.
if len(spec.Taints) > 0 {
errs = append(errs, field.Invalid(fldPath.Child("taints"), spec.Taints, "taints are not currently supported"))
}

return errs
return handleUnsupportedMAPIObjectMetaFields(field.NewPath("spec").Child("metadata"), spec.ObjectMeta)
}

// handleUnsupportedMAPIObjectMetaFields checks for unsupported MAPI metadta fields and returns a list of errors
Expand Down
Loading