github.com/openshift/installer@v1.4.17/pkg/asset/agent/image/ignition.go (about) 1 package image 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "net" 8 "net/url" 9 "path" 10 "path/filepath" 11 "strings" 12 13 "github.com/coreos/ignition/v2/config/util" 14 igntypes "github.com/coreos/ignition/v2/config/v3_2/types" 15 "github.com/coreos/stream-metadata-go/arch" 16 "github.com/coreos/stream-metadata-go/stream" 17 "github.com/pkg/errors" 18 "github.com/sirupsen/logrus" 19 "gopkg.in/yaml.v2" 20 21 hiveext "github.com/openshift/assisted-service/api/hiveextension/v1beta1" 22 "github.com/openshift/assisted-service/api/v1beta1" 23 "github.com/openshift/assisted-service/models" 24 "github.com/openshift/installer/pkg/asset" 25 agentcommon "github.com/openshift/installer/pkg/asset/agent" 26 "github.com/openshift/installer/pkg/asset/agent/agentconfig" 27 "github.com/openshift/installer/pkg/asset/agent/common" 28 "github.com/openshift/installer/pkg/asset/agent/gencrypto" 29 "github.com/openshift/installer/pkg/asset/agent/joiner" 30 "github.com/openshift/installer/pkg/asset/agent/manifests" 31 "github.com/openshift/installer/pkg/asset/agent/mirror" 32 "github.com/openshift/installer/pkg/asset/agent/workflow" 33 "github.com/openshift/installer/pkg/asset/ignition" 34 "github.com/openshift/installer/pkg/asset/ignition/bootstrap" 35 "github.com/openshift/installer/pkg/asset/password" 36 "github.com/openshift/installer/pkg/asset/tls" 37 "github.com/openshift/installer/pkg/types" 38 "github.com/openshift/installer/pkg/types/agent" 39 "github.com/openshift/installer/pkg/version" 40 ) 41 42 const addNodesEnvPath = "/etc/assisted/add-nodes.env" 43 const rendezvousHostEnvPath = "/etc/assisted/rendezvous-host.env" 44 const manifestPath = "/etc/assisted/manifests" 45 const hostnamesPath = "/etc/assisted/hostnames" 46 const nmConnectionsPath = "/etc/assisted/network" 47 const extraManifestPath = "/etc/assisted/extra-manifests" 48 const registriesConfPath = "/etc/containers/registries.conf" 49 const registryCABundlePath = "/etc/pki/ca-trust/source/anchors/domain.crt" 50 const clusterConfigPath = "/etc/assisted/clusterconfig" 51 52 // Ignition is an asset that generates the agent installer ignition file. 53 type Ignition struct { 54 Config *igntypes.Config 55 CPUArch string 56 RendezvousIP string 57 } 58 59 // agentTemplateData is the data used to replace values in agent template 60 // files. 61 type agentTemplateData struct { 62 ServiceProtocol string 63 PullSecret string 64 ControlPlaneAgents int 65 WorkerAgents int 66 ReleaseImages string 67 ReleaseImage string 68 ReleaseImageMirror string 69 HaveMirrorConfig bool 70 PublicContainerRegistries string 71 InfraEnvID string 72 ClusterName string 73 OSImage *models.OsImage 74 Proxy *v1beta1.Proxy 75 ConfigImageFiles string 76 ImageTypeISO string 77 PublicKeyPEM string 78 Token string 79 TokenExpiry string 80 AuthType string 81 CaBundleMount string 82 } 83 84 // Name returns the human-friendly name of the asset. 85 func (a *Ignition) Name() string { 86 return "Agent Installer Ignition" 87 } 88 89 // Dependencies returns the assets on which the Ignition asset depends. 90 func (a *Ignition) Dependencies() []asset.Asset { 91 return []asset.Asset{ 92 &workflow.AgentWorkflow{}, 93 &joiner.ClusterInfo{}, 94 &joiner.AddNodesConfig{}, 95 &manifests.AgentManifests{}, 96 &manifests.ExtraManifests{}, 97 &tls.KubeAPIServerLBSignerCertKey{}, 98 &tls.KubeAPIServerLocalhostSignerCertKey{}, 99 &tls.KubeAPIServerServiceNetworkSignerCertKey{}, 100 &tls.AdminKubeConfigSignerCertKey{}, 101 &password.KubeadminPassword{}, 102 &agentconfig.AgentConfig{}, 103 &agentconfig.AgentHosts{}, 104 &mirror.RegistriesConf{}, 105 &mirror.CaBundle{}, 106 &gencrypto.AuthConfig{}, 107 &common.InfraEnvID{}, 108 } 109 } 110 111 // Generate generates the agent installer ignition. 112 func (a *Ignition) Generate(_ context.Context, dependencies asset.Parents) error { 113 agentWorkflow := &workflow.AgentWorkflow{} 114 clusterInfo := &joiner.ClusterInfo{} 115 addNodesConfig := &joiner.AddNodesConfig{} 116 agentManifests := &manifests.AgentManifests{} 117 agentConfigAsset := &agentconfig.AgentConfig{} 118 agentHostsAsset := &agentconfig.AgentHosts{} 119 extraManifests := &manifests.ExtraManifests{} 120 authConfig := &gencrypto.AuthConfig{} 121 infraEnvAsset := &common.InfraEnvID{} 122 dependencies.Get(agentManifests, agentConfigAsset, agentHostsAsset, extraManifests, authConfig, agentWorkflow, clusterInfo, addNodesConfig, infraEnvAsset) 123 124 pwd := &password.KubeadminPassword{} 125 dependencies.Get(pwd) 126 pwdHash := string(pwd.PasswordHash) 127 128 infraEnv := agentManifests.InfraEnv 129 130 config := igntypes.Config{ 131 Ignition: igntypes.Ignition{ 132 Version: igntypes.MaxVersion.String(), 133 }, 134 Passwd: igntypes.Passwd{ 135 Users: []igntypes.PasswdUser{ 136 { 137 Name: "core", 138 SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{ 139 igntypes.SSHAuthorizedKey(infraEnv.Spec.SSHAuthorizedKey), 140 }, 141 PasswordHash: &pwdHash, 142 }, 143 }, 144 }, 145 } 146 147 clusterName := "" 148 imageTypeISO := "full-iso" 149 numMasters := 0 150 numWorkers := 0 151 enabledServices := getDefaultEnabledServices() 152 openshiftVersion := "" 153 var err error 154 var streamGetter CoreOSBuildFetcher 155 156 switch agentWorkflow.Workflow { 157 case workflow.AgentWorkflowTypeInstall: 158 // Set rendezvous IP. 159 nodeZeroIP, err := RetrieveRendezvousIP(agentConfigAsset.Config, agentHostsAsset.Hosts, agentManifests.NMStateConfigs) 160 if err != nil { 161 return err 162 } 163 a.RendezvousIP = nodeZeroIP 164 logrus.Infof("The rendezvous host IP (node0 IP) is %s", a.RendezvousIP) 165 // Define cluster name and image type. 166 clusterName = fmt.Sprintf("%s.%s", agentManifests.ClusterDeployment.Spec.ClusterName, agentManifests.ClusterDeployment.Spec.BaseDomain) 167 if agentManifests.AgentClusterInstall.Spec.PlatformType == hiveext.ExternalPlatformType { 168 imageTypeISO = "minimal-iso" 169 } 170 // Fetch the required number of master and worker nodes. 171 numMasters = agentManifests.AgentClusterInstall.Spec.ProvisionRequirements.ControlPlaneAgents 172 numWorkers = agentManifests.AgentClusterInstall.Spec.ProvisionRequirements.WorkerAgents 173 // Enable specific install services 174 enabledServices = append(enabledServices, "start-cluster-installation.service") 175 // Version is retrieved from the embedded data 176 openshiftVersion, err = version.Version() 177 if err != nil { 178 return err 179 } 180 streamGetter = DefaultCoreOSStreamGetter 181 182 case workflow.AgentWorkflowTypeAddNodes: 183 // In the add-nodes workflow, every node will act independently from the others. 184 a.RendezvousIP = "127.0.0.1" 185 // Reuse the existing cluster name. 186 clusterName = clusterInfo.ClusterName 187 // Fetch the required number of master and worker nodes. Currently only adding workers 188 // is supported, so forcing the expected number of masters to zero, and assuming implcitly 189 // that all the hosts defined are workers. 190 numMasters = 0 191 numWorkers = len(addNodesConfig.Config.Hosts) 192 193 // Enable add-nodes specific services 194 enabledServices = append(enabledServices, "agent-add-node.service") 195 // Generate add-nodes.env file 196 addNodesEnvFile := ignition.FileFromString(addNodesEnvPath, "root", 0644, getAddNodesEnv(*clusterInfo, authConfig.AgentAuthTokenExpiry)) 197 config.Storage.Files = append(config.Storage.Files, addNodesEnvFile) 198 199 // Enable auth token service 200 enabledServices = append(enabledServices, "agent-auth-token-status.service") 201 202 // Version matches the source cluster one 203 openshiftVersion = clusterInfo.Version 204 streamGetter = func(ctx context.Context) (*stream.Stream, error) { 205 return clusterInfo.OSImage, nil 206 } 207 // If defined, add the ignition endpoints 208 if err := addDay2IgnitionEndpoints(&config, *clusterInfo); err != nil { 209 return err 210 } 211 212 default: 213 return fmt.Errorf("AgentWorkflowType value not supported: %s", agentWorkflow.Workflow) 214 } 215 216 // Default to x86_64 217 archName := arch.RpmArch(types.ArchitectureAMD64) 218 if infraEnv.Spec.CpuArchitecture != "" { 219 archName = infraEnv.Spec.CpuArchitecture 220 } 221 // Examine the release payload to see if its multi 222 releaseArch, err := agentcommon.DetermineReleaseImageArch(agentManifests.GetPullSecretData(), agentManifests.ClusterImageSet.Spec.ReleaseImage) 223 if err != nil { 224 logrus.Warnf("Unable to validate the release image architecture, using infraEnv.Spec.CpuArchitecture for the release image arch") 225 releaseArch = archName 226 } else { 227 releaseArch = arch.RpmArch(releaseArch) 228 logrus.Debugf("Found Release Image Architecture: %s", releaseArch) 229 } 230 releaseArchs := []string{releaseArch} 231 if releaseArch == "multi" { 232 releaseArchs = []string{arch.RpmArch(types.ArchitectureARM64), arch.RpmArch(types.ArchitectureAMD64), arch.RpmArch(types.ArchitecturePPC64LE), arch.RpmArch(types.ArchitectureS390X)} 233 } 234 releaseImageList, err := releaseImageListWithVersion(agentManifests.ClusterImageSet.Spec.ReleaseImage, releaseArch, releaseArchs, openshiftVersion) 235 if err != nil { 236 return err 237 } 238 239 registriesConfig := &mirror.RegistriesConf{} 240 registryCABundle := &mirror.CaBundle{} 241 dependencies.Get(registriesConfig, registryCABundle) 242 243 publicContainerRegistries := getPublicContainerRegistries(registriesConfig) 244 245 releaseImageMirror := mirror.GetMirrorFromRelease(agentManifests.ClusterImageSet.Spec.ReleaseImage, registriesConfig) 246 247 infraEnvID := infraEnvAsset.ID 248 logrus.Debug("Generated random infra-env id ", infraEnvID) 249 250 osImage, err := getOSImagesInfo(archName, openshiftVersion, streamGetter) 251 if err != nil { 252 return err 253 } 254 a.CPUArch = *osImage.CPUArchitecture 255 256 caBundleMount := defineCABundleMount(registriesConfig, registryCABundle) 257 agentTemplateData := getTemplateData( 258 clusterName, 259 agentManifests.GetPullSecretData(), 260 releaseImageList, 261 agentManifests.ClusterImageSet.Spec.ReleaseImage, 262 releaseImageMirror, 263 publicContainerRegistries, 264 imageTypeISO, 265 infraEnvID, 266 authConfig.PublicKey, 267 authConfig.AuthType, 268 authConfig.AgentAuthToken, 269 authConfig.AgentAuthTokenExpiry, 270 caBundleMount, 271 len(registriesConfig.MirrorConfig) > 0, 272 numMasters, numWorkers, 273 osImage, 274 infraEnv.Spec.Proxy, 275 ) 276 277 err = bootstrap.AddStorageFiles(&config, "/", "agent/files", agentTemplateData) 278 if err != nil { 279 return err 280 } 281 282 rendezvousHostFile := ignition.FileFromString(rendezvousHostEnvPath, 283 "root", 0644, 284 getRendezvousHostEnv(agentTemplateData.ServiceProtocol, a.RendezvousIP, authConfig.AgentAuthToken, agentWorkflow.Workflow)) 285 config.Storage.Files = append(config.Storage.Files, rendezvousHostFile) 286 287 err = addBootstrapScripts(&config, agentManifests.ClusterImageSet.Spec.ReleaseImage) 288 if err != nil { 289 return err 290 } 291 292 // add ZTP manifests to manifestPath 293 for _, file := range agentManifests.FileList { 294 manifestFile := ignition.FileFromBytes(filepath.Join(manifestPath, filepath.Base(file.Filename)), 295 "root", 0600, file.Data) 296 config.Storage.Files = append(config.Storage.Files, manifestFile) 297 } 298 299 // add AgentConfig if provided 300 if agentConfigAsset.Config != nil { 301 agentConfigFile := ignition.FileFromBytes(filepath.Join(manifestPath, filepath.Base(agentConfigAsset.File.Filename)), 302 "root", 0600, agentConfigAsset.File.Data) 303 config.Storage.Files = append(config.Storage.Files, agentConfigFile) 304 } 305 306 addMacAddressToHostnameMappings(&config, agentHostsAsset) 307 308 err = addStaticNetworkConfig(&config, agentManifests.StaticNetworkConfigs) 309 if err != nil { 310 return err 311 } 312 313 // Enable pre-network-manager-config.service only when there are network configs defined 314 if len(agentManifests.StaticNetworkConfigs) != 0 { 315 enabledServices = append(enabledServices, "pre-network-manager-config.service") 316 } 317 318 err = bootstrap.AddSystemdUnits(&config, "agent/systemd/units", agentTemplateData, enabledServices) 319 if err != nil { 320 return err 321 } 322 323 addTLSData(&config, dependencies) 324 325 addMirrorData(&config, registriesConfig, registryCABundle) 326 327 err = addHostConfig(&config, agentHostsAsset) 328 if err != nil { 329 return err 330 } 331 332 err = addExtraManifests(&config, extraManifests) 333 if err != nil { 334 return err 335 } 336 337 a.Config = &config 338 return nil 339 } 340 341 func getDefaultEnabledServices() []string { 342 return []string{ 343 "agent-interactive-console.service", 344 "agent-interactive-console-serial@.service", 345 "agent-register-cluster.service", 346 "agent-import-cluster.service", 347 "agent-register-infraenv.service", 348 "agent.service", 349 "assisted-service-db.service", 350 "assisted-service-pod.service", 351 "assisted-service.service", 352 "node-zero.service", 353 "multipathd.service", 354 "selinux.service", 355 "install-status.service", 356 "set-hostname.service", 357 } 358 } 359 360 func addBootstrapScripts(config *igntypes.Config, releaseImage string) (err error) { 361 // Set up bootstrap service recording 362 if err := bootstrap.AddStorageFiles(config, 363 "/usr/local/bin/bootstrap-service-record.sh", 364 "bootstrap/files/usr/local/bin/bootstrap-service-record.sh", 365 nil); err != nil { 366 return err 367 } 368 369 // Use bootstrap script to get container images 370 relImgData := struct{ ReleaseImage string }{ 371 ReleaseImage: releaseImage, 372 } 373 for _, script := range []string{"release-image.sh", "release-image-download.sh"} { 374 if err := bootstrap.AddStorageFiles(config, 375 "/usr/local/bin/"+script, 376 "bootstrap/files/usr/local/bin/"+script+".template", 377 relImgData); err != nil { 378 return err 379 } 380 } 381 return nil 382 } 383 384 func getTemplateData(name, pullSecret, releaseImageList, releaseImage, releaseImageMirror, publicContainerRegistries, 385 imageTypeISO, infraEnvID, publicKey, authType, token, tokenExpiry, caBundleMount string, 386 haveMirrorConfig bool, 387 numMasters, numWorkers int, 388 osImage *models.OsImage, 389 proxy *v1beta1.Proxy) *agentTemplateData { 390 return &agentTemplateData{ 391 ServiceProtocol: "http", 392 PullSecret: pullSecret, 393 ControlPlaneAgents: numMasters, 394 WorkerAgents: numWorkers, 395 ReleaseImages: releaseImageList, 396 ReleaseImage: releaseImage, 397 ReleaseImageMirror: releaseImageMirror, 398 HaveMirrorConfig: haveMirrorConfig, 399 PublicContainerRegistries: publicContainerRegistries, 400 InfraEnvID: infraEnvID, 401 ClusterName: name, 402 OSImage: osImage, 403 Proxy: proxy, 404 ImageTypeISO: imageTypeISO, 405 PublicKeyPEM: publicKey, 406 AuthType: authType, 407 Token: token, 408 TokenExpiry: tokenExpiry, 409 CaBundleMount: caBundleMount, 410 } 411 } 412 413 func getRendezvousHostEnv(serviceProtocol, nodeZeroIP, token string, workflowType workflow.AgentWorkflowType) string { 414 serviceBaseURL := url.URL{ 415 Scheme: serviceProtocol, 416 Host: net.JoinHostPort(nodeZeroIP, "8090"), 417 Path: "/", 418 } 419 imageServiceBaseURL := url.URL{ 420 Scheme: serviceProtocol, 421 Host: net.JoinHostPort(nodeZeroIP, "8888"), 422 Path: "/", 423 } 424 // AGENT_AUTH_TOKEN is required to authenticate API requests against agent-installer-local auth type. 425 // PULL_SECRET_TOKEN contains the same value as AGENT_AUTH_TOKEN. The name PULL_SECRET_TOKEN is used in 426 // assisted-installer-agent, which is responsible for authenticating API requests related to agents. 427 // Historically, PULL_SECRET_TOKEN was used solely to store the pull secrets. 428 // However, as the authentication mechanisms have evolved, PULL_SECRET_TOKEN now 429 // stores a JWT (JSON Web Token) in the context of local authentication. 430 // Consequently, PULL_SECRET_TOKEN must be set with the value of AGENT_AUTH_TOKEN to maintain compatibility 431 // and ensure successful authentication. 432 // In the absence of PULL_SECRET_TOKEN, the cluster installation will wait forever. 433 434 return fmt.Sprintf(`NODE_ZERO_IP=%s 435 SERVICE_BASE_URL=%s 436 IMAGE_SERVICE_BASE_URL=%s 437 AGENT_AUTH_TOKEN=%s 438 PULL_SECRET_TOKEN=%s 439 WORKFLOW_TYPE=%s 440 `, nodeZeroIP, serviceBaseURL.String(), imageServiceBaseURL.String(), token, token, workflowType) 441 } 442 443 func getAddNodesEnv(clusterInfo joiner.ClusterInfo, authTokenExpiry string) string { 444 return fmt.Sprintf(`CLUSTER_ID=%s 445 CLUSTER_NAME=%s 446 CLUSTER_API_VIP_DNS_NAME=%s 447 AGENT_AUTH_TOKEN_EXPIRY=%s 448 `, clusterInfo.ClusterID, clusterInfo.ClusterName, clusterInfo.APIDNSName, authTokenExpiry) 449 } 450 451 func addStaticNetworkConfig(config *igntypes.Config, staticNetworkConfig []*models.HostStaticNetworkConfig) (err error) { 452 if len(staticNetworkConfig) == 0 { 453 return nil 454 } 455 456 // Get the static network configuration from nmstate and generate NetworkManager ignition files 457 filesList, err := manifests.GetNMIgnitionFiles(staticNetworkConfig) 458 if err != nil { 459 return err 460 } 461 462 for i := range filesList { 463 nmFilePath := path.Join(nmConnectionsPath, filesList[i].FilePath) 464 nmStateIgnFile := ignition.FileFromBytes(nmFilePath, "root", 0600, []byte(filesList[i].FileContents)) 465 config.Storage.Files = append(config.Storage.Files, nmStateIgnFile) 466 } 467 468 nmStateScriptFilePath := "/usr/local/bin/pre-network-manager-config.sh" 469 // A local version of the assisted-service internal script is currently used 470 nmStateScript := ignition.FileFromBytes(nmStateScriptFilePath, "root", 0755, []byte(manifests.PreNetworkConfigScript)) 471 config.Storage.Files = append(config.Storage.Files, nmStateScript) 472 473 return nil 474 } 475 476 func addTLSData(config *igntypes.Config, dependencies asset.Parents) { 477 certKeys := []asset.Asset{ 478 &tls.KubeAPIServerLBSignerCertKey{}, 479 &tls.KubeAPIServerLocalhostSignerCertKey{}, 480 &tls.KubeAPIServerServiceNetworkSignerCertKey{}, 481 &tls.AdminKubeConfigSignerCertKey{}, 482 } 483 dependencies.Get(certKeys...) 484 485 for _, ck := range certKeys { 486 for _, d := range ck.(asset.WritableAsset).Files() { 487 f := ignition.FileFromBytes(path.Join("/opt/agent", d.Filename), "root", 0600, d.Data) 488 config.Storage.Files = append(config.Storage.Files, f) 489 } 490 } 491 492 pwd := &password.KubeadminPassword{} 493 dependencies.Get(pwd) 494 config.Storage.Files = append(config.Storage.Files, 495 ignition.FileFromBytes("/opt/agent/tls/kubeadmin-password.hash", "root", 0600, pwd.PasswordHash)) 496 } 497 498 func addMirrorData(config *igntypes.Config, registriesConfig *mirror.RegistriesConf, registryCABundle *mirror.CaBundle) { 499 500 // This is required for assisted-service to build the ICSP for openshift-install 501 if registriesConfig.File != nil { 502 registriesFile := ignition.FileFromBytes(registriesConfPath, 503 "root", 0644, registriesConfig.File.Data) 504 config.Storage.Files = append(config.Storage.Files, registriesFile) 505 } 506 507 // This is required for the agent to run the podman commands to the mirror 508 if registryCABundle.File != nil && len(registryCABundle.File.Data) > 0 { 509 caFile := ignition.FileFromBytes(registryCABundlePath, 510 "root", 0600, registryCABundle.File.Data) 511 config.Storage.Files = append(config.Storage.Files, caFile) 512 } 513 } 514 515 func defineCABundleMount(registriesConfig *mirror.RegistriesConf, registryCABundle *mirror.CaBundle) string { 516 // By default, the current host CA bundle is used (it will also contain eventually a user CA bundle, if 517 // defined in the AdditionalTrustBundle field of install-config.yaml). 518 hostSourceCABundle := "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" 519 520 // If mirror registry is configured and the user provided a bundle, then let's mount just the user one. 521 if len(registriesConfig.MirrorConfig) > 0 && registryCABundle.File != nil && len(registryCABundle.File.Data) > 0 { 522 hostSourceCABundle = registryCABundlePath 523 } 524 525 return fmt.Sprintf("-v %s:/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem:z", hostSourceCABundle) 526 } 527 528 // Creates a file named with a host's MAC address. The desired hostname 529 // is the file's content. The files are read by a systemd service that 530 // sets the hostname using "hostnamectl set-hostname" when the ISO boots. 531 func addMacAddressToHostnameMappings( 532 config *igntypes.Config, 533 agentHostsAsset *agentconfig.AgentHosts) { 534 if len(agentHostsAsset.Hosts) == 0 { 535 return 536 } 537 for _, host := range agentHostsAsset.Hosts { 538 if host.Hostname != "" { 539 file := ignition.FileFromBytes(filepath.Join(hostnamesPath, 540 strings.ToLower(filepath.Base(host.Interfaces[0].MacAddress))), 541 "root", 0600, []byte(host.Hostname)) 542 config.Storage.Files = append(config.Storage.Files, file) 543 } 544 } 545 } 546 547 func addHostConfig(config *igntypes.Config, agentHosts *agentconfig.AgentHosts) error { 548 confs, err := agentHosts.HostConfigFiles() 549 if err != nil { 550 return err 551 } 552 553 for path, content := range confs { 554 hostConfigFile := ignition.FileFromBytes(filepath.Join("/etc/assisted/hostconfig", path), "root", 0644, content) 555 config.Storage.Files = append(config.Storage.Files, hostConfigFile) 556 } 557 return nil 558 } 559 560 func addDay2IgnitionEndpoints(config *igntypes.Config, clusterInfo joiner.ClusterInfo) error { 561 if clusterInfo.IgnitionEndpointWorker == nil { 562 return nil 563 } 564 565 user := "root" 566 mode := 0644 567 config.Storage.Directories = append(config.Storage.Directories, igntypes.Directory{ 568 Node: igntypes.Node{ 569 Path: clusterConfigPath, 570 User: igntypes.NodeUser{ 571 Name: &user, 572 }, 573 Overwrite: util.BoolToPtr(true), 574 }, 575 DirectoryEmbedded1: igntypes.DirectoryEmbedded1{ 576 Mode: &mode, 577 }, 578 }) 579 580 workerIgnitionBytes, err := json.Marshal(clusterInfo.IgnitionEndpointWorker) 581 if err != nil { 582 return err 583 } 584 workerIgnitionFile := ignition.FileFromBytes(path.Join(clusterConfigPath, "worker-ignition-endpoint.json"), user, mode, workerIgnitionBytes) 585 config.Storage.Files = append(config.Storage.Files, workerIgnitionFile) 586 return nil 587 } 588 589 func addExtraManifests(config *igntypes.Config, extraManifests *manifests.ExtraManifests) error { 590 591 user := "root" 592 mode := 0644 593 594 config.Storage.Directories = append(config.Storage.Directories, igntypes.Directory{ 595 Node: igntypes.Node{ 596 Path: extraManifestPath, 597 User: igntypes.NodeUser{ 598 Name: &user, 599 }, 600 Overwrite: util.BoolToPtr(true), 601 }, 602 DirectoryEmbedded1: igntypes.DirectoryEmbedded1{ 603 Mode: &mode, 604 }, 605 }) 606 607 for _, file := range extraManifests.FileList { 608 609 type unstructured map[string]interface{} 610 611 yamlList, err := manifests.GetMultipleYamls[unstructured](file.Data) 612 if err != nil { 613 return errors.Wrapf(err, "could not decode YAML for %s", file.Filename) 614 } 615 616 for n, manifest := range yamlList { 617 m, err := yaml.Marshal(manifest) 618 if err != nil { 619 return err 620 } 621 622 base := filepath.Base(file.Filename) 623 ext := filepath.Ext(file.Filename) 624 baseWithoutExt := strings.TrimSuffix(base, ext) 625 baseFileName := filepath.Join(extraManifestPath, baseWithoutExt) 626 fileName := fmt.Sprintf("%s-%d%s", baseFileName, n, ext) 627 628 extraFile := ignition.FileFromBytes(fileName, user, mode, m) 629 config.Storage.Files = append(config.Storage.Files, extraFile) 630 } 631 } 632 633 return nil 634 } 635 636 func getOSImagesInfo(cpuArch string, openshiftVersion string, streamGetter CoreOSBuildFetcher) (*models.OsImage, error) { 637 st, err := streamGetter(context.Background()) 638 if err != nil { 639 return nil, err 640 } 641 642 osImage := &models.OsImage{ 643 CPUArchitecture: &cpuArch, 644 } 645 osImage.OpenshiftVersion = &openshiftVersion 646 647 streamArch, err := st.GetArchitecture(cpuArch) 648 if err != nil { 649 return nil, err 650 } 651 652 artifacts, ok := streamArch.Artifacts["metal"] 653 if !ok { 654 return nil, fmt.Errorf("failed to retrieve coreos metal info for architecture %s", cpuArch) 655 } 656 osImage.Version = &artifacts.Release 657 658 isoFormat, ok := artifacts.Formats["iso"] 659 if !ok { 660 return nil, fmt.Errorf("failed to retrieve coreos ISO info for architecture %s", cpuArch) 661 } 662 osImage.URL = &isoFormat.Disk.Location 663 664 return osImage, nil 665 } 666 667 // RetrieveRendezvousIP Returns the Rendezvous IP from either AgentConfig or NMStateConfig 668 func RetrieveRendezvousIP(agentConfig *agent.Config, hosts []agent.Host, nmStateConfigs []*v1beta1.NMStateConfig) (string, error) { 669 var err error 670 var rendezvousIP string 671 672 if agentConfig != nil && agentConfig.RendezvousIP != "" { 673 rendezvousIP = agentConfig.RendezvousIP 674 logrus.Debug("RendezvousIP from the AgentConfig ", rendezvousIP) 675 676 } else { 677 rendezvousIP, err = manifests.GetNodeZeroIP(hosts, nmStateConfigs) 678 if err != nil { 679 return "", errors.Wrap(err, "missing rendezvousIP in agent-config, at least one host networkConfig, or at least one NMStateConfig manifest") 680 } 681 logrus.Debug("RendezvousIP from the NMStateConfig ", rendezvousIP) 682 } 683 684 // Convert IPv6 address to canonical to match host format for comparisons 685 addr := net.ParseIP(rendezvousIP) 686 if addr == nil { 687 err = errors.New(fmt.Sprintf("invalid rendezvous IP: %s", rendezvousIP)) 688 return "", err 689 } 690 return addr.String(), err 691 } 692 693 func getPublicContainerRegistries(registriesConfig *mirror.RegistriesConf) string { 694 695 if len(registriesConfig.MirrorConfig) > 0 { 696 registries := []string{} 697 for _, config := range registriesConfig.MirrorConfig { 698 location := strings.SplitN(config.Location, "/", 2)[0] 699 700 allRegs := fmt.Sprint(registries) 701 if !strings.Contains(allRegs, location) { 702 registries = append(registries, location) 703 } 704 } 705 return strings.Join(registries, ",") 706 } 707 708 return "quay.io" 709 }