Skip to content

Commit dbc7e68

Browse files
committed
Use template rendering of file
1 parent 2938447 commit dbc7e68

File tree

15 files changed

+234
-64
lines changed

15 files changed

+234
-64
lines changed

api/bootstrap/kubeadm/v1beta1/kubeadmconfig_types.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ var (
4646
missingSecretNameMsg = "secret file source must specify non-empty secret name"
4747
missingSecretKeyMsg = "secret file source must specify non-empty secret key"
4848
pathConflictMsg = "path property must be unique among all files"
49+
invalidFileContentFormatMsg = "contentFormat must be empty or go-template"
4950
)
5051

5152
// KubeadmConfigSpec defines the desired state of KubeadmConfig.
@@ -223,6 +224,16 @@ func (c *KubeadmConfigSpec) validateFiles(pathPrefix *field.Path) field.ErrorLis
223224
// n.b.: if we ever add types besides Secret as a ContentFrom
224225
// Source, we must add webhook validation here for one of the
225226
// sources being non-nil.
227+
if file.ContentFormat != "" && file.ContentFormat != FileContentFormatGoTemplate {
228+
allErrs = append(
229+
allErrs,
230+
field.Invalid(
231+
pathPrefix.Child("files").Index(i).Child("contentFormat"),
232+
file.ContentFormat,
233+
invalidFileContentFormatMsg,
234+
),
235+
)
236+
}
226237
if file.ContentFrom != nil {
227238
if file.ContentFrom.Secret.Name == "" {
228239
allErrs = append(
@@ -575,6 +586,16 @@ func init() {
575586
// +kubebuilder:validation:Enum=base64;gzip;gzip+base64
576587
type Encoding string
577588

589+
// FileContentFormat specifies how file content is interpreted after resolving content/contentFrom and before writing bootstrap data.
590+
// +kubebuilder:validation:Enum="";go-template
591+
type FileContentFormat string
592+
593+
const (
594+
// FileContentFormatGoTemplate means content is rendered as a Go text/template with KubernetesVersion and other
595+
// fields documented by the kubeadm bootstrap provider. The default empty value means content is used verbatim.
596+
FileContentFormatGoTemplate FileContentFormat = "go-template"
597+
)
598+
578599
const (
579600
// Base64 implies the contents of the file are encoded as base64.
580601
Base64 Encoding = "base64"
@@ -608,6 +629,13 @@ type File struct {
608629
// +optional
609630
Encoding Encoding `json:"encoding,omitempty"`
610631

632+
// contentFormat specifies how to interpret content after it is loaded (inline or from contentFrom).
633+
// When set to "go-template", content is rendered as a Go text/template with data including KubernetesVersion
634+
// (semver.String(), no "v" prefix). For a worker joining a cluster, that version is the control plane
635+
// Kubernetes version when the controller can read it; otherwise the Machine's version.
636+
// +optional
637+
ContentFormat FileContentFormat `json:"contentFormat,omitempty"`
638+
611639
// append specifies whether to append Content to existing file if Path exists.
612640
// +optional
613641
Append bool `json:"append,omitempty"`

api/bootstrap/kubeadm/v1beta1/zz_generated.conversion.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/bootstrap/kubeadm/v1beta2/kubeadmconfig_types.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ var (
4747
missingSecretNameMsg = "secret file source must specify non-empty secret name"
4848
missingSecretKeyMsg = "secret file source must specify non-empty secret key"
4949
pathConflictMsg = "path property must be unique among all files"
50+
invalidFileContentFormatMsg = "contentFormat must be empty or go-template"
5051
)
5152

5253
// KubeadmConfigSpec defines the desired state of KubeadmConfig.
@@ -220,6 +221,16 @@ func (c *KubeadmConfigSpec) validateFiles(pathPrefix *field.Path) field.ErrorLis
220221
// n.b.: if we ever add types besides Secret as a ContentFrom
221222
// Source, we must add webhook validation here for one of the
222223
// sources being non-nil.
224+
if file.ContentFormat != "" && file.ContentFormat != FileContentFormatGoTemplate {
225+
allErrs = append(
226+
allErrs,
227+
field.Invalid(
228+
pathPrefix.Child("files").Index(i).Child("contentFormat"),
229+
file.ContentFormat,
230+
invalidFileContentFormatMsg,
231+
),
232+
)
233+
}
223234
if file.ContentFrom.IsDefined() {
224235
if file.ContentFrom.Secret.Name == "" {
225236
allErrs = append(
@@ -624,6 +635,16 @@ func init() {
624635
// +kubebuilder:validation:Enum=base64;gzip;gzip+base64
625636
type Encoding string
626637

638+
// FileContentFormat specifies how file content is interpreted after resolving content/contentFrom and before writing bootstrap data.
639+
// +kubebuilder:validation:Enum="";go-template
640+
type FileContentFormat string
641+
642+
const (
643+
// FileContentFormatGoTemplate means content is rendered as a Go text/template (see kubeadm bootstrap provider docs).
644+
// The default empty value means content is used verbatim.
645+
FileContentFormatGoTemplate FileContentFormat = "go-template"
646+
)
647+
627648
const (
628649
// Base64 implies the contents of the file are encoded as base64.
629650
Base64 Encoding = "base64"
@@ -657,6 +678,13 @@ type File struct {
657678
// +optional
658679
Encoding Encoding `json:"encoding,omitempty"`
659680

681+
// contentFormat specifies how to interpret content after it is loaded (inline or from contentFrom).
682+
// When set to "go-template", content is rendered as a Go text/template with data including KubernetesVersion
683+
// (semver.String(), no "v" prefix). For a worker joining a cluster, that version is the control plane
684+
// Kubernetes version when the controller can read it; otherwise the Machine's version.
685+
// +optional
686+
ContentFormat FileContentFormat `json:"contentFormat,omitempty"`
687+
660688
// append specifies whether to append Content to existing file if Path exists.
661689
// +optional
662690
Append *bool `json:"append,omitempty"`

bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bootstrap/kubeadm/internal/cloudinit/cloudinit_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,6 @@ func TestNewJoinNodeCommands(t *testing.T) {
416416
- "echo $(date) ': hello PostKubeadmCommands!'"`
417417

418418
g.Expect(out).To(ContainSubstring(expectedRunCmd))
419-
420-
g.Expect(out).To(ContainSubstring("path: " + KubeadmVersionPath))
421419
}
422420

423421
func TestOmittableFields(t *testing.T) {

bootstrap/kubeadm/internal/cloudinit/node.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,6 @@ limitations under the License.
1616

1717
package cloudinit
1818

19-
import (
20-
bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2"
21-
)
22-
23-
const (
24-
// KubeadmVersionPath is the path where the control plane Kubernetes version is written for worker nodes.
25-
// It must exist before kubeadm join runs.
26-
KubeadmVersionPath = "/run/cluster-api/kubeadm-version"
27-
)
28-
2919
const (
3020
nodeCloudInit = `{{.Header}}
3121
{{template "files" .WriteFiles}}
@@ -62,13 +52,5 @@ type NodeInput struct {
6252
func NewNode(input *NodeInput) ([]byte, error) {
6353
input.prepare()
6454
input.Header = cloudConfigHeader
65-
// Write control plane version to KubeadmVersionPath so it exists before kubeadm join.
66-
versionFile := bootstrapv1.File{
67-
Path: KubeadmVersionPath,
68-
Owner: "root:root",
69-
Permissions: "0644",
70-
Content: input.KubernetesVersion.String(),
71-
}
72-
input.WriteFiles = append([]bootstrapv1.File{versionFile}, input.WriteFiles...)
7355
return generate("Node", nodeCloudInit, input)
7456
}

bootstrap/kubeadm/internal/cloudinit/node_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ func TestNewNode(t *testing.T) {
4646
},
4747
},
4848
},
49-
checkWriteFiles(KubeadmVersionPath, "/etc/foo.conf", "/run/kubeadm/kubeadm-join-config.yaml", "/run/cluster-api/placeholder"),
49+
checkWriteFiles("/etc/foo.conf", "/run/kubeadm/kubeadm-join-config.yaml", "/run/cluster-api/placeholder"),
5050
false,
5151
},
5252
{
53-
"check for existence of kubeadm-version path, /run/kubeadm/kubeadm-join-config.yaml and /run/cluster-api/placeholder",
53+
"check for existence of /run/kubeadm/kubeadm-join-config.yaml and /run/cluster-api/placeholder",
5454
&NodeInput{},
55-
checkWriteFiles(KubeadmVersionPath, "/run/kubeadm/kubeadm-join-config.yaml", "/run/cluster-api/placeholder"),
55+
checkWriteFiles("/run/kubeadm/kubeadm-join-config.yaml", "/run/cluster-api/placeholder"),
5656
false,
5757
},
5858
}

bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import (
4444

4545
bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2"
4646
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
47+
"sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/bootstrapfiles"
4748
"sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cloudinit"
4849
"sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/ignition"
4950
"sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/locking"
@@ -579,6 +580,17 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex
579580
})
580581
return ctrl.Result{}, err
581582
}
583+
files, err = bootstrapfiles.RenderTemplates(files, bootstrapfiles.DataFromVersion(parsedVersion))
584+
if err != nil {
585+
v1beta1conditions.MarkFalse(scope.Config, bootstrapv1.DataSecretAvailableV1Beta1Condition, bootstrapv1.DataSecretGenerationFailedV1Beta1Reason, clusterv1.ConditionSeverityWarning, "%s", err.Error())
586+
conditions.Set(scope.Config, metav1.Condition{
587+
Type: bootstrapv1.KubeadmConfigDataSecretAvailableCondition,
588+
Status: metav1.ConditionFalse,
589+
Reason: bootstrapv1.KubeadmConfigDataSecretNotAvailableReason,
590+
Message: "Failed to render go-template in spec.files",
591+
})
592+
return ctrl.Result{}, err
593+
}
582594

583595
users, err := r.resolveUsers(ctx, scope.Config)
584596
if err != nil {
@@ -765,6 +777,18 @@ func (r *KubeadmConfigReconciler) joinWorker(ctx context.Context, scope *Scope)
765777
files = append(files, *kubeconfig)
766778
}
767779

780+
files, err = bootstrapfiles.RenderTemplates(files, bootstrapfiles.DataFromVersion(parsedVersion))
781+
if err != nil {
782+
v1beta1conditions.MarkFalse(scope.Config, bootstrapv1.DataSecretAvailableV1Beta1Condition, bootstrapv1.DataSecretGenerationFailedV1Beta1Reason, clusterv1.ConditionSeverityWarning, "%s", err.Error())
783+
conditions.Set(scope.Config, metav1.Condition{
784+
Type: bootstrapv1.KubeadmConfigDataSecretAvailableCondition,
785+
Status: metav1.ConditionFalse,
786+
Reason: bootstrapv1.KubeadmConfigDataSecretNotAvailableReason,
787+
Message: "Failed to render go-template in spec.files",
788+
})
789+
return ctrl.Result{}, err
790+
}
791+
768792
nodeInput := &cloudinit.NodeInput{
769793
BaseUserData: cloudinit.BaseUserData{
770794
AdditionalFiles: files,
@@ -951,6 +975,18 @@ func (r *KubeadmConfigReconciler) joinControlplane(ctx context.Context, scope *S
951975
files = append(files, *kubeconfig)
952976
}
953977

978+
files, err = bootstrapfiles.RenderTemplates(files, bootstrapfiles.DataFromVersion(parsedVersion))
979+
if err != nil {
980+
v1beta1conditions.MarkFalse(scope.Config, bootstrapv1.DataSecretAvailableV1Beta1Condition, bootstrapv1.DataSecretGenerationFailedV1Beta1Reason, clusterv1.ConditionSeverityWarning, "%s", err.Error())
981+
conditions.Set(scope.Config, metav1.Condition{
982+
Type: bootstrapv1.KubeadmConfigDataSecretAvailableCondition,
983+
Status: metav1.ConditionFalse,
984+
Reason: bootstrapv1.KubeadmConfigDataSecretNotAvailableReason,
985+
Message: "Failed to render go-template in spec.files",
986+
})
987+
return ctrl.Result{}, err
988+
}
989+
954990
controlPlaneJoinInput := &cloudinit.ControlPlaneJoinInput{
955991
JoinConfiguration: joinData,
956992
Certificates: certificates,

bootstrap/kubeadm/internal/ignition/ignition.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,6 @@ func NewNode(input *NodeInput) ([]byte, string, error) {
6363
return nil, "", fmt.Errorf("node input can't be nil")
6464
}
6565

66-
// Write control plane version to KubeadmVersionPath so it exists before kubeadm join.
67-
versionFile := bootstrapv1.File{
68-
Path: cloudinit.KubeadmVersionPath,
69-
Owner: "root:root",
70-
Permissions: "0644",
71-
Content: input.KubernetesVersion.String(),
72-
}
73-
input.WriteFiles = append([]bootstrapv1.File{versionFile}, input.WriteFiles...)
7466
input.WriteFiles = append(input.WriteFiles, input.AdditionalFiles...)
7567
input.KubeadmCommand = fmt.Sprintf(kubeadmCommandTemplate, joinSubcommand, input.KubeadmVerbosity)
7668

0 commit comments

Comments
 (0)