github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/pkg/infradriver/ssh_infradriver.go (about) 1 // Copyright © 2022 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 infradriver 16 17 import ( 18 "context" 19 "fmt" 20 "net" 21 "path/filepath" 22 "strings" 23 24 "github.com/containers/buildah/util" 25 "github.com/imdario/mergo" 26 "github.com/sealerio/sealer/common" 27 v1 "github.com/sealerio/sealer/types/api/v1" 28 v2 "github.com/sealerio/sealer/types/api/v2" 29 mapUtils "github.com/sealerio/sealer/utils/maps" 30 "github.com/sealerio/sealer/utils/shellcommand" 31 "github.com/sealerio/sealer/utils/ssh" 32 strUtil "github.com/sealerio/sealer/utils/strings" 33 "golang.org/x/sync/errgroup" 34 k8sv1 "k8s.io/api/core/v1" 35 k8snet "k8s.io/utils/net" 36 ) 37 38 type SSHInfraDriver struct { 39 sshConfigs map[string]ssh.Interface 40 hosts []net.IP 41 hostTaint map[string][]k8sv1.Taint 42 hostRolesMap map[string][]string 43 roleHostsMap map[string][]net.IP 44 hostLabels map[string]map[string]string 45 hostEnvMap map[string]map[string]string 46 clusterEnv map[string]string 47 cluster v2.Cluster 48 } 49 50 func convertTaints(taints []string) ([]k8sv1.Taint, error) { 51 var k8staints []k8sv1.Taint 52 for _, taint := range taints { 53 data, err := formatData(taint) 54 if err != nil { 55 return nil, err 56 } 57 k8staints = append(k8staints, data) 58 } 59 return k8staints, nil 60 } 61 62 // NewInfraDriver will create a new Infra driver, and if extraEnv specified, it will set env not exist in Cluster 63 func NewInfraDriver(cluster *v2.Cluster) (InfraDriver, error) { 64 var err error 65 ret := &SSHInfraDriver{ 66 cluster: *cluster, 67 sshConfigs: map[string]ssh.Interface{}, 68 roleHostsMap: map[string][]net.IP{}, 69 hostRolesMap: map[string][]string{}, 70 // todo need to separate env into app render data and sys render data 71 hostEnvMap: map[string]map[string]string{}, 72 hostLabels: map[string]map[string]string{}, 73 hostTaint: map[string][]k8sv1.Taint{}, 74 } 75 76 // initialize hosts field 77 for _, host := range cluster.Spec.Hosts { 78 ret.hosts = append(ret.hosts, host.IPS...) 79 } 80 81 if len(ret.hosts) == 0 { 82 return nil, fmt.Errorf("no hosts specified") 83 } 84 85 if err = checkAllHostsSameFamily(ret.hosts); err != nil { 86 return nil, err 87 } 88 89 if k8snet.IsIPv6String(ret.hosts[0].String()) { 90 hostIPFamilyEnv := fmt.Sprintf("%s=%s", common.EnvHostIPFamily, k8snet.IPv6) 91 if !util.StringInSlice(hostIPFamilyEnv, cluster.Spec.Env) { 92 cluster.Spec.Env = append(cluster.Spec.Env, hostIPFamilyEnv) 93 } 94 } 95 96 // initialize sshConfigs field 97 for i := range cluster.Spec.Hosts { 98 if err = mergo.Merge(&cluster.Spec.Hosts[i].SSH, &cluster.Spec.SSH); err != nil { 99 return nil, err 100 } 101 for _, ip := range cluster.Spec.Hosts[i].IPS { 102 ret.sshConfigs[ip.String()] = ssh.NewSSHClient(&cluster.Spec.Hosts[i].SSH, true) 103 } 104 } 105 106 // initialize roleHostsMap field 107 for _, host := range cluster.Spec.Hosts { 108 for _, role := range host.Roles { 109 ips, ok := ret.roleHostsMap[role] 110 if !ok { 111 ret.roleHostsMap[role] = host.IPS 112 } else { 113 ret.roleHostsMap[role] = append(ips, host.IPS...) 114 } 115 } 116 for _, ip := range host.IPS { 117 ret.hostRolesMap[ip.String()] = host.Roles 118 } 119 } 120 121 ret.clusterEnv = strUtil.ConvertStringSliceToMap(cluster.Spec.Env) 122 123 // initialize hostEnvMap and host labels field 124 // merge the host ENV and global env, the host env will overwrite cluster.Spec.Env 125 for _, host := range cluster.Spec.Hosts { 126 for _, ip := range host.IPS { 127 ret.hostEnvMap[ip.String()] = mapUtils.Merge(strUtil.ConvertStringSliceToMap(host.Env), ret.clusterEnv) 128 ret.hostLabels[ip.String()] = host.Labels 129 } 130 } 131 132 for _, host := range cluster.Spec.Hosts { 133 for _, ip := range host.IPS { 134 ret.hostTaint[ip.String()], err = convertTaints(host.Taints) 135 if err != nil { 136 return nil, err 137 } 138 } 139 } 140 141 return ret, err 142 } 143 144 func (d *SSHInfraDriver) GetHostTaints(host net.IP) []k8sv1.Taint { 145 return d.hostTaint[host.String()] 146 } 147 148 func (d *SSHInfraDriver) GetHostIPList() []net.IP { 149 return d.hosts 150 } 151 152 func (d *SSHInfraDriver) GetHostIPListByRole(role string) []net.IP { 153 return d.roleHostsMap[role] 154 } 155 156 func (d *SSHInfraDriver) GetRoleListByHostIP(ip string) []string { 157 return d.hostRolesMap[ip] 158 } 159 160 func (d *SSHInfraDriver) GetHostEnv(host net.IP) map[string]string { 161 // Set env for each host 162 hostEnv := d.hostEnvMap[host.String()] 163 if _, ok := hostEnv[common.EnvHostIP]; !ok { 164 hostEnv[common.EnvHostIP] = host.String() 165 } 166 return hostEnv 167 } 168 169 func (d *SSHInfraDriver) GetHostLabels(host net.IP) map[string]string { 170 return d.hostLabels[host.String()] 171 } 172 173 func (d *SSHInfraDriver) GetClusterEnv() map[string]string { 174 return d.clusterEnv 175 } 176 177 func (d *SSHInfraDriver) AddClusterEnv(envs []string) { 178 if d.clusterEnv == nil || envs == nil { 179 return 180 } 181 newEnv := strUtil.ConvertStringSliceToMap(envs) 182 for k, v := range newEnv { 183 d.clusterEnv[k] = v 184 } 185 } 186 187 func (d *SSHInfraDriver) GetClusterRegistry() v2.Registry { 188 return d.cluster.Spec.Registry 189 } 190 191 func (d *SSHInfraDriver) Copy(host net.IP, localFilePath, remoteFilePath string) error { 192 client := d.sshConfigs[host.String()] 193 if client == nil { 194 return fmt.Errorf("ip(%s) is not in cluster", host.String()) 195 } 196 return client.Copy(host, localFilePath, remoteFilePath) 197 } 198 199 func (d *SSHInfraDriver) CopyR(host net.IP, remoteFilePath, localFilePath string) error { 200 client := d.sshConfigs[host.String()] 201 if client == nil { 202 return fmt.Errorf("ip(%s) is not in cluster", host.String()) 203 } 204 //client.CopyR take remoteFilePath as src file 205 return client.CopyR(host, localFilePath, remoteFilePath) 206 } 207 208 func (d *SSHInfraDriver) CmdAsync(host net.IP, env map[string]string, cmd ...string) error { 209 client := d.sshConfigs[host.String()] 210 if client == nil { 211 return fmt.Errorf("ip(%s) is not in cluster", host.String()) 212 } 213 return client.CmdAsync(host, env, cmd...) 214 } 215 216 func (d *SSHInfraDriver) Cmd(host net.IP, env map[string]string, cmd string) ([]byte, error) { 217 client := d.sshConfigs[host.String()] 218 if client == nil { 219 return nil, fmt.Errorf("ip(%s) is not in cluster", host.String()) 220 } 221 return client.Cmd(host, env, cmd) 222 } 223 224 func (d *SSHInfraDriver) CmdToString(host net.IP, env map[string]string, cmd, spilt string) (string, error) { 225 client := d.sshConfigs[host.String()] 226 if client == nil { 227 return "", fmt.Errorf("ip(%s) is not in cluster", host.String()) 228 } 229 return client.CmdToString(host, env, cmd, spilt) 230 } 231 232 func (d *SSHInfraDriver) IsFileExist(host net.IP, remoteFilePath string) (bool, error) { 233 client := d.sshConfigs[host.String()] 234 if client == nil { 235 return false, fmt.Errorf("ip(%s) is not in cluster", host.String()) 236 } 237 return client.IsFileExist(host, remoteFilePath) 238 } 239 240 func (d *SSHInfraDriver) IsDirExist(host net.IP, remoteDirPath string) (bool, error) { 241 client := d.sshConfigs[host.String()] 242 if client == nil { 243 return false, fmt.Errorf("ip(%s) is not in cluster", host.String()) 244 } 245 return client.RemoteDirExist(host, remoteDirPath) 246 } 247 248 func (d *SSHInfraDriver) GetPlatform(host net.IP) (v1.Platform, error) { 249 client := d.sshConfigs[host.String()] 250 if client == nil { 251 return v1.Platform{}, fmt.Errorf("ip(%s) is not in cluster", host.String()) 252 } 253 return client.GetPlatform(host) 254 } 255 256 func (d *SSHInfraDriver) Ping(host net.IP) error { 257 client := d.sshConfigs[host.String()] 258 if client == nil { 259 return fmt.Errorf("ip(%s) is not in cluster", host.String()) 260 } 261 return client.Ping(host) 262 } 263 264 func (d *SSHInfraDriver) SetHostName(host net.IP, hostName string) error { 265 setHostNameCmd := fmt.Sprintf("hostnamectl set-hostname %s", hostName) 266 return d.CmdAsync(host, nil, setHostNameCmd) 267 } 268 269 func (d *SSHInfraDriver) SetClusterHostAliases(hosts []net.IP) error { 270 for _, host := range hosts { 271 for _, hostAliases := range d.cluster.Spec.HostAliases { 272 hostname := strings.Join(hostAliases.Hostnames, " ") 273 err := d.CmdAsync(host, nil, shellcommand.CommandSetHostAlias(hostname, hostAliases.IP)) 274 if err != nil { 275 return err 276 } 277 } 278 } 279 return nil 280 } 281 282 func (d *SSHInfraDriver) DeleteClusterHostAliases(hosts []net.IP) error { 283 for _, host := range hosts { 284 err := d.CmdAsync(host, nil, shellcommand.CommandUnSetHostAlias()) 285 if err != nil { 286 return err 287 } 288 } 289 return nil 290 } 291 292 func (d *SSHInfraDriver) GetClusterName() string { 293 return d.cluster.Name 294 } 295 296 func (d *SSHInfraDriver) GetClusterImageName() string { 297 return d.cluster.Spec.Image 298 } 299 300 func (d *SSHInfraDriver) GetClusterLaunchCmds() []string { 301 return d.cluster.Spec.CMD 302 } 303 304 func (d *SSHInfraDriver) GetClusterLaunchApps() []string { 305 return d.cluster.Spec.APPNames 306 } 307 308 func (d *SSHInfraDriver) GetHostName(hostIP net.IP) (string, error) { 309 hostName, err := d.CmdToString(hostIP, nil, "uname -n", "") 310 if err != nil { 311 return "", err 312 } 313 if hostName == "" { 314 return "", fmt.Errorf("faild to get remote hostname of host(%s)", hostIP.String()) 315 } 316 317 return strings.ToLower(hostName), nil 318 } 319 320 func (d *SSHInfraDriver) GetHostsPlatform(hosts []net.IP) (map[v1.Platform][]net.IP, error) { 321 hostsPlatformMap := make(map[v1.Platform][]net.IP) 322 323 for _, ip := range hosts { 324 plat, err := d.GetPlatform(ip) 325 if err != nil { 326 return nil, err 327 } 328 329 _, ok := hostsPlatformMap[plat] 330 if !ok { 331 hostsPlatformMap[plat] = []net.IP{ip} 332 } else { 333 hostsPlatformMap[plat] = append(hostsPlatformMap[plat], ip) 334 } 335 } 336 337 return hostsPlatformMap, nil 338 } 339 340 func (d *SSHInfraDriver) GetClusterRootfsPath() string { 341 dataRoot := d.cluster.Spec.DataRoot 342 if dataRoot == "" { 343 dataRoot = common.DefaultSealerDataDir 344 } 345 346 return filepath.Join(dataRoot, d.cluster.Name, "rootfs") 347 } 348 349 func (d *SSHInfraDriver) GetClusterBasePath() string { 350 dataRoot := d.cluster.Spec.DataRoot 351 if dataRoot == "" { 352 dataRoot = common.DefaultSealerDataDir 353 } 354 355 return filepath.Join(dataRoot, d.cluster.Name) 356 } 357 358 func (d *SSHInfraDriver) Execute(hosts []net.IP, f func(host net.IP) error) error { 359 eg, _ := errgroup.WithContext(context.Background()) 360 for _, ip := range hosts { 361 host := ip 362 eg.Go(func() error { 363 err := f(host) 364 if err != nil { 365 return fmt.Errorf("on host [%s]: %v", host.String(), err) 366 } 367 return nil 368 }) 369 } 370 371 if err := eg.Wait(); err != nil { 372 return err 373 } 374 375 return nil 376 } 377 378 func checkAllHostsSameFamily(nodeList []net.IP) error { 379 var netFamily bool 380 for i, ip := range nodeList { 381 if i == 0 { 382 netFamily = k8snet.IsIPv4(ip) 383 } 384 385 if netFamily != k8snet.IsIPv4(ip) { 386 return fmt.Errorf("all hosts must be in same ip family, but the node list given are mixed with ipv4 and ipv6: %v", nodeList) 387 } 388 } 389 return nil 390 }