github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/pkg/registry/installer.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 registry 16 17 import ( 18 "fmt" 19 "net" 20 "path/filepath" 21 "strconv" 22 23 "github.com/sirupsen/logrus" 24 25 "github.com/sealerio/sealer/common" 26 "github.com/sealerio/sealer/pkg/clustercert/cert" 27 "github.com/sealerio/sealer/pkg/imagedistributor" 28 "github.com/sealerio/sealer/pkg/infradriver" 29 v2 "github.com/sealerio/sealer/types/api/v2" 30 netutils "github.com/sealerio/sealer/utils/net" 31 osutils "github.com/sealerio/sealer/utils/os" 32 strutils "github.com/sealerio/sealer/utils/strings" 33 ) 34 35 // Installer provide registry lifecycle management. 36 type Installer interface { 37 // Reconcile registry deploy hosts thought comparing current deploy host and desiredHosts and return the final registry deploy hosts. 38 // if current deploy host is less than desiredHosts , means scale-up registry node 39 // if current deploy host is bigger than desiredHosts , means scale-down registry node 40 // if current deploy host is equal targetHosts , do nothing 41 // launch registry node 42 // scale-up registry node 43 // scale down registry node 44 Reconcile(desiredHosts []net.IP) ([]net.IP, error) 45 46 // Clean all registry deploy hosts 47 Clean() error 48 } 49 50 func NewInstaller(currentDeployHost []net.IP, 51 regConfig *v2.LocalRegistry, 52 infraDriver infradriver.InfraDriver, 53 distributor imagedistributor.Distributor) Installer { 54 return &localInstaller{ 55 currentDeployHosts: currentDeployHost, 56 infraDriver: infraDriver, 57 LocalRegistry: regConfig, 58 distributor: distributor, 59 } 60 } 61 62 type localInstaller struct { 63 *v2.LocalRegistry 64 currentDeployHosts []net.IP 65 infraDriver infradriver.InfraDriver 66 distributor imagedistributor.Distributor 67 } 68 69 func (l *localInstaller) Reconcile(desiredHosts []net.IP) ([]net.IP, error) { 70 // if deployHosts is null,means first time installation 71 if len(l.currentDeployHosts) == 0 { 72 err := l.install(desiredHosts) 73 if err != nil { 74 return nil, err 75 } 76 return desiredHosts, nil 77 } 78 79 joinedHosts, deletedHosts := strutils.Diff(l.currentDeployHosts, desiredHosts) 80 // if targetHosts is equal deployHosts, just return. 81 if len(joinedHosts) == 0 && len(deletedHosts) == 0 { 82 return l.currentDeployHosts, nil 83 } 84 85 // join new hosts 86 if len(joinedHosts) != 0 { 87 err := l.install(joinedHosts) 88 if err != nil { 89 return nil, err 90 } 91 return append(l.currentDeployHosts, joinedHosts...), nil 92 } 93 94 // delete hosts 95 if len(deletedHosts) != 0 { 96 err := l.clean(deletedHosts) 97 if err != nil { 98 return nil, err 99 } 100 return netutils.RemoveIPs(l.currentDeployHosts, deletedHosts), nil 101 } 102 103 return nil, nil 104 } 105 106 func (l *localInstaller) install(deployHosts []net.IP) error { 107 logrus.Infof("will launch local private registry on %+v\n", deployHosts) 108 109 if err := l.syncBasicAuthFile(deployHosts); err != nil { 110 return err 111 } 112 113 if err := l.syncRegistryCert(deployHosts); err != nil { 114 return err 115 } 116 117 if err := l.reconcileRegistry(deployHosts); err != nil { 118 return err 119 } 120 return nil 121 } 122 123 func (l *localInstaller) syncBasicAuthFile(hosts []net.IP) error { 124 //gen basic auth info: if not config, will skip. 125 if l.RegistryConfig.Username == "" || l.RegistryConfig.Password == "" { 126 return nil 127 } 128 129 var ( 130 basicAuthFile = filepath.Join(l.infraDriver.GetClusterRootfsPath(), "etc", common.DefaultRegistryHtPasswdFile) 131 ) 132 133 if !osutils.IsFileExist(basicAuthFile) { 134 htpasswd, err := GenerateHTTPBasicAuth(l.RegistryConfig.Username, l.RegistryConfig.Password) 135 if err != nil { 136 return err 137 } 138 139 err = osutils.NewCommonWriter(basicAuthFile).WriteFile([]byte(htpasswd)) 140 if err != nil { 141 return err 142 } 143 } 144 145 for _, deployHost := range hosts { 146 err := l.infraDriver.Copy(deployHost, basicAuthFile, basicAuthFile) 147 if err != nil { 148 return fmt.Errorf("failed to copy registry auth file to %s: %v", basicAuthFile, err) 149 } 150 } 151 152 return nil 153 } 154 155 func (l *localInstaller) syncRegistryCert(hosts []net.IP) error { 156 // if deploy registry as InsecureMode ,skip to gen cert. 157 if *l.Insecure { 158 return nil 159 } 160 var ( 161 certPath = filepath.Join(l.infraDriver.GetClusterRootfsPath(), "certs") 162 certName = l.Domain 163 fullCertName = certName + ".crt" 164 fullKeyName = certName + ".key" 165 ) 166 167 certExisted := osutils.IsFileExist(filepath.Join(certPath, fullCertName)) 168 keyExisted := osutils.IsFileExist(filepath.Join(certPath, fullKeyName)) 169 170 if certExisted && !keyExisted || !certExisted && keyExisted { 171 return fmt.Errorf("failed to sync registry cert file %s or %s is not existed", fullCertName, fullKeyName) 172 } 173 174 if !certExisted && !keyExisted { 175 if err := l.gen(certPath, certName); err != nil { 176 return err 177 } 178 } 179 180 for _, deployHost := range hosts { 181 err := l.infraDriver.Copy(deployHost, certPath, certPath) 182 if err != nil { 183 return fmt.Errorf("failed to copy registry cert to deployHost: %v", err) 184 } 185 } 186 187 return nil 188 } 189 190 func (l *localInstaller) gen(certPath, certName string) error { 191 DNSNames := []string{l.Domain} 192 if l.Cert.SubjectAltName != nil { 193 DNSNames = append(DNSNames, l.Cert.SubjectAltName.IPs...) 194 DNSNames = append(DNSNames, l.Cert.SubjectAltName.DNSNames...) 195 } 196 197 regCertConfig := cert.CertificateDescriptor{ 198 CommonName: "registry-ca", 199 DNSNames: DNSNames, 200 Organization: nil, 201 Year: 100, 202 AltNames: cert.AltNames{}, 203 Usages: nil, 204 } 205 206 caGenerator := cert.NewAuthorityCertificateGenerator(regCertConfig) 207 caCert, caKey, err := caGenerator.Generate() 208 if err != nil { 209 return fmt.Errorf("unable to generate registry cert: %v", err) 210 } 211 212 // write cert file to disk 213 err = cert.NewCertificateFileManger(certPath, certName).Write(caCert, caKey) 214 if err != nil { 215 return fmt.Errorf("unable to save registry cert: %v", err) 216 } 217 218 return nil 219 } 220 221 func (l *localInstaller) reconcileRegistry(hosts []net.IP) error { 222 var ( 223 rootfs = l.infraDriver.GetClusterRootfsPath() 224 dataDir = filepath.Join(rootfs, "registry") 225 ) 226 // distribute registry data 227 if err := l.distributor.DistributeRegistry(hosts, dataDir); err != nil { 228 return err 229 } 230 231 // bash init-registry.sh ${port} ${mountData} ${domain} 232 clusterEnvs := l.infraDriver.GetClusterEnv() 233 initRegistry := fmt.Sprintf("cd %s/scripts && bash init-registry.sh %s %s %s", rootfs, strconv.Itoa(l.Port), dataDir, l.Domain) 234 for _, deployHost := range hosts { 235 if err := l.infraDriver.CmdAsync(deployHost, clusterEnvs, initRegistry); err != nil { 236 return err 237 } 238 } 239 return nil 240 } 241 242 func (l *localInstaller) clean(cleanHosts []net.IP) error { 243 deleteRegistryCommand := "if docker inspect %s 2>/dev/null;then docker rm -f %[1]s;fi && ((! nerdctl ps -a 2>/dev/null |grep %[1]s) || (nerdctl stop %[1]s && nerdctl rmi -f %[1]s))" 244 for _, deployHost := range cleanHosts { 245 if err := l.infraDriver.CmdAsync(deployHost, nil, fmt.Sprintf(deleteRegistryCommand, "sealer-registry")); err != nil { 246 return err 247 } 248 } 249 return nil 250 } 251 252 func (l *localInstaller) Clean() error { 253 return l.clean(l.currentDeployHosts) 254 }