From 91da5409c5ab164f2738ab0af00bdcde4d116f97 Mon Sep 17 00:00:00 2001 From: Mateusz Gozdek Date: Tue, 9 Feb 2021 12:10:21 +0100 Subject: [PATCH 1/9] bootstrap/kubeadm/controllers: move cloudinit input to variable So it can be reused in the future. Signed-off-by: Mateusz Gozdek --- .../controllers/kubeadmconfig_controller.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go index ca281ac7a086..bc8774d8cd52 100644 --- a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go +++ b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go @@ -427,7 +427,7 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex return ctrl.Result{}, err } - cloudInitData, err := cloudinit.NewInitControlPlane(&cloudinit.ControlPlaneInput{ + controlPlaneInput := &cloudinit.ControlPlaneInput{ BaseUserData: cloudinit.BaseUserData{ AdditionalFiles: files, NTP: scope.Config.Spec.NTP, @@ -441,7 +441,9 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex InitConfiguration: initdata, ClusterConfiguration: clusterdata, Certificates: certificates, - }) + } + + cloudInitData, err := cloudinit.NewInitControlPlane(controlPlaneInput) if err != nil { scope.Error(err, "Failed to generate cloud init for bootstrap control plane") return ctrl.Result{}, err @@ -502,7 +504,7 @@ func (r *KubeadmConfigReconciler) joinWorker(ctx context.Context, scope *Scope) return ctrl.Result{}, err } - cloudJoinData, err := cloudinit.NewNode(&cloudinit.NodeInput{ + nodeInput := &cloudinit.NodeInput{ BaseUserData: cloudinit.BaseUserData{ AdditionalFiles: files, NTP: scope.Config.Spec.NTP, @@ -515,7 +517,9 @@ func (r *KubeadmConfigReconciler) joinWorker(ctx context.Context, scope *Scope) UseExperimentalRetry: scope.Config.Spec.UseExperimentalRetryJoin, }, JoinConfiguration: joinData, - }) + } + + cloudJoinData, err := cloudinit.NewNode(nodeInput) if err != nil { scope.Error(err, "Failed to create a worker join configuration") return ctrl.Result{}, err @@ -579,7 +583,7 @@ func (r *KubeadmConfigReconciler) joinControlplane(ctx context.Context, scope *S return ctrl.Result{}, err } - cloudJoinData, err := cloudinit.NewJoinControlPlane(&cloudinit.ControlPlaneJoinInput{ + controlPlaneJoinInput := &cloudinit.ControlPlaneJoinInput{ JoinConfiguration: joinData, Certificates: certificates, BaseUserData: cloudinit.BaseUserData{ @@ -593,7 +597,9 @@ func (r *KubeadmConfigReconciler) joinControlplane(ctx context.Context, scope *S KubeadmVerbosity: verbosityFlag, UseExperimentalRetry: scope.Config.Spec.UseExperimentalRetryJoin, }, - }) + } + + cloudJoinData, err := cloudinit.NewJoinControlPlane(controlPlaneJoinInput) if err != nil { scope.Error(err, "Failed to create a control plane join configuration") return ctrl.Result{}, err From b7a3afdf9aa08037fc9e4c6accbf1ea9899a72b3 Mon Sep 17 00:00:00 2001 From: Mateusz Gozdek Date: Tue, 9 Feb 2021 12:11:59 +0100 Subject: [PATCH 2/9] bootstrap/kubeadm/controllers: generate data in switch statement Based on specified format. Use function to determine format, so more complex logic can be implemented for selecting the format in the future. Signed-off-by: Mateusz Gozdek --- .../controllers/kubeadmconfig_controller.go | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go index bc8774d8cd52..7f51eff8f2cf 100644 --- a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go +++ b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go @@ -443,7 +443,13 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex Certificates: certificates, } - cloudInitData, err := cloudinit.NewInitControlPlane(controlPlaneInput) + var cloudInitData []byte + + switch scope.Config.Spec.Format { + default: + cloudInitData, err = cloudinit.NewInitControlPlane(controlPlaneInput) + } + if err != nil { scope.Error(err, "Failed to generate cloud init for bootstrap control plane") return ctrl.Result{}, err @@ -519,7 +525,13 @@ func (r *KubeadmConfigReconciler) joinWorker(ctx context.Context, scope *Scope) JoinConfiguration: joinData, } - cloudJoinData, err := cloudinit.NewNode(nodeInput) + var cloudJoinData []byte + + switch scope.Config.Spec.Format { + default: + cloudJoinData, err = cloudinit.NewNode(nodeInput) + } + if err != nil { scope.Error(err, "Failed to create a worker join configuration") return ctrl.Result{}, err @@ -599,7 +611,13 @@ func (r *KubeadmConfigReconciler) joinControlplane(ctx context.Context, scope *S }, } - cloudJoinData, err := cloudinit.NewJoinControlPlane(controlPlaneJoinInput) + var cloudJoinData []byte + + switch scope.Config.Spec.Format { + default: + cloudJoinData, err = cloudinit.NewJoinControlPlane(controlPlaneJoinInput) + } + if err != nil { scope.Error(err, "Failed to create a control plane join configuration") return ctrl.Result{}, err From 9fce6438cf96cd44b23b7a69ddd7b49f0bde067b Mon Sep 17 00:00:00 2001 From: Mateusz Gozdek Date: Tue, 9 Feb 2021 18:33:45 +0100 Subject: [PATCH 3/9] bootstrap/kubeadm/controllers: add format field to generate secrets So Infrastructure providers can read it from there and act accordingly. This lift the requirement of each Infrastructure provider to roll their own bootstapping data format fields, so part of it can be automated. Signed-off-by: Mateusz Gozdek --- bootstrap/kubeadm/controllers/kubeadmconfig_controller.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go index 7f51eff8f2cf..e060068e8ed4 100644 --- a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go +++ b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go @@ -885,7 +885,8 @@ func (r *KubeadmConfigReconciler) storeBootstrapData(ctx context.Context, scope }, }, Data: map[string][]byte{ - "value": data, + "value": data, + "format": []byte(scope.Config.Spec.Format), }, Type: clusterv1.ClusterSecretType, } From a287d9ca30b65fbde822d0baf6ab78776c36e187 Mon Sep 17 00:00:00 2001 From: Mateusz Gozdek Date: Tue, 9 Feb 2021 18:36:05 +0100 Subject: [PATCH 4/9] bootstrap/kubeadm/internal/ignition: initial commit This commit adds Ignition packages next to cloudinit package of CABPK to open a way to generate bootstrap data in alternative format. Signed-off-by: Mateusz Gozdek --- .../kubeadm/internal/ignition/clc/clc.go | 357 ++++++++++++++++ .../kubeadm/internal/ignition/clc/clc_test.go | 170 ++++++++ .../kubeadm/internal/ignition/ignition.go | 115 ++++++ .../internal/ignition/ignition_test.go | 386 ++++++++++++++++++ go.mod | 4 +- go.sum | 29 +- test/infrastructure/docker/go.sum | 25 ++ 7 files changed, 1082 insertions(+), 4 deletions(-) create mode 100644 bootstrap/kubeadm/internal/ignition/clc/clc.go create mode 100644 bootstrap/kubeadm/internal/ignition/clc/clc_test.go create mode 100644 bootstrap/kubeadm/internal/ignition/ignition.go create mode 100644 bootstrap/kubeadm/internal/ignition/ignition_test.go diff --git a/bootstrap/kubeadm/internal/ignition/clc/clc.go b/bootstrap/kubeadm/internal/ignition/clc/clc.go new file mode 100644 index 000000000000..bdb1fec9362e --- /dev/null +++ b/bootstrap/kubeadm/internal/ignition/clc/clc.go @@ -0,0 +1,357 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package clc generates Ignition using Container Linux Config Transpiler. +package clc + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + "text/template" + + ignition "github.com/coreos/ignition/config/v2_3" + ignitionTypes "github.com/coreos/ignition/config/v2_3/types" + clct "github.com/flatcar-linux/container-linux-config-transpiler/config" + "github.com/pkg/errors" + + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4" + "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cloudinit" +) + +const ( + clcTemplate = `--- +{{- if .Users }} +passwd: + users: + {{- range .Users }} + - name: {{ .Name }} + {{- if .Gecos }} + gecos: {{ .Gecos }} + {{- end }} + {{- if .Groups }} + groups: + {{- range Split .Groups ", " }} + - {{ . }} + {{- end }} + {{- end }} + {{- if .HomeDir }} + home_dir: {{ .HomeDir }} + {{- end }} + {{- if .Shell }} + shell: {{ .Shell }} + {{- end }} + {{- if .Passwd }} + password_hash: {{ .Passwd }} + {{- end }} + {{- if .PrimaryGroup }} + primary_group: {{ .PrimaryGroup }} + {{- end }} + {{- if .SSHAuthorizedKeys }} + ssh_authorized_keys: + {{- range .SSHAuthorizedKeys }} + - {{ . }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +systemd: + units: + - name: kubeadm.service + enabled: true + contents: | + [Unit] + Description=kubeadm + # Run only once. After successful run, this file is moved to /tmp/. + ConditionPathExists=/etc/kubeadm.yml + [Service] + # To not restart the unit when it exits, as it is expected. + Type=oneshot + ExecStart=/etc/kubeadm.sh + [Install] + WantedBy=multi-user.target + {{- if .NTP }}{{ if .NTP.Enabled }} + - name: ntpd.service + enabled: true + {{- end }}{{- end }} + {{- range .Mounts }} + {{- $label := index . 0 }} + {{- $mountpoint := index . 1 }} + {{- $disk := index $.FilesystemDevicesByLabel $label }} + {{- $mountOptions := slice . 2 }} + - name: {{ $mountpoint | MountpointName }}.mount + enabled: true + contents: | + [Unit] + Description = Mount {{ $label }} + + [Mount] + What={{ $disk }} + Where={{ $mountpoint }} + Options={{ Join $mountOptions "," }} + + [Install] + WantedBy=multi-user.target + {{- end }} +storage: + {{- if .DiskSetup }}{{- if .DiskSetup.Partitions }} + disks: + {{- range .DiskSetup.Partitions }} + - device: {{ .Device }} + {{- if .Overwrite }} + wipe_table: {{ .Overwrite }} + {{- end }} + {{- if .Layout }} + partitions: + - {} + {{- end }} + {{- end }} + {{- end }}{{- end }} + {{- if .DiskSetup }}{{- if .DiskSetup.Filesystems }} + filesystems: + {{- range .DiskSetup.Filesystems }} + - name: {{ .Label }} + mount: + device: {{ .Device }} + format: {{ .Filesystem }} + label: {{ .Label }} + {{- if .ExtraOpts }} + options: + {{- range .ExtraOpts }} + - {{ . }} + {{- end }} + {{- end }} + {{- end }} + {{- end }}{{- end }} + files: + {{- range .Users }} + {{- if .Sudo }} + - path: /etc/sudoers.d/{{ .Name }} + mode: 0600 + contents: + inline: | + {{ .Name }} {{ .Sudo }} + {{- end }} + {{- end }} + {{- if .UsersWithPasswordAuth }} + - path: /etc/ssh/sshd_config + mode: 0600 + contents: + inline: | + # Use most defaults for sshd configuration. + Subsystem sftp internal-sftp + ClientAliveInterval 180 + UseDNS no + UsePAM yes + PrintLastLog no # handled by PAM + PrintMotd no # handled by PAM + + Match User {{ .UsersWithPasswordAuth }} + PasswordAuthentication yes + {{- end }} + {{- range .WriteFiles }} + - path: {{ .Path }} + # Owner + # + # If Encoding == gzip+base64 || Encoding == gzip + # compression: true + # + # If Encoding == gzip+base64 || Encoding == "base64" + # Put "!!binary" notation before the content to let YAML decoder treat data as + # base64 data. + # + {{ if ne .Permissions "" -}} + mode: {{ .Permissions }} + {{ end -}} + contents: + inline: | + {{ .Content | Indent 10 }} + {{- end }} + - path: /etc/kubeadm.sh + mode: 0700 + contents: + inline: | + #!/bin/bash + set -e + {{ range .PreKubeadmCommands }} + {{ . }} + {{- end }} + + {{ .KubeadmCommand }} + mkdir -p /run/cluster-api && echo success > /run/cluster-api/bootstrap-success.complete + mv /etc/kubeadm.yml /tmp/ + {{range .PostKubeadmCommands }} + {{ . }} + {{- end }} + - path: /etc/kubeadm.yml + mode: 0600 + contents: + inline: | + --- + {{ .KubeadmConfig | Indent 10 }} + {{- if .NTP }}{{- if and .NTP.Enabled .NTP.Servers }} + - path: /etc/ntp.conf + mode: 0644 + contents: + inline: | + # Common pool + {{- range .NTP.Servers }} + server {{ . }} + {{- end }} + + # Warning: Using default NTP settings will leave your NTP + # server accessible to all hosts on the Internet. + + # If you want to deny all machines (including your own) + # from accessing the NTP server, uncomment: + #restrict default ignore + + # Default configuration: + # - Allow only time queries, at a limited rate, sending KoD when in excess. + # - Allow all local queries (IPv4, IPv6) + restrict default nomodify nopeer noquery notrap limited kod + restrict 127.0.0.1 + restrict [::1] + {{- end }}{{- end }} +` +) + +type render struct { + *cloudinit.BaseUserData + + KubeadmConfig string + UsersWithPasswordAuth string + FilesystemDevicesByLabel map[string]string +} + +func defaultTemplateFuncMap() template.FuncMap { + return template.FuncMap{ + "Indent": templateYAMLIndent, + "Split": strings.Split, + "Join": strings.Join, + "MountpointName": mountpointName, + } +} + +func mountpointName(name string) string { + return strings.TrimPrefix(strings.ReplaceAll(name, "/", "-"), "-") +} + +func templateYAMLIndent(i int, input string) string { + split := strings.Split(input, "\n") + ident := "\n" + strings.Repeat(" ", i) + return strings.Join(split, ident) +} + +func renderCLC(input *cloudinit.BaseUserData, kubeadmConfig string) ([]byte, error) { + if input == nil { + return nil, errors.New("empty base user data") + } + + t := template.Must(template.New("template").Funcs(defaultTemplateFuncMap()).Parse(clcTemplate)) + + usersWithPasswordAuth := []string{} + for _, user := range input.Users { + if user.LockPassword != nil && !*user.LockPassword { + usersWithPasswordAuth = append(usersWithPasswordAuth, user.Name) + } + } + + filesystemDevicesByLabel := map[string]string{} + if input.DiskSetup != nil { + for _, filesystem := range input.DiskSetup.Filesystems { + filesystemDevicesByLabel[filesystem.Label] = filesystem.Device + } + } + + data := render{ + BaseUserData: input, + KubeadmConfig: kubeadmConfig, + UsersWithPasswordAuth: strings.Join(usersWithPasswordAuth, ","), + FilesystemDevicesByLabel: filesystemDevicesByLabel, + } + + var out bytes.Buffer + if err := t.Execute(&out, data); err != nil { + return nil, errors.Wrapf(err, "failed to render template") + } + + return out.Bytes(), nil +} + +func Render(input *cloudinit.BaseUserData, clc *bootstrapv1.ContainerLinuxConfig, kubeadmConfig string) ([]byte, string, error) { + if clc == nil { + return nil, "", errors.New("get empty CLC config") + } + + clcBytes, err := renderCLC(input, kubeadmConfig) + if err != nil { + return nil, "", errors.Wrapf(err, "rendering CLC configuration") + } + + userData, warnings, err := buildIgnitionConfig(clcBytes, clc) + if err != nil { + return nil, "", errors.Wrapf(err, "building Ignition config") + } + + return userData, warnings, nil +} + +func buildIgnitionConfig(baseCLC []byte, clc *bootstrapv1.ContainerLinuxConfig) ([]byte, string, error) { + // We control baseCLC config, so treat it as strict. + ign, _, err := clcToIgnition(baseCLC, true) + if err != nil { + return nil, "", errors.Wrapf(err, "converting generated CLC to Ignition") + } + + var clcWarnings string + + if clc.AdditionalConfig != "" { + additionalIgn, warnings, err := clcToIgnition([]byte(clc.AdditionalConfig), clc.Strict) + if err != nil { + return nil, "", errors.Wrapf(err, "converting additional CLC to Ignition") + } + + clcWarnings = warnings + + ign = ignition.Append(ign, additionalIgn) + } + + userData, err := json.Marshal(&ign) + if err != nil { + return nil, "", errors.Wrapf(err, "marshaling generated Ignition config into JSON") + } + + return userData, clcWarnings, nil +} + +func clcToIgnition(data []byte, strict bool) (ignitionTypes.Config, string, error) { + clc, ast, reports := clct.Parse(data) + + if (len(reports.Entries) > 0 && strict) || reports.IsFatal() { + return ignitionTypes.Config{}, "", fmt.Errorf("error parsing Container Linux Config: %v", reports.String()) + } + + ign, report := clct.Convert(clc, "", ast) + if (len(report.Entries) > 0 && strict) || report.IsFatal() { + return ignitionTypes.Config{}, "", fmt.Errorf("error converting to Ignition: %v", report.String()) + } + + reports.Merge(report) + + return ign, reports.String(), nil +} diff --git a/bootstrap/kubeadm/internal/ignition/clc/clc_test.go b/bootstrap/kubeadm/internal/ignition/clc/clc_test.go new file mode 100644 index 000000000000..158815e156e9 --- /dev/null +++ b/bootstrap/kubeadm/internal/ignition/clc/clc_test.go @@ -0,0 +1,170 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package clc_test tests clc package. +package clc_test + +import ( + "testing" + + ignition "github.com/coreos/ignition/config/v2_3" + + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4" + "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cloudinit" + "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/ignition/clc" +) + +const configWithWarning = `--- +storage: + files: + - path: /foo + contents: + inline: foo +` + +func stringPointer(s string) *string { + return &s +} + +func Test_Render(t *testing.T) { + t.Parallel() + + t.Run("renders_valid_Ignition_JSON", func(t *testing.T) { + t.Parallel() + + trueValue := true + + input := &cloudinit.BaseUserData{ + PreKubeadmCommands: []string{ + "pre-command", + "another-pre-command", + }, + PostKubeadmCommands: []string{ + "post-kubeadm-command", + "another-post-kubeamd-command", + }, + KubeadmCommand: "kubeadm join", + NTP: &bootstrapv1.NTP{ + Enabled: &trueValue, + Servers: []string{ + "foo.bar", + "baz", + }, + }, + Users: []bootstrapv1.User{ + { + Name: "foo", + Gecos: stringPointer("Foo B. Bar"), + Groups: stringPointer("foo, bar"), + HomeDir: stringPointer("/home/foo"), + Shell: stringPointer("/bin/false"), + Passwd: stringPointer("$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/"), + PrimaryGroup: stringPointer("foo"), + Sudo: stringPointer("ALL=(ALL) NOPASSWD:ALL"), + SSHAuthorizedKeys: []string{ + "foo", + "bar", + }, + }, + }, + DiskSetup: &bootstrapv1.DiskSetup{ + Partitions: []bootstrapv1.Partition{ + { + Device: "/dev/disk/azure/scsi1/lun0", + Layout: true, + Overwrite: &trueValue, + TableType: stringPointer("gpt"), + }, + }, + Filesystems: []bootstrapv1.Filesystem{ + { + Device: "/dev/disk/azure/scsi1/lun0", + Filesystem: "ext4", + Label: "test_disk", + ExtraOpts: []string{"-F", "-E", "lazy_itable_init=1,lazy_journal_init=1"}, + }, + }, + }, + Mounts: []bootstrapv1.MountPoints{ + { + "test_disk", "/var/lib/testdir", "foo", + }, + }, + } + config := &bootstrapv1.ContainerLinuxConfig{} + kubeadmConfig := "foo" + + ignitionBytes, _, err := clc.Render(input, config, kubeadmConfig) + if err != nil { + t.Fatalf("rendering: %v", err) + } + + _, reports, err := ignition.Parse(ignitionBytes) + if err != nil { + t.Fatalf("Parsing generated Ignition: %v", err) + } + + if reports.IsFatal() { + t.Fatalf("Generated Ignition has fatal reports: %s", reports) + } + }) + + t.Run("validates_input_parameter", func(t *testing.T) { + t.Parallel() + + if _, _, err := clc.Render(nil, &bootstrapv1.ContainerLinuxConfig{}, "foo"); err == nil { + t.Fatal("expected error when passing empty input data") + } + }) + + t.Run("validates_clc_parameter", func(t *testing.T) { + t.Parallel() + + if _, _, err := clc.Render(&cloudinit.BaseUserData{}, nil, "bar"); err == nil { + t.Fatal("expected error when passing empty CLC config") + } + }) + + t.Run("treats_warnings_as_errors_in_strict_mode", func(t *testing.T) { + config := &bootstrapv1.ContainerLinuxConfig{ + Strict: true, + AdditionalConfig: configWithWarning, + } + + if _, _, err := clc.Render(&cloudinit.BaseUserData{}, config, "foo"); err == nil { + t.Fatalf("expected error") + } + }) + + t.Run("returns_warnings", func(t *testing.T) { + config := &bootstrapv1.ContainerLinuxConfig{ + AdditionalConfig: configWithWarning, + } + + data, warnings, err := clc.Render(&cloudinit.BaseUserData{}, config, "foo") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if warnings == "" { + t.Errorf("expected warnings to be not empty") + } + + if len(data) == 0 { + t.Errorf("epected data to be returned on config with warnings") + } + }) +} diff --git a/bootstrap/kubeadm/internal/ignition/ignition.go b/bootstrap/kubeadm/internal/ignition/ignition.go new file mode 100644 index 000000000000..54263d01c5f5 --- /dev/null +++ b/bootstrap/kubeadm/internal/ignition/ignition.go @@ -0,0 +1,115 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package ignition aggregates all Ignition flavors into a single package to be consumed +// by the bootstrap provider by exposing an API similar to 'internal/cloudinit' package. +package ignition + +import ( + "fmt" + + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4" + "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cloudinit" + "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/ignition/clc" +) + +const ( + joinSubcommand = "join" + initSubcommand = "init" + kubeadmCommandTemplate = "kubeadm %s --config /etc/kubeadm.yml %s" +) + +// NodeInput defines the context to generate a node user data. +type NodeInput struct { + *cloudinit.NodeInput + + Ignition *bootstrapv1.IgnitionSpec +} + +// ControlPlaneJoinInput defines context to generate controlplane instance user data for control plane node join. +type ControlPlaneJoinInput struct { + *cloudinit.ControlPlaneJoinInput + + Ignition *bootstrapv1.IgnitionSpec +} + +// ControlPlaneInput defines the context to generate a controlplane instance user data. +type ControlPlaneInput struct { + *cloudinit.ControlPlaneInput + + Ignition *bootstrapv1.IgnitionSpec +} + +// NewNode returns Ignition configuration for new worker node joining the cluster. +func NewNode(input *NodeInput) ([]byte, string, error) { + if input == nil { + return nil, "", fmt.Errorf("input can't be nil") + } + + if input.NodeInput == nil { + return nil, "", fmt.Errorf("node input can't be nil") + } + + input.WriteFiles = append(input.WriteFiles, input.AdditionalFiles...) + input.KubeadmCommand = fmt.Sprintf(kubeadmCommandTemplate, joinSubcommand, input.KubeadmVerbosity) + + return render(&input.BaseUserData, input.Ignition, input.JoinConfiguration) +} + +// NewJoinControlPlane returns Ignition configuration for new controlplane node joining the cluster. +func NewJoinControlPlane(input *ControlPlaneJoinInput) ([]byte, string, error) { + if input == nil { + return nil, "", fmt.Errorf("input can't be nil") + } + + if input.ControlPlaneJoinInput == nil { + return nil, "", fmt.Errorf("controlplane join input can't be nil") + } + + input.WriteFiles = input.Certificates.AsFiles() + input.WriteFiles = append(input.WriteFiles, input.AdditionalFiles...) + input.KubeadmCommand = fmt.Sprintf(kubeadmCommandTemplate, joinSubcommand, input.KubeadmVerbosity) + + return render(&input.BaseUserData, input.Ignition, input.JoinConfiguration) +} + +// NewInitControlPlane returns Ignition configuration for bootstrapping new cluster. +func NewInitControlPlane(input *ControlPlaneInput) ([]byte, string, error) { + if input == nil { + return nil, "", fmt.Errorf("input can't be nil") + } + + if input.ControlPlaneInput == nil { + return nil, "", fmt.Errorf("controlplane input can't be nil") + } + + input.WriteFiles = input.Certificates.AsFiles() + input.WriteFiles = append(input.WriteFiles, input.AdditionalFiles...) + input.KubeadmCommand = fmt.Sprintf(kubeadmCommandTemplate, initSubcommand, input.KubeadmVerbosity) + + kubeadmConfig := fmt.Sprintf("%s\n---\n%s", input.ClusterConfiguration, input.InitConfiguration) + + return render(&input.BaseUserData, input.Ignition, kubeadmConfig) +} + +func render(input *cloudinit.BaseUserData, ignitionConfig *bootstrapv1.IgnitionSpec, kubeadmConfig string) ([]byte, string, error) { + clcConfig := &bootstrapv1.ContainerLinuxConfig{} + if ignitionConfig != nil && ignitionConfig.ContainerLinuxConfig != nil { + clcConfig = ignitionConfig.ContainerLinuxConfig + } + + return clc.Render(input, clcConfig, kubeadmConfig) +} diff --git a/bootstrap/kubeadm/internal/ignition/ignition_test.go b/bootstrap/kubeadm/internal/ignition/ignition_test.go new file mode 100644 index 000000000000..6301acda433e --- /dev/null +++ b/bootstrap/kubeadm/internal/ignition/ignition_test.go @@ -0,0 +1,386 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package ignition_test + +import ( + "encoding/json" + "fmt" + "net/url" + "strings" + "testing" + + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4" + "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cloudinit" + "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/ignition" +) + +const testString = "foo bar baz" + +func Test_NewNode(t *testing.T) { + t.Parallel() + + t.Run("returns_error_when", func(t *testing.T) { + t.Parallel() + + cases := map[string]*ignition.NodeInput{ + "nil_input_is_given": nil, + "nil_node_input_is_given": {}, + } + + for name, input := range cases { + input := input + + t.Run(name, func(t *testing.T) { + t.Parallel() + + ignitionData, _, err := ignition.NewNode(input) + if err == nil { + t.Fatalf("Expected error") + } + + if ignitionData != nil { + t.Fatalf("Unexpected data returned %v", ignitionData) + } + }) + } + }) + + t.Run("returns_JSON_data_without_error", func(t *testing.T) { + t.Parallel() + + input := &ignition.NodeInput{ + NodeInput: &cloudinit.NodeInput{}, + Ignition: &bootstrapv1.IgnitionSpec{}, + } + + ignitionData, _, err := ignition.NewNode(input) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if ignitionData == nil { + t.Fatalf("Returned data is nil") + } + + decodedValue := map[string]interface{}{} + + if err := json.Unmarshal(ignitionData, &decodedValue); err != nil { + t.Fatalf("Decoding received Ignition data as JSON: %v", err) + } + }) + + t.Run("returns_ignition_with_user_specified_snippet", func(t *testing.T) { + t.Parallel() + + input := &ignition.NodeInput{ + NodeInput: &cloudinit.NodeInput{}, + Ignition: &bootstrapv1.IgnitionSpec{ + ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ + AdditionalConfig: fmt.Sprintf(`storage: + files: + - path: /etc/foo + mode: 0644 + contents: + inline: | + %s +`, testString), + }, + }, + } + + ignitionData, _, err := ignition.NewNode(input) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Ignition stores content URL-encoded. + u := url.URL{Path: testString} + + if !strings.Contains(string(ignitionData), u.String()) { + t.Fatalf("Expected %q to be included in %q", testString, string(ignitionData)) + } + }) + + t.Run("returns_warnings_if_any", func(t *testing.T) { + t.Parallel() + + input := &ignition.NodeInput{ + NodeInput: &cloudinit.NodeInput{}, + Ignition: &bootstrapv1.IgnitionSpec{ + ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ + AdditionalConfig: fmt.Sprintf(`storage: + files: + - path: /etc/foo + contents: + inline: | + %s +`, testString), + }, + }, + } + + ignitionData, warnings, err := ignition.NewNode(input) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if warnings == "" { + t.Fatalf("Expected warnings") + } + + if len(ignitionData) == 0 { + t.Fatalf("Data should be returned with warnings but no errors") + } + }) +} + +func Test_NewJoinControlPlane(t *testing.T) { + t.Parallel() + + t.Run("returns_error_when", func(t *testing.T) { + t.Parallel() + + cases := map[string]*ignition.ControlPlaneJoinInput{ + "nil_input_is_given": nil, + "nil_node_input_is_given": {}, + } + + for name, input := range cases { + input := input + + t.Run(name, func(t *testing.T) { + t.Parallel() + + ignitionData, _, err := ignition.NewJoinControlPlane(input) + if err == nil { + t.Fatalf("Expected error") + } + + if ignitionData != nil { + t.Fatalf("Unexpected data returned %v", ignitionData) + } + }) + } + }) + + t.Run("returns_JSON_data_without_error", func(t *testing.T) { + t.Parallel() + + input := &ignition.ControlPlaneJoinInput{ + ControlPlaneJoinInput: &cloudinit.ControlPlaneJoinInput{}, + Ignition: &bootstrapv1.IgnitionSpec{}, + } + + ignitionData, _, err := ignition.NewJoinControlPlane(input) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if ignitionData == nil { + t.Fatalf("Returned data is nil") + } + + decodedValue := map[string]interface{}{} + + if err := json.Unmarshal(ignitionData, &decodedValue); err != nil { + t.Fatalf("Decoding received Ignition data as JSON: %v", err) + } + }) + + t.Run("returns_ignition_with_user_specified_snippet", func(t *testing.T) { + t.Parallel() + + input := &ignition.ControlPlaneJoinInput{ + ControlPlaneJoinInput: &cloudinit.ControlPlaneJoinInput{}, + Ignition: &bootstrapv1.IgnitionSpec{ + ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ + AdditionalConfig: fmt.Sprintf(`storage: + files: + - path: /etc/foo + mode: 0644 + contents: + inline: | + %s +`, testString), + }, + }, + } + + ignitionData, _, err := ignition.NewJoinControlPlane(input) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Ignition stores content URL-encoded. + u := url.URL{Path: testString} + + if !strings.Contains(string(ignitionData), u.String()) { + t.Fatalf("Expected %q to be included in %q", testString, string(ignitionData)) + } + }) + + t.Run("returns_warnings_if_any", func(t *testing.T) { + t.Parallel() + + input := &ignition.ControlPlaneJoinInput{ + ControlPlaneJoinInput: &cloudinit.ControlPlaneJoinInput{}, + Ignition: &bootstrapv1.IgnitionSpec{ + ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ + AdditionalConfig: fmt.Sprintf(`storage: + files: + - path: /etc/foo + contents: + inline: | + %s +`, testString), + }, + }, + } + + ignitionData, warnings, err := ignition.NewJoinControlPlane(input) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if warnings == "" { + t.Fatalf("Expected to get some warnings") + } + + if len(ignitionData) == 0 { + t.Fatalf("Data should be returned with warnings but no errors") + } + }) +} + +func Test_NewInitControlPlane(t *testing.T) { + t.Parallel() + + t.Run("returns_error_when", func(t *testing.T) { + t.Parallel() + + cases := map[string]*ignition.ControlPlaneInput{ + "nil_input_is_given": nil, + "nil_node_input_is_given": {}, + } + + for name, input := range cases { + input := input + + t.Run(name, func(t *testing.T) { + t.Parallel() + + ignitionData, _, err := ignition.NewInitControlPlane(input) + if err == nil { + t.Fatalf("Expected error") + } + + if ignitionData != nil { + t.Fatalf("Unexpected data returned %v", ignitionData) + } + }) + } + }) + + t.Run("returns_without_error", func(t *testing.T) { + t.Parallel() + + input := &ignition.ControlPlaneInput{ + ControlPlaneInput: &cloudinit.ControlPlaneInput{}, + } + + ignitionData, _, err := ignition.NewInitControlPlane(input) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if ignitionData == nil { + t.Fatalf("Returned data is nil") + } + + t.Run("valid_JSON_data", func(t *testing.T) { + decodedValue := map[string]interface{}{} + + if err := json.Unmarshal(ignitionData, &decodedValue); err != nil { + t.Fatalf("Decoding received Ignition data as JSON: %v", err) + } + }) + }) + + t.Run("returns_ignition_with_user_specified_snippet", func(t *testing.T) { + t.Parallel() + + input := &ignition.ControlPlaneInput{ + ControlPlaneInput: &cloudinit.ControlPlaneInput{}, + Ignition: &bootstrapv1.IgnitionSpec{ + ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ + AdditionalConfig: fmt.Sprintf(`storage: + files: + - path: /etc/foo + mode: 0644 + contents: + inline: | + %s +`, testString), + }, + }, + } + + ignitionData, _, err := ignition.NewInitControlPlane(input) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Ignition stores content URL-encoded. + u := url.URL{Path: testString} + + if !strings.Contains(string(ignitionData), u.String()) { + t.Fatalf("Expected %q to be included in %q", testString, string(ignitionData)) + } + }) + + t.Run("returns_warnings_if_any", func(t *testing.T) { + t.Parallel() + + input := &ignition.ControlPlaneInput{ + ControlPlaneInput: &cloudinit.ControlPlaneInput{}, + Ignition: &bootstrapv1.IgnitionSpec{ + ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ + AdditionalConfig: fmt.Sprintf(`storage: + files: + - path: /etc/foo + contents: + inline: | + %s +`, testString), + }, + }, + } + + ignitionData, warnings, err := ignition.NewInitControlPlane(input) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if warnings == "" { + t.Fatalf("Expected warnings") + } + + if len(ignitionData) == 0 { + t.Fatalf("Data should be returned with warnings but no errors") + } + }) +} diff --git a/go.mod b/go.mod index 68a2784b48b6..58abd6dbc38b 100644 --- a/go.mod +++ b/go.mod @@ -6,12 +6,14 @@ require ( github.com/MakeNowJust/heredoc v1.0.0 github.com/blang/semver v3.5.1+incompatible github.com/coredns/corefile-migration v1.0.11 + github.com/coreos/ignition v0.35.0 github.com/davecgh/go-spew v1.1.1 github.com/docker/distribution v2.7.1+incompatible github.com/drone/envsubst v1.0.3-0.20200709223903-efdb65b94e5a github.com/evanphx/json-patch v4.9.0+incompatible github.com/fatih/color v1.7.0 - github.com/go-logr/logr v0.1.0 + github.com/flatcar-linux/container-linux-config-transpiler v0.6.2-0.20201012083443-7abced2731c3 + github.com/go-logr/logr v0.4.0 github.com/gobuffalo/flect v0.2.2 github.com/gogo/protobuf v1.3.1 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect diff --git a/go.sum b/go.sum index 8b8a2b4a78c1..e895430cf2a5 100644 --- a/go.sum +++ b/go.sum @@ -29,12 +29,19 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/ajeddeloh/go-json v0.0.0-20160803184958-73d058cf8437 h1:gZCtZ+Hh/e3CGEX8q/yAcp8wWu5ZS6NMk6VGzpQhI3s= +github.com/ajeddeloh/go-json v0.0.0-20160803184958-73d058cf8437/go.mod h1:otnto4/Icqn88WCcM4bhIJNSgsh9VLBuspyyCfvof9c= +github.com/ajeddeloh/yaml v0.0.0-20170912190910-6b94386aeefd h1:NlKlOv3aVJ5ODMC0JWPvddw05KENkL3cZttIuu8kJRo= +github.com/ajeddeloh/yaml v0.0.0-20170912190910-6b94386aeefd/go.mod h1:idhzw68Q7v4j+rQ2AGyq3OlZW2Jij9mdmGA4/Sk6J0E= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20150109002421-6b4e7dc5e314/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053 h1:H/GMMKYPkEIC3DF/JWQz8Pdd+Feifov2EIgGfNpeogI= -github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053/go.mod h1:xW8sBma2LE3QxFSzCnH9qe6gAE2yO9GvQaWwX89HxbE= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alessio/shellescape v1.2.2 h1:8LnL+ncxhWT2TR00dfJRT25JWWrhkMZXneHVWnetDZg= +github.com/alessio/shellescape v1.2.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= @@ -67,11 +74,15 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.2.1-0.20170209201757-5e3acbb5668c/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180202092358-40e2722dffea/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/ignition v0.35.0 h1:UFodoYq1mOPrbEjtxIsZbThcDyQwAI1owczRDqWmKkQ= +github.com/coreos/ignition v0.35.0/go.mod h1:WJQapxzEn9DE0ryxsGvm8QnBajm/XsS/PkrDqSpz+bA= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= @@ -113,6 +124,8 @@ github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQo github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flatcar-linux/container-linux-config-transpiler v0.6.2-0.20201012083443-7abced2731c3 h1:hW0RDH+BC2hdXOt71s7EWk5tBetTVuR1fEUC5zoWiyA= +github.com/flatcar-linux/container-linux-config-transpiler v0.6.2-0.20201012083443-7abced2731c3/go.mod h1:UZqEik8xaq0D3Uu6Jwwdv/sU0kkPW8KT3Z+Bpo5gSSI= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -421,7 +434,7 @@ github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfD github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= @@ -436,6 +449,8 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/vincent-petithory/dataurl v0.0.0-20160330182126-9a301d65acbb h1:lyL3z7vYwTWXf4/bI+A01+cCSnfhKIBhy+SQ46Z/ml8= +github.com/vincent-petithory/dataurl v0.0.0-20160330182126-9a301d65acbb/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -455,6 +470,11 @@ go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go4.org v0.0.0-20160314031811-03efcb870d84 h1:WZkGC1qzoax/QSt84wmvIxk+ZOmGIChsTzdrv9t4nvk= +go4.org v0.0.0-20160314031811-03efcb870d84/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -601,6 +621,9 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= diff --git a/test/infrastructure/docker/go.sum b/test/infrastructure/docker/go.sum index e01117ae52c9..11b08e78ab83 100644 --- a/test/infrastructure/docker/go.sum +++ b/test/infrastructure/docker/go.sum @@ -22,8 +22,11 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/ajeddeloh/go-json v0.0.0-20160803184958-73d058cf8437/go.mod h1:otnto4/Icqn88WCcM4bhIJNSgsh9VLBuspyyCfvof9c= +github.com/ajeddeloh/yaml v0.0.0-20170912190910-6b94386aeefd/go.mod h1:idhzw68Q7v4j+rQ2AGyq3OlZW2Jij9mdmGA4/Sk6J0E= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20150109002421-6b4e7dc5e314/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053 h1:H/GMMKYPkEIC3DF/JWQz8Pdd+Feifov2EIgGfNpeogI= @@ -59,9 +62,12 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.2.1-0.20170209201757-5e3acbb5668c/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180202092358-40e2722dffea/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/ignition v0.35.0/go.mod h1:WJQapxzEn9DE0ryxsGvm8QnBajm/XsS/PkrDqSpz+bA= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -95,6 +101,7 @@ github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flatcar-linux/container-linux-config-transpiler v0.6.2-0.20201012083443-7abced2731c3/go.mod h1:UZqEik8xaq0D3Uu6Jwwdv/sU0kkPW8KT3Z+Bpo5gSSI= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -380,7 +387,11 @@ github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfD github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +<<<<<<< HEAD github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +======= +github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +>>>>>>> 36a536734... bootstrap/kubeadm/internal/ignition: initial commit github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= @@ -393,6 +404,7 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/vincent-petithory/dataurl v0.0.0-20160330182126-9a301d65acbb/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -409,6 +421,13 @@ go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +<<<<<<< HEAD +======= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go4.org v0.0.0-20160314031811-03efcb870d84/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +>>>>>>> 36a536734... bootstrap/kubeadm/internal/ignition: initial commit golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -556,6 +575,12 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +<<<<<<< HEAD +======= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +>>>>>>> 36a536734... bootstrap/kubeadm/internal/ignition: initial commit gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= From 9caff2eebbd698c27a7a936fe1c43248ce77bbb3 Mon Sep 17 00:00:00 2001 From: Mateusz Gozdek Date: Tue, 9 Feb 2021 18:38:34 +0100 Subject: [PATCH 5/9] bootstrap/kubeadm/controllers: support Ignition as bootstrap data format cloudinit remains the default format, but if either format is specified explicilty as 'ignition' or 'spec.ignition' field is set, Ignition will be used instead. Signed-off-by: Mateusz Gozdek --- .../controllers/kubeadmconfig_controller.go | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go index e060068e8ed4..e5f0b667d41c 100644 --- a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go +++ b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go @@ -34,6 +34,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3" bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha3" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cloudinit" + "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/ignition" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/locking" kubeadmv1beta1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/v1beta1" bsutil "sigs.k8s.io/cluster-api/bootstrap/util" @@ -446,12 +447,23 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex var cloudInitData []byte switch scope.Config.Spec.Format { + case bootstrapv1.Ignition: + ign, _, err := ignition.NewInitControlPlane(&ignition.ControlPlaneInput{ + ControlPlaneInput: controlPlaneInput, + Ignition: scope.Config.Spec.Ignition, + }) + if err != nil { + scope.Error(err, "Failed to generate Ignition user data for bootstrap control plane") + return ctrl.Result{}, err + } + + cloudInitData = ign default: cloudInitData, err = cloudinit.NewInitControlPlane(controlPlaneInput) } if err != nil { - scope.Error(err, "Failed to generate cloud init for bootstrap control plane") + scope.Error(err, "Failed to generate user data for bootstrap control plane") return ctrl.Result{}, err } @@ -528,6 +540,18 @@ func (r *KubeadmConfigReconciler) joinWorker(ctx context.Context, scope *Scope) var cloudJoinData []byte switch scope.Config.Spec.Format { + case bootstrapv1.Ignition: + ign, _, err := ignition.NewNode(&ignition.NodeInput{ + NodeInput: nodeInput, + Ignition: scope.Config.Spec.Ignition, + }) + + if err != nil { + scope.Error(err, "Failed to generate Ignition user data for the worker node") + return ctrl.Result{}, err + } + + cloudJoinData = ign default: cloudJoinData, err = cloudinit.NewNode(nodeInput) } @@ -614,6 +638,18 @@ func (r *KubeadmConfigReconciler) joinControlplane(ctx context.Context, scope *S var cloudJoinData []byte switch scope.Config.Spec.Format { + case bootstrapv1.Ignition: + ign, _, err := ignition.NewJoinControlPlane(&ignition.ControlPlaneJoinInput{ + ControlPlaneJoinInput: controlPlaneJoinInput, + Ignition: scope.Config.Spec.Ignition, + }) + + if err != nil { + scope.Error(err, "Failed to generate Ignition user data for join control plane") + return ctrl.Result{}, err + } + + cloudJoinData = ign default: cloudJoinData, err = cloudinit.NewJoinControlPlane(controlPlaneJoinInput) } From 0aaf8ace33da1b95fdda9bb71eaf84a54d5e6017 Mon Sep 17 00:00:00 2001 From: Deepak Sharma Date: Mon, 29 Mar 2021 10:22:56 +0530 Subject: [PATCH 6/9] cherrypick changes for type --- .../v1alpha3/kubeadmbootstrapconfig_types.go | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go b/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go index 31edf633a4ed..fde37a8569fa 100644 --- a/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go +++ b/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go @@ -23,14 +23,18 @@ import ( ) // Format specifies the output format of the bootstrap data -// +kubebuilder:validation:Enum=cloud-config +// +kubebuilder:validation:Enum=cloud-config;ignition type Format string const ( // CloudConfig make the bootstrap data to be of cloud-config format CloudConfig Format = "cloud-config" + + // Ignition make the bootstrap data to be of Ignition format. + Ignition Format = "ignition" ) + // KubeadmConfigSpec defines the desired state of KubeadmConfig. // Either ClusterConfiguration and InitConfiguration should be defined or the JoinConfiguration should be defined. type KubeadmConfigSpec struct { @@ -95,6 +99,34 @@ type KubeadmConfigSpec struct { // For more information, refer to https://github.com/kubernetes-sigs/cluster-api/pull/2763#discussion_r397306055. // +optional UseExperimentalRetryJoin bool `json:"useExperimentalRetryJoin,omitempty"` + + // Ignition contains Ignition specific configuration. + // + // If set, bootstrap data will be generated in Ignition format instead of cloud-init. + // + // +optional + Ignition *IgnitionSpec `json:"ignitionConfig,omitempty"` +} + +// IgnitionSpec contains Ignition specific configuration. +type IgnitionSpec struct { + // ContainerLinuxConfig contains CLC specific configuration. + // + // +optional + ContainerLinuxConfig *ContainerLinuxConfig `json:"containerLinuxConfig,omitempty"` +} + +// ContainerLinuxConfig contains CLC specific configuration. +// +// We use structured type here to keep space for adding additional fields, for example 'version'. +type ContainerLinuxConfig struct { + // AdditionalConfig contains additional configuration which will be added bootstrap data in CLC format. + // + // +optional + AdditionalConfig string `json:"additionalConfig,omitempty"` + + // Strict controls if AdditionalConfig should be strictly parsed. If so, warnings are treated as errors. + Strict bool `json:"strict,omitempty"` } // KubeadmConfigStatus defines the observed state of KubeadmConfig From 767fee3795379c9eb2c40b8dd26395cc0bce32c3 Mon Sep 17 00:00:00 2001 From: Deepak Sharma Date: Mon, 19 Apr 2021 10:05:53 +0530 Subject: [PATCH 7/9] capi ignition changes --- .../api/v1alpha3/zz_generated.deepcopy.go | 40 +++++++++++++++++++ ...strap.cluster.x-k8s.io_kubeadmconfigs.yaml | 19 +++++++++ ...uster.x-k8s.io_kubeadmconfigtemplates.yaml | 22 ++++++++++ .../kubeadm/internal/ignition/clc/clc.go | 11 ++++- .../kubeadm/internal/ignition/clc/clc_test.go | 2 +- .../kubeadm/internal/ignition/ignition.go | 4 +- .../internal/ignition/ignition_test.go | 2 +- ...cluster.x-k8s.io_kubeadmcontrolplanes.yaml | 19 +++++++++ 8 files changed, 114 insertions(+), 5 deletions(-) diff --git a/bootstrap/kubeadm/api/v1alpha3/zz_generated.deepcopy.go b/bootstrap/kubeadm/api/v1alpha3/zz_generated.deepcopy.go index 1f016861de1e..b77ce1c2bf2b 100644 --- a/bootstrap/kubeadm/api/v1alpha3/zz_generated.deepcopy.go +++ b/bootstrap/kubeadm/api/v1alpha3/zz_generated.deepcopy.go @@ -26,6 +26,21 @@ import ( "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/v1beta1" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerLinuxConfig) DeepCopyInto(out *ContainerLinuxConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerLinuxConfig. +func (in *ContainerLinuxConfig) DeepCopy() *ContainerLinuxConfig { + if in == nil { + return nil + } + out := new(ContainerLinuxConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DiskSetup) DeepCopyInto(out *DiskSetup) { *out = *in @@ -126,6 +141,26 @@ func (in *Filesystem) DeepCopy() *Filesystem { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IgnitionSpec) DeepCopyInto(out *IgnitionSpec) { + *out = *in + if in.ContainerLinuxConfig != nil { + in, out := &in.ContainerLinuxConfig, &out.ContainerLinuxConfig + *out = new(ContainerLinuxConfig) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IgnitionSpec. +func (in *IgnitionSpec) DeepCopy() *IgnitionSpec { + if in == nil { + return nil + } + out := new(IgnitionSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KubeadmConfig) DeepCopyInto(out *KubeadmConfig) { *out = *in @@ -253,6 +288,11 @@ func (in *KubeadmConfigSpec) DeepCopyInto(out *KubeadmConfigSpec) { *out = new(int32) **out = **in } + if in.Ignition != nil { + in, out := &in.Ignition, &out.Ignition + *out = new(IgnitionSpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmConfigSpec. diff --git a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml index 259b92d748e5..54197e14e808 100644 --- a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml +++ b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml @@ -1305,7 +1305,26 @@ spec: description: Format specifies the output format of the bootstrap data enum: - cloud-config + - ignition type: string + ignitionConfig: + description: "Ignition contains Ignition specific configuration. \n + If set, bootstrap data will be generated in Ignition format instead + of cloud-init." + properties: + containerLinuxConfig: + description: ContainerLinuxConfig contains CLC specific configuration. + properties: + additionalConfig: + description: AdditionalConfig contains additional configuration + which will be added bootstrap data in CLC format. + type: string + strict: + description: Strict controls if AdditionalConfig should be + strictly parsed. If so, warnings are treated as errors. + type: boolean + type: object + type: object initConfiguration: description: InitConfiguration along with ClusterConfiguration are the configurations necessary for the init command diff --git a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml index 00ceb452fe9a..4b93c4cae195 100644 --- a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml +++ b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml @@ -1372,7 +1372,29 @@ spec: data enum: - cloud-config + - ignition type: string + ignitionConfig: + description: "Ignition contains Ignition specific configuration. + \n If set, bootstrap data will be generated in Ignition + format instead of cloud-init." + properties: + containerLinuxConfig: + description: ContainerLinuxConfig contains CLC specific + configuration. + properties: + additionalConfig: + description: AdditionalConfig contains additional + configuration which will be added bootstrap data + in CLC format. + type: string + strict: + description: Strict controls if AdditionalConfig should + be strictly parsed. If so, warnings are treated + as errors. + type: boolean + type: object + type: object initConfiguration: description: InitConfiguration along with ClusterConfiguration are the configurations necessary for the init command diff --git a/bootstrap/kubeadm/internal/ignition/clc/clc.go b/bootstrap/kubeadm/internal/ignition/clc/clc.go index bdb1fec9362e..9bbd74d49b5a 100644 --- a/bootstrap/kubeadm/internal/ignition/clc/clc.go +++ b/bootstrap/kubeadm/internal/ignition/clc/clc.go @@ -29,7 +29,7 @@ import ( clct "github.com/flatcar-linux/container-linux-config-transpiler/config" "github.com/pkg/errors" - bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4" + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha3" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cloudinit" ) @@ -82,6 +82,8 @@ systemd: # To not restart the unit when it exits, as it is expected. Type=oneshot ExecStart=/etc/kubeadm.sh + StandardOutput=append:/var/log/kubeadm.log + StandardError=append:/var/log/kubeadm.err.log [Install] WantedBy=multi-user.target {{- if .NTP }}{{ if .NTP.Enabled }} @@ -108,6 +110,10 @@ systemd: WantedBy=multi-user.target {{- end }} storage: + links: + - filesystem: "root" + path: "/etc/systemd/system/multi-user.target.wants/kubeadm.service" + target: "/etc/systemd/system/kubeadm.service" {{- if .DiskSetup }}{{- if .DiskSetup.Partitions }} disks: {{- range .DiskSetup.Partitions }} @@ -187,6 +193,9 @@ storage: inline: | #!/bin/bash set -e + export PATH="$PATH:/opt/bin" + echo "New path now is" + echo $PATH {{ range .PreKubeadmCommands }} {{ . }} {{- end }} diff --git a/bootstrap/kubeadm/internal/ignition/clc/clc_test.go b/bootstrap/kubeadm/internal/ignition/clc/clc_test.go index 158815e156e9..5a7a7600d202 100644 --- a/bootstrap/kubeadm/internal/ignition/clc/clc_test.go +++ b/bootstrap/kubeadm/internal/ignition/clc/clc_test.go @@ -22,7 +22,7 @@ import ( ignition "github.com/coreos/ignition/config/v2_3" - bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4" + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha3" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cloudinit" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/ignition/clc" ) diff --git a/bootstrap/kubeadm/internal/ignition/ignition.go b/bootstrap/kubeadm/internal/ignition/ignition.go index 54263d01c5f5..c9c7840142da 100644 --- a/bootstrap/kubeadm/internal/ignition/ignition.go +++ b/bootstrap/kubeadm/internal/ignition/ignition.go @@ -21,7 +21,7 @@ package ignition import ( "fmt" - bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4" + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha3" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cloudinit" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/ignition/clc" ) @@ -29,7 +29,7 @@ import ( const ( joinSubcommand = "join" initSubcommand = "init" - kubeadmCommandTemplate = "kubeadm %s --config /etc/kubeadm.yml %s" + kubeadmCommandTemplate = "kubeadm %s --config /etc/kubeadm.yml %s --cri-socket /run/docker/libcontainerd/docker-containerd.sock" ) // NodeInput defines the context to generate a node user data. diff --git a/bootstrap/kubeadm/internal/ignition/ignition_test.go b/bootstrap/kubeadm/internal/ignition/ignition_test.go index 6301acda433e..686ee70e303a 100644 --- a/bootstrap/kubeadm/internal/ignition/ignition_test.go +++ b/bootstrap/kubeadm/internal/ignition/ignition_test.go @@ -23,7 +23,7 @@ import ( "strings" "testing" - bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4" + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha3" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cloudinit" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/ignition" ) diff --git a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml index 0e03dd417821..a321ee52c988 100644 --- a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml +++ b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml @@ -583,7 +583,26 @@ spec: data enum: - cloud-config + - ignition type: string + ignitionConfig: + description: "Ignition contains Ignition specific configuration. + \n If set, bootstrap data will be generated in Ignition format + instead of cloud-init." + properties: + containerLinuxConfig: + description: ContainerLinuxConfig contains CLC specific configuration. + properties: + additionalConfig: + description: AdditionalConfig contains additional configuration + which will be added bootstrap data in CLC format. + type: string + strict: + description: Strict controls if AdditionalConfig should + be strictly parsed. If so, warnings are treated as errors. + type: boolean + type: object + type: object initConfiguration: description: InitConfiguration along with ClusterConfiguration are the configurations necessary for the init command From c1828743de6341f77bdcf8568e7d7e1dad05527a Mon Sep 17 00:00:00 2001 From: Deepak Sharma Date: Wed, 12 May 2021 10:15:21 +0530 Subject: [PATCH 8/9] mask locksmithd.service --- bootstrap/kubeadm/internal/ignition/clc/clc.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bootstrap/kubeadm/internal/ignition/clc/clc.go b/bootstrap/kubeadm/internal/ignition/clc/clc.go index 9bbd74d49b5a..3728ee93d208 100644 --- a/bootstrap/kubeadm/internal/ignition/clc/clc.go +++ b/bootstrap/kubeadm/internal/ignition/clc/clc.go @@ -71,6 +71,8 @@ passwd: {{- end }} systemd: units: + - name: locksmithd.service + mask: true - name: kubeadm.service enabled: true contents: | From 9723ba7ff627c1ad73fcb456a3dca4355b3854ad Mon Sep 17 00:00:00 2001 From: Deepak Sharma Date: Wed, 12 May 2021 10:17:24 +0530 Subject: [PATCH 9/9] update kubeadmbootstrapconfig --- .../kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go b/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go index fde37a8569fa..0491a139053c 100644 --- a/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go +++ b/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go @@ -120,7 +120,13 @@ type IgnitionSpec struct { // // We use structured type here to keep space for adding additional fields, for example 'version'. type ContainerLinuxConfig struct { - // AdditionalConfig contains additional configuration which will be added bootstrap data in CLC format. + // AdditionalConfig contains additional configuration which will be appended/merged [1] to the + // Ingnition configuration generated by the bootstrapper controller. + // + // Format of data is documented here [2]. + // + // [1] https://coreos.github.io/ignition/operator-notes/#config-merging + // [2] https://kinvolk.io/docs/flatcar-container-linux/latest/provisioning/cl-config/ // // +optional AdditionalConfig string `json:"additionalConfig,omitempty"` @@ -129,6 +135,7 @@ type ContainerLinuxConfig struct { Strict bool `json:"strict,omitempty"` } + // KubeadmConfigStatus defines the observed state of KubeadmConfig type KubeadmConfigStatus struct { // Ready indicates the BootstrapData field is ready to be consumed