github.com/SUSE/skuba@v1.4.17/pkg/skuba/actions/node/bootstrap/bootstrap.go (about) 1 /* 2 * Copyright (c) 2019 SUSE LLC. 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 18 package node 19 20 import ( 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 26 "github.com/pkg/errors" 27 "k8s.io/apimachinery/pkg/runtime/schema" 28 "k8s.io/apimachinery/pkg/util/version" 29 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" 30 kubeadmconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" 31 32 "github.com/SUSE/skuba/internal/pkg/skuba/addons" 33 "github.com/SUSE/skuba/internal/pkg/skuba/deployments" 34 "github.com/SUSE/skuba/internal/pkg/skuba/kubeadm" 35 "github.com/SUSE/skuba/internal/pkg/skuba/kubernetes" 36 "github.com/SUSE/skuba/internal/pkg/skuba/node" 37 "github.com/SUSE/skuba/pkg/skuba" 38 ) 39 40 // Bootstrap initializes the first master node of the cluster 41 func Bootstrap(bootstrapConfiguration deployments.BootstrapConfiguration, target *deployments.Target) error { 42 coreBootstrapDone := false 43 44 if clientSet, err := kubernetes.GetAdminClientSet(); err == nil { 45 _, err := clientSet.Discovery().ServerVersion() 46 if err == nil { 47 fmt.Printf("[bootstrap] node %q has already the core components bootstrapped\n", target.Target) 48 coreBootstrapDone = true 49 } 50 } 51 52 initConfiguration, err := node.LoadInitConfigurationFromFile(skuba.KubeadmInitConfFile()) 53 if err != nil { 54 return errors.Wrapf(err, "could not parse %s file", skuba.KubeadmInitConfFile()) 55 } 56 57 if !coreBootstrapDone { 58 if err := coreBootstrap(initConfiguration, bootstrapConfiguration, target); err != nil { 59 return err 60 } 61 } 62 63 if err := downloadSecrets(target); err != nil { 64 return err 65 } 66 67 // Load admin.conf after download secrets from remote bootstrapped master node. 68 clientSet, err := kubernetes.GetAdminClientSet() 69 if err != nil { 70 return err 71 } 72 73 fmt.Printf("[bootstrap] deploying core add-ons on node %q\n", target.Target) 74 versionToDeploy, err := version.ParseSemantic(initConfiguration.KubernetesVersion) 75 if err != nil { 76 return errors.Wrapf(err, "could not parse semantic version: %s", initConfiguration.KubernetesVersion) 77 } 78 addonConfiguration := addons.AddonConfiguration{ 79 ClusterVersion: versionToDeploy, 80 ControlPlane: initConfiguration.ControlPlaneEndpoint, 81 ClusterName: initConfiguration.ClusterName, 82 } 83 // re-render all addons base manifest 84 for addonName, addon := range addons.Addons { 85 if err := addon.Write(addonConfiguration); err != nil { 86 return errors.Wrapf(err, "failed to refresh addon %s manifest", string(addonName)) 87 } 88 } 89 dryRun := false 90 if err := addons.DeployAddons(clientSet, addonConfiguration, dryRun); err != nil { 91 return err 92 } 93 94 fmt.Printf("[bootstrap] successfully bootstrapped core add-ons on node %q\n", target.Target) 95 return nil 96 } 97 98 // Takes care of bootstrapping the core components of the nodes, containerized add-ons are 99 // not handled here. 100 func coreBootstrap(initConfiguration *kubeadmapi.InitConfiguration, bootstrapConfiguration deployments.BootstrapConfiguration, target *deployments.Target) error { 101 versionToDeploy := version.MustParseSemantic(initConfiguration.KubernetesVersion) 102 103 if _, err := target.InstallNodePattern(deployments.KubernetesBaseOSConfiguration{ 104 CurrentVersion: versionToDeploy.String(), 105 }); err != nil { 106 return err 107 } 108 109 fmt.Println("[bootstrap] updating init configuration with target information") 110 if err := node.AddTargetInformationToInitConfigurationWithClusterVersion(target, initConfiguration, versionToDeploy); err != nil { 111 return errors.Wrap(err, "unable to add target information to init configuration") 112 } 113 114 finalInitConfigurationContents, err := kubeadmconfigutil.MarshalInitConfigurationToBytes(initConfiguration, schema.GroupVersion{ 115 Group: "kubeadm.k8s.io", 116 Version: kubeadm.GetKubeadmApisVersion(versionToDeploy), 117 }) 118 119 if err != nil { 120 return errors.Wrap(err, "could not marshal configuration") 121 } 122 123 fmt.Println("[bootstrap] writing init configuration for node") 124 if err := ioutil.WriteFile(skuba.KubeadmInitConfFile(), finalInitConfigurationContents, 0600); err != nil { 125 return errors.Wrap(err, "error writing init configuration") 126 } 127 128 var criSetup string 129 if _, err := os.Stat(skuba.CriDefaultsConfFile()); err == nil { 130 criSetup = "cri.configure" 131 } else if _, err := os.Stat(skuba.CriDockerDefaultsConfFile()); err == nil { 132 criSetup = "cri.sysconfig" 133 } 134 135 // bsc#1155810: generate cluster-wide kubelet root certificate 136 if err := kubernetes.GenerateKubeletRootCert(); err != nil { 137 return err 138 } 139 140 fmt.Println("[bootstrap] applying init configuration to node") 141 err = target.Apply( 142 bootstrapConfiguration, 143 "kernel.check-modules", 144 "kubeadm.reset", 145 "kubernetes.bootstrap.upload-secrets", 146 "kernel.load-modules", 147 "kernel.configure-parameters", 148 "firewalld.disable", 149 "apparmor.start", 150 criSetup, 151 "cri.start", 152 "kubelet.rootcert.upload", 153 "kubelet.servercert.create-and-upload", 154 "kubelet.configure", 155 "kubelet.enable", 156 "kubeadm.init", 157 "skuba-update.start.no-block", 158 "skuba-update-timer.enable", 159 ) 160 if err != nil { 161 return err 162 } 163 164 fmt.Printf("[bootstrap] successfully bootstrapped core components on node %q with Kubernetes: %q\n", target.Target, versionToDeploy.String()) 165 return nil 166 } 167 168 func downloadSecrets(target *deployments.Target) error { 169 if err := os.MkdirAll(filepath.Join("pki", "etcd"), 0700); err != nil { 170 return errors.Wrapf(err, "could not create %s folder", filepath.Join("pki", "etcd")) 171 } 172 173 fmt.Printf("[bootstrap] downloading secrets from bootstrapped node %q\n", target.Target) 174 for _, secretLocation := range deployments.Secrets { 175 secretData, err := target.DownloadFileContents(filepath.Join("/etc/kubernetes", secretLocation)) 176 if err != nil { 177 return err 178 } 179 if err := ioutil.WriteFile(secretLocation, []byte(secretData), 0600); err != nil { 180 return err 181 } 182 } 183 184 return nil 185 }