sigs.k8s.io/cluster-api@v1.7.1/bootstrap/kubeadm/internal/cloudinit/cloudinit.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cloudinit 18 19 import ( 20 "bytes" 21 _ "embed" 22 "fmt" 23 "text/template" 24 25 "github.com/pkg/errors" 26 27 bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" 28 ) 29 30 const ( 31 standardJoinCommand = "kubeadm join --config /run/kubeadm/kubeadm-join-config.yaml %s" 32 // sentinelFileCommand writes a file to /run/cluster-api to signal successful Kubernetes bootstrapping in a way that 33 // works both for Linux and Windows OS. 34 sentinelFileCommand = "echo success > /run/cluster-api/bootstrap-success.complete" 35 retriableJoinScriptName = "/usr/local/bin/kubeadm-bootstrap-script" 36 retriableJoinScriptOwner = "root" 37 retriableJoinScriptPermissions = "0755" 38 cloudConfigHeader = `## template: jinja 39 #cloud-config 40 ` 41 ) 42 43 // BaseUserData is shared across all the various types of files written to disk. 44 type BaseUserData struct { 45 Header string 46 PreKubeadmCommands []string 47 PostKubeadmCommands []string 48 AdditionalFiles []bootstrapv1.File 49 WriteFiles []bootstrapv1.File 50 Users []bootstrapv1.User 51 NTP *bootstrapv1.NTP 52 DiskSetup *bootstrapv1.DiskSetup 53 Mounts []bootstrapv1.MountPoints 54 ControlPlane bool 55 UseExperimentalRetry bool 56 KubeadmCommand string 57 KubeadmVerbosity string 58 SentinelFileCommand string 59 } 60 61 func (input *BaseUserData) prepare() error { 62 input.Header = cloudConfigHeader 63 input.WriteFiles = append(input.WriteFiles, input.AdditionalFiles...) 64 input.KubeadmCommand = fmt.Sprintf(standardJoinCommand, input.KubeadmVerbosity) 65 if input.UseExperimentalRetry { 66 input.KubeadmCommand = retriableJoinScriptName 67 joinScriptFile, err := generateBootstrapScript(input) 68 if err != nil { 69 return errors.Wrap(err, "failed to generate user data for machine joining control plane") 70 } 71 input.WriteFiles = append(input.WriteFiles, *joinScriptFile) 72 } 73 input.SentinelFileCommand = sentinelFileCommand 74 return nil 75 } 76 77 func generate(kind string, tpl string, data interface{}) ([]byte, error) { 78 tm := template.New(kind).Funcs(defaultTemplateFuncMap) 79 if _, err := tm.Parse(filesTemplate); err != nil { 80 return nil, errors.Wrap(err, "failed to parse files template") 81 } 82 83 if _, err := tm.Parse(commandsTemplate); err != nil { 84 return nil, errors.Wrap(err, "failed to parse commands template") 85 } 86 87 if _, err := tm.Parse(ntpTemplate); err != nil { 88 return nil, errors.Wrap(err, "failed to parse ntp template") 89 } 90 91 if _, err := tm.Parse(usersTemplate); err != nil { 92 return nil, errors.Wrap(err, "failed to parse users template") 93 } 94 95 if _, err := tm.Parse(diskSetupTemplate); err != nil { 96 return nil, errors.Wrap(err, "failed to parse disk setup template") 97 } 98 99 if _, err := tm.Parse(fsSetupTemplate); err != nil { 100 return nil, errors.Wrap(err, "failed to parse fs setup template") 101 } 102 103 if _, err := tm.Parse(mountsTemplate); err != nil { 104 return nil, errors.Wrap(err, "failed to parse mounts template") 105 } 106 107 t, err := tm.Parse(tpl) 108 if err != nil { 109 return nil, errors.Wrapf(err, "failed to parse %s template", kind) 110 } 111 112 var out bytes.Buffer 113 if err := t.Execute(&out, data); err != nil { 114 return nil, errors.Wrapf(err, "failed to generate %s template", kind) 115 } 116 117 return out.Bytes(), nil 118 } 119 120 var ( 121 //go:embed kubeadm-bootstrap-script.sh 122 kubeadmBootstrapScript string 123 ) 124 125 func generateBootstrapScript(input interface{}) (*bootstrapv1.File, error) { 126 joinScript, err := generate("JoinScript", kubeadmBootstrapScript, input) 127 if err != nil { 128 return nil, errors.Wrap(err, "failed to bootstrap script for machine joins") 129 } 130 return &bootstrapv1.File{ 131 Path: retriableJoinScriptName, 132 Owner: retriableJoinScriptOwner, 133 Permissions: retriableJoinScriptPermissions, 134 Content: string(joinScript), 135 }, nil 136 }