Repository: rancher/cluster-api-provider-rke2
Pull request #636 (2025-04-28 09:27:57)

Render rke2-install without semanage for flatcar
<!--
Label the PR with the kind of change this for:

kind/feature
-->

**What this PR does / why we need it**:
Flatcar OS doesn't have `semanage` due to missing python from base distro. This PR removes semanage command from rke2-install only if `additionalIgnition` have `variant: flatcar` set

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #

**Special notes for your reviewer**:

**Checklist**:
<!-- Put an "X" character inside the brackets of each completed task. Some may be optional depending on the PR in which case these can be deleted -->

- [x] squashed commits into logical changes
- [ ] includes documentation
- [x] adds unit tests
- [ ] adds or updates e2e tests


furkatgofurov7: @k0da thanks for the PR, can you take a look at linting CI and fix them?

Patch:
diff --git a/bootstrap/internal/ignition/ignition.go b/bootstrap/internal/ignition/ignition.go
index 65f8937..b2dcd6d 100644
--- a/bootstrap/internal/ignition/ignition.go
+++ b/bootstrap/internal/ignition/ignition.go
@@ -18,6 +18,7 @@ package ignition
 import (
 	"errors"
 	"fmt"
+	"strings"
 
 	bootstrapv1 "github.com/rancher/cluster-api-provider-rke2/bootstrap/api/v1beta1"
 	"github.com/rancher/cluster-api-provider-rke2/bootstrap/internal/cloudinit"
@@ -102,6 +103,19 @@ func NewJoinControlPlane(input *ControlPlaneInput) ([]byte, error) {
 	return render(&processedInput.BaseUserData, processedInput.AdditionalIgnition)
 }
 
+func removeSemanageCmd(cmds []string) []string {
+	// this is flatcar drop /opt filesystem from default config
+	newCmds := []string{}
+
+	for _, cmd := range cmds {
+		if !strings.HasPrefix(cmd, "semanage") {
+			newCmds = append(newCmds, cmd)
+		}
+	}
+
+	return newCmds
+}
+
 // NewInitControlPlane returns Ignition configuration for bootstrapping new cluster.
 func NewInitControlPlane(input *ControlPlaneInput) ([]byte, error) {
 	processedInput, err := controlPlaneConfigInput(input)
@@ -139,6 +153,10 @@ func render(input *cloudinit.BaseUserData, ignitionConfig *bootstrapv1.Additiona
 		additionalButaneConfig = ignitionConfig
 	}
 
+	if strings.Contains(ignitionConfig.Config, "variant: flatcar") {
+		input.DeployRKE2Commands = removeSemanageCmd(input.DeployRKE2Commands)
+	}
+
 	return butane.Render(input, additionalButaneConfig)
 }
 


Test patch:
diff --git a/bootstrap/internal/ignition/ignition_test.go b/bootstrap/internal/ignition/ignition_test.go
index 794187e..79befbe 100644
--- a/bootstrap/internal/ignition/ignition_test.go
+++ b/bootstrap/internal/ignition/ignition_test.go
@@ -39,6 +39,8 @@ func TestIgnition(t *testing.T) {
 	RunSpecs(t, "Ignition Suite")
 }
 
+var flatcarIgnition = `variant: flatcar`
+
 var additionalIgnition = `variant: fcos
 version: 1.4.0
 systemd:
@@ -139,6 +141,29 @@ var _ = Describe("NewJoinWorker", func() {
 		Expect(err).ToNot(HaveOccurred())
 		Expect(scriptContents).To(ContainSubstring("/opt/rke2-cis-script.sh"))
 	})
+	It("should render rke2-install without semanage for flatcar", func() {
+		input.AdditionalIgnition = &bootstrapv1.AdditionalUserData{
+			Config: flatcarIgnition,
+			Strict: true,
+		}
+		ignitionJson, err := NewJoinWorker(input)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(ignitionJson).ToNot(BeNil())
+
+		ign, reports, err := ignition.Parse(ignitionJson)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(reports.IsFatal()).To(BeFalse())
+
+		scriptContentsEnc := strings.Split(*ign.Storage.Files[3].Contents.Source, ",")[1]
+		scriptContentsGzip, err := base64.StdEncoding.DecodeString(scriptContentsEnc)
+		Expect(err).ToNot(HaveOccurred())
+		reader := bytes.NewReader(scriptContentsGzip)
+		gzreader, err := gzip.NewReader(reader)
+		Expect(err).ToNot(HaveOccurred())
+		scriptContents, err := io.ReadAll(gzreader)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(scriptContents).ToNot(ContainSubstring("semanage"))
+	})
 
 })
 
@@ -190,6 +215,29 @@ var _ = Describe("NewJoinControlPlane", func() {
 		Expect(err).To(HaveOccurred())
 		Expect(ignitionJson).To(BeNil())
 	})
+	It("should render rke2-install without semanage for flatcar", func() {
+		input.AdditionalIgnition = &bootstrapv1.AdditionalUserData{
+			Config: flatcarIgnition,
+			Strict: true,
+		}
+		ignitionJson, err := NewJoinControlPlane(input)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(ignitionJson).ToNot(BeNil())
+
+		ign, reports, err := ignition.Parse(ignitionJson)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(reports.IsFatal()).To(BeFalse())
+
+		scriptContentsEnc := strings.Split(*ign.Storage.Files[3].Contents.Source, ",")[1]
+		scriptContentsGzip, err := base64.StdEncoding.DecodeString(scriptContentsEnc)
+		Expect(err).ToNot(HaveOccurred())
+		reader := bytes.NewReader(scriptContentsGzip)
+		gzreader, err := gzip.NewReader(reader)
+		Expect(err).ToNot(HaveOccurred())
+		scriptContents, err := io.ReadAll(gzreader)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(scriptContents).ToNot(ContainSubstring("semanage"))
+	})
 })
 
 var _ = Describe("NewInitControlPlane", func() {