github.com/openshift/installer@v1.4.17/pkg/asset/imagebased/image/ignition.go (about) 1 package image 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 8 "github.com/coreos/ignition/v2/config/merge" 9 "github.com/coreos/ignition/v2/config/v3_2" 10 igntypes "github.com/coreos/ignition/v2/config/v3_2/types" 11 12 "github.com/openshift/installer/pkg/asset" 13 "github.com/openshift/installer/pkg/asset/ignition" 14 "github.com/openshift/installer/pkg/asset/ignition/bootstrap" 15 "github.com/openshift/installer/pkg/types" 16 ) 17 18 const ( 19 trustedBundlePath = "/etc/pki/ca-trust/source/anchors/additional-trust-bundle.pem" 20 21 registriesConfPath = "/etc/containers/registries.conf" 22 23 postDeploymentScriptPath = "/var/tmp/post.sh" 24 ) 25 26 // Ignition is an asset that generates the image-based installer ignition file. 27 type Ignition struct { 28 Config *igntypes.Config 29 } 30 31 // Name returns the human-friendly name of the asset. 32 func (i *Ignition) Name() string { 33 return "Image-based Installer Ignition" 34 } 35 36 // Dependencies returns the assets on which the Ignition asset depends. 37 func (i *Ignition) Dependencies() []asset.Asset { 38 return []asset.Asset{ 39 &ImageBasedInstallationConfig{}, 40 &RegistriesConf{}, 41 &PostDeployment{}, 42 } 43 } 44 45 type ibiConfigurationFile struct { 46 ExtraPartitionLabel string `json:"extraPartitionLabel,omitempty"` 47 ExtraPartitionNumber uint `json:"extraPartitionNumber,omitempty"` 48 ExtraPartitionStart string `json:"extraPartitionStart,omitempty"` 49 InstallationDisk string `json:"installationDisk"` 50 SeedImage string `json:"seedImage"` 51 SeedVersion string `json:"seedVersion"` 52 Shutdown bool `json:"shutdown,omitempty"` 53 SkipDiskCleanup bool `json:"skipDiskCleanup,omitempty"` 54 } 55 56 type ibiTemplateData struct { 57 SeedImage string 58 Proxy *types.Proxy 59 PullSecret string 60 NetworkConfig string 61 IBIConfiguration string 62 } 63 64 // Generate generates the image-based installer ignition. 65 func (i *Ignition) Generate(_ context.Context, dependencies asset.Parents) error { 66 configAsset := &ImageBasedInstallationConfig{} 67 registriesConf := &RegistriesConf{} 68 postDeployment := &PostDeployment{} 69 70 dependencies.Get(configAsset, registriesConf, postDeployment) 71 72 ibiConfig := configAsset.Config 73 if ibiConfig == nil { 74 return fmt.Errorf("%s is required", configFilename) 75 } 76 77 config := &igntypes.Config{ 78 Ignition: igntypes.Ignition{ 79 Version: igntypes.MaxVersion.String(), 80 }, 81 Passwd: igntypes.Passwd{ 82 Users: []igntypes.PasswdUser{ 83 { 84 Name: "core", 85 SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{ 86 igntypes.SSHAuthorizedKey(ibiConfig.SSHKey), 87 }, 88 }, 89 }, 90 }, 91 } 92 93 ibiConfigFile := ibiConfigurationFile{ 94 ExtraPartitionLabel: ibiConfig.ExtraPartitionLabel, 95 ExtraPartitionNumber: ibiConfig.ExtraPartitionNumber, 96 ExtraPartitionStart: ibiConfig.ExtraPartitionStart, 97 InstallationDisk: ibiConfig.InstallationDisk, 98 SeedVersion: ibiConfig.SeedVersion, 99 SeedImage: ibiConfig.SeedImage, 100 Shutdown: ibiConfig.Shutdown, 101 SkipDiskCleanup: ibiConfig.SkipDiskCleanup, 102 } 103 ibiConfigJSON, err := json.Marshal(ibiConfigFile) 104 if err != nil { 105 return fmt.Errorf("failed to marshall the ibi-configuration data: %w", err) 106 } 107 108 ibiTemplateData := &ibiTemplateData{ 109 SeedImage: ibiConfig.SeedImage, 110 Proxy: ibiConfig.Proxy, 111 PullSecret: ibiConfig.PullSecret, 112 IBIConfiguration: string(ibiConfigJSON), 113 NetworkConfig: ibiConfig.NetworkConfig.String(), 114 } 115 116 if len(registriesConf.Data) > 0 { 117 file := ignition.FileFromString(registriesConfPath, "root", 0o644, string(registriesConf.Data)) 118 config.Storage.Files = append(config.Storage.Files, file) 119 } 120 121 if ibiConfig.AdditionalTrustBundle != "" { 122 file := ignition.FileFromString(trustedBundlePath, "root", 0o600, ibiConfig.AdditionalTrustBundle) 123 config.Storage.Files = append(config.Storage.Files, file) 124 } 125 126 if postDeployment.File != nil { 127 file := ignition.FileFromString(postDeploymentScriptPath, "root", 0o755, string(postDeployment.File.Data)) 128 config.Storage.Files = append(config.Storage.Files, file) 129 } 130 131 if ibiConfig.IgnitionConfigOverride != "" { 132 if err := setIngnitionConfigOverride(config, ibiConfig.IgnitionConfigOverride); err != nil { 133 return fmt.Errorf("failed to override ignition config: %w", err) 134 } 135 } 136 137 if err := bootstrap.AddStorageFiles(config, "/", "imagebased/files", ibiTemplateData); err != nil { 138 return fmt.Errorf("failed to add image-based files to ignition config: %w", err) 139 } 140 141 enabledServices := defaultEnabledServices() 142 if ibiConfig.NetworkConfig.String() != "" { 143 enabledServices = append(enabledServices, "network-config.service") 144 } 145 if err := bootstrap.AddSystemdUnits(config, "imagebased/systemd/units", ibiTemplateData, enabledServices); err != nil { 146 return fmt.Errorf("failed to add image-based systemd units to ignition config: %w", err) 147 } 148 149 i.Config = config 150 151 return nil 152 } 153 154 func setIngnitionConfigOverride(config *igntypes.Config, override string) error { 155 ignitionConfigOverride, _, err := v3_2.Parse([]byte(override)) 156 if err != nil { 157 return fmt.Errorf("failed to parse ignition config override: %w", err) 158 } 159 160 merged, _ := merge.MergeStructTranscribe(*config, ignitionConfigOverride) 161 marshaledMerged, err := json.Marshal(merged) 162 if err != nil { 163 return fmt.Errorf("failed to marshal merged ignition config: %w", err) 164 } 165 166 if err := json.Unmarshal(marshaledMerged, config); err != nil { 167 return fmt.Errorf("failed to unmarshal merged ignition config: %w", err) 168 } 169 return nil 170 } 171 172 func defaultEnabledServices() []string { 173 return []string{ 174 "install-rhcos-and-restore-seed.service", 175 } 176 }