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  }