diff --git a/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go b/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go index 31edf633a4ed..0491a139053c 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,8 +99,43 @@ 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 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"` + + // 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 type KubeadmConfigStatus struct { // Ready indicates the BootstrapData field is ready to be consumed 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/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go index ca281ac7a086..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" @@ -427,7 +428,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,9 +442,28 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex InitConfiguration: initdata, ClusterConfiguration: clusterdata, Certificates: certificates, - }) + } + + 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 } @@ -502,7 +522,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 +535,27 @@ func (r *KubeadmConfigReconciler) joinWorker(ctx context.Context, scope *Scope) UseExperimentalRetry: scope.Config.Spec.UseExperimentalRetryJoin, }, JoinConfiguration: joinData, - }) + } + + 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) + } + if err != nil { scope.Error(err, "Failed to create a worker join configuration") return ctrl.Result{}, err @@ -579,7 +619,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 +633,27 @@ func (r *KubeadmConfigReconciler) joinControlplane(ctx context.Context, scope *S KubeadmVerbosity: verbosityFlag, UseExperimentalRetry: scope.Config.Spec.UseExperimentalRetryJoin, }, - }) + } + + 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) + } + if err != nil { scope.Error(err, "Failed to create a control plane join configuration") return ctrl.Result{}, err @@ -861,7 +921,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, } diff --git a/bootstrap/kubeadm/internal/ignition/clc/clc.go b/bootstrap/kubeadm/internal/ignition/clc/clc.go new file mode 100644 index 000000000000..3728ee93d208 --- /dev/null +++ b/bootstrap/kubeadm/internal/ignition/clc/clc.go @@ -0,0 +1,368 @@ +/* +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/v1alpha3" + "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: locksmithd.service + mask: true + - 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 + StandardOutput=append:/var/log/kubeadm.log + StandardError=append:/var/log/kubeadm.err.log + [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: + 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 }} + - 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 + export PATH="$PATH:/opt/bin" + echo "New path now is" + echo $PATH + {{ 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..5a7a7600d202 --- /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/v1alpha3" + "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..c9c7840142da --- /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/v1alpha3" + "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 --cri-socket /run/docker/libcontainerd/docker-containerd.sock" +) + +// 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..686ee70e303a --- /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/v1alpha3" + "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/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 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=