github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/runtime/utils.go (about) 1 // Copyright © 2021 Alibaba Group Holding Ltd. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package runtime 16 17 import ( 18 "bytes" 19 "encoding/json" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "os" 24 "path" 25 "path/filepath" 26 "strings" 27 28 ocispecs "github.com/opencontainers/image-spec/specs-go/v1" 29 30 "github.com/alibaba/sealer/pkg/runtime/kubeadm_types/v1beta2" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 k8sruntime "k8s.io/apimachinery/pkg/runtime" 33 "k8s.io/apimachinery/pkg/util/yaml" 34 "k8s.io/kube-proxy/config/v1alpha1" 35 "k8s.io/kubelet/config/v1beta1" 36 37 v2 "github.com/alibaba/sealer/types/api/v2" 38 39 "github.com/pkg/errors" 40 41 "github.com/alibaba/sealer/common" 42 "github.com/alibaba/sealer/logger" 43 "github.com/alibaba/sealer/utils" 44 "github.com/alibaba/sealer/utils/ssh" 45 ) 46 47 // VersionCompare :if v1 >= v2 return true, else return false 48 func VersionCompare(v1, v2 string) bool { 49 v1 = strings.Replace(v1, "v", "", -1) 50 v2 = strings.Replace(v2, "v", "", -1) 51 v1 = strings.Split(v1, "-")[0] 52 v2 = strings.Split(v2, "-")[0] 53 v1List := strings.Split(v1, ".") 54 v2List := strings.Split(v2, ".") 55 56 if len(v1List) != 3 || len(v2List) != 3 { 57 logger.Error("error version format %s %s", v1, v2) 58 return false 59 } 60 if v1List[0] > v2List[0] { 61 return true 62 } else if v1List[0] < v2List[0] { 63 return false 64 } 65 if v1List[1] > v2List[1] { 66 return true 67 } else if v1List[1] < v2List[1] { 68 return false 69 } 70 if v1List[2] > v2List[2] { 71 return true 72 } 73 return true 74 } 75 76 func PreInitMaster0(sshClient ssh.Interface, remoteHostIP string) error { 77 err := ssh.WaitSSHReady(sshClient, 6, remoteHostIP) 78 if err != nil { 79 return fmt.Errorf("apply cloud cluster failed: %s", err) 80 } 81 // send sealer and cluster file to remote host 82 sealerPath := utils.ExecutableFilePath() 83 err = sshClient.Copy(remoteHostIP, sealerPath, common.RemoteSealerPath) 84 if err != nil { 85 return fmt.Errorf("send sealer to remote host %s failed:%v", remoteHostIP, err) 86 } 87 err = sshClient.CmdAsync(remoteHostIP, fmt.Sprintf(common.ChmodCmd, common.RemoteSealerPath)) 88 if err != nil { 89 return fmt.Errorf("chmod +x sealer on remote host %s failed:%v", remoteHostIP, err) 90 } 91 logger.Info("send sealer cmd to %s success !", remoteHostIP) 92 93 // send tmp cluster file 94 err = sshClient.Copy(remoteHostIP, common.TmpClusterfile, common.TmpClusterfile) 95 if err != nil { 96 return fmt.Errorf("send cluster file to remote host %s failed:%v", remoteHostIP, err) 97 } 98 logger.Info("send cluster file to %s success !", remoteHostIP) 99 100 // send register login info 101 authFile := common.DefaultRegistryAuthConfigDir() 102 if utils.IsFileExist(authFile) { 103 err = sshClient.Copy(remoteHostIP, authFile, common.DefaultRegistryAuthDir) 104 if err != nil { 105 return fmt.Errorf("failed to send register config %s to remote host %s err: %v", authFile, remoteHostIP, err) 106 } 107 logger.Info("send register info to %s success !", remoteHostIP) 108 } else { 109 logger.Warn("failed to find %s, if image registry is private, please login first", authFile) 110 } 111 return nil 112 } 113 114 func GetKubectlAndKubeconfig(ssh ssh.Interface, host, rootfs string) error { 115 // fetch the cluster kubeconfig, and add /etc/hosts "EIP apiserver.cluster.local" so we can get the current cluster status later 116 err := ssh.Fetch(host, path.Join(common.DefaultKubeConfigDir(), "config"), common.KubeAdminConf) 117 if err != nil { 118 return errors.Wrap(err, "failed to copy kubeconfig") 119 } 120 _, err = utils.RunSimpleCmd(fmt.Sprintf("cat /etc/hosts |grep '%s %s' || echo '%s %s' >> /etc/hosts", 121 host, common.APIServerDomain, host, common.APIServerDomain)) 122 if err != nil { 123 return errors.Wrap(err, "failed to add master IP to etc hosts") 124 } 125 if !utils.IsFileExist(common.KubectlPath) { 126 _, err = utils.CopySingleFile(filepath.Join(rootfs, "bin/kubectl"), common.KubectlPath) 127 if err != nil { 128 return err 129 } 130 err = utils.Cmd("chmod", "+x", common.KubectlPath) 131 if err != nil { 132 return errors.Wrap(err, "chmod a+x kubectl failed") 133 } 134 } 135 return nil 136 } 137 138 // LoadMetadata :read metadata via cluster image name. 139 func LoadMetadata(rootfs string) (*Metadata, error) { 140 metadataPath := filepath.Join(rootfs, common.DefaultMetadataName) 141 var metadataFile []byte 142 var err error 143 var md Metadata 144 if !utils.IsFileExist(metadataPath) { 145 return nil, nil 146 } 147 148 metadataFile, err = ioutil.ReadFile(filepath.Clean(metadataPath)) 149 if err != nil { 150 return nil, fmt.Errorf("failed to read CloudImage metadata %v", err) 151 } 152 err = json.Unmarshal(metadataFile, &md) 153 if err != nil { 154 return nil, fmt.Errorf("failed to load CloudImage metadata %v", err) 155 } 156 return &md, nil 157 } 158 159 func GetCloudImagePlatform(rootfs string) (cp ocispecs.Platform) { 160 // current we only support build on linux 161 cp = ocispecs.Platform{ 162 Architecture: "amd64", 163 OS: "linux", 164 Variant: "", 165 OSVersion: "", 166 } 167 meta, err := LoadMetadata(rootfs) 168 if err != nil { 169 return 170 } 171 if meta == nil { 172 return 173 } 174 if meta.Arch != "" { 175 cp.Architecture = meta.Arch 176 } 177 if meta.Variant != "" { 178 cp.Variant = meta.Variant 179 } 180 return 181 } 182 183 func ReadChanError(errors chan error) (err error) { 184 for { 185 if len(errors) == 0 { 186 break 187 } 188 err = fmt.Errorf("%v,%v", err, <-errors) 189 } 190 191 return 192 } 193 194 func GetMasterIPList(cluster *v2.Cluster) (masters []string) { 195 if cluster == nil { 196 return 197 } 198 return getHostsIPByRole(cluster, common.MASTER) 199 } 200 201 func GetMaster0Ip(cluster *v2.Cluster) string { 202 //cluster master ips > 0 203 return cluster.Spec.Hosts[0].IPS[0] 204 } 205 206 func GetNodeIPList(cluster *v2.Cluster) (masters []string) { 207 if cluster == nil { 208 return 209 } 210 return getHostsIPByRole(cluster, common.NODE) 211 } 212 213 func getHostsIPByRole(cluster *v2.Cluster, role string) (nodes []string) { 214 for _, host := range cluster.Spec.Hosts { 215 if utils.InList(role, host.Roles) { 216 nodes = append(nodes, host.IPS...) 217 } 218 } 219 return 220 } 221 222 func DecodeCRDFromFile(filePath string, kind string) (interface{}, error) { 223 file, err := os.Open(filepath.Clean(filePath)) 224 if err != nil { 225 return nil, fmt.Errorf("failed to dump config %v", err) 226 } 227 defer func() { 228 if err := file.Close(); err != nil { 229 logger.Warn("failed to dump config close clusterfile failed %v", err) 230 } 231 }() 232 return DecodeCRDFromReader(file, kind) 233 } 234 235 func DecodeCRDFromReader(r io.Reader, kind string) (interface{}, error) { 236 d := yaml.NewYAMLOrJSONDecoder(r, 4096) 237 238 for { 239 ext := k8sruntime.RawExtension{} 240 if err := d.Decode(&ext); err != nil { 241 if err == io.EOF { 242 break 243 } 244 return nil, err 245 } 246 // TODO: This needs to be able to handle object in other encodings and schemas. 247 ext.Raw = bytes.TrimSpace(ext.Raw) 248 if len(ext.Raw) == 0 || bytes.Equal(ext.Raw, []byte("null")) { 249 continue 250 } 251 metaType := metav1.TypeMeta{} 252 err := yaml.Unmarshal(ext.Raw, &metaType) 253 if err != nil { 254 return nil, fmt.Errorf("decode cluster failed %v", err) 255 } 256 // ext.Raw 257 if metaType.Kind == kind { 258 return TypeConversion(ext.Raw, kind) 259 } 260 } 261 return nil, nil 262 } 263 264 func DecodeCRDFromString(config string, kind string) (interface{}, error) { 265 return DecodeCRDFromReader(strings.NewReader(config), kind) 266 } 267 268 func TypeConversion(raw []byte, kind string) (i interface{}, err error) { 269 i = typeConversion(kind) 270 if i == nil { 271 return nil, fmt.Errorf("not found type %s from %s", kind, string(raw)) 272 } 273 return i, yaml.Unmarshal(raw, i) 274 } 275 276 func typeConversion(kind string) interface{} { 277 switch kind { 278 case Cluster: 279 return &v2.Cluster{} 280 case InitConfiguration: 281 return &v1beta2.InitConfiguration{} 282 case JoinConfiguration: 283 return &v1beta2.JoinConfiguration{} 284 case ClusterConfiguration: 285 return &v1beta2.ClusterConfiguration{} 286 case KubeletConfiguration: 287 return &v1beta1.KubeletConfiguration{} 288 case KubeProxyConfiguration: 289 return &v1alpha1.KubeProxyConfiguration{} 290 } 291 return nil 292 }