github.com/openshift/installer@v1.4.17/pkg/asset/rhcos/image.go (about) 1 // Package rhcos contains assets for RHCOS. 2 package rhcos 3 4 import ( 5 "context" 6 "fmt" 7 "net/url" 8 "os" 9 "time" 10 11 "github.com/coreos/stream-metadata-go/arch" 12 "github.com/sirupsen/logrus" 13 14 "github.com/openshift/installer/pkg/asset" 15 "github.com/openshift/installer/pkg/asset/installconfig" 16 "github.com/openshift/installer/pkg/rhcos" 17 "github.com/openshift/installer/pkg/types" 18 "github.com/openshift/installer/pkg/types/aws" 19 "github.com/openshift/installer/pkg/types/azure" 20 "github.com/openshift/installer/pkg/types/baremetal" 21 "github.com/openshift/installer/pkg/types/external" 22 "github.com/openshift/installer/pkg/types/gcp" 23 "github.com/openshift/installer/pkg/types/ibmcloud" 24 "github.com/openshift/installer/pkg/types/none" 25 "github.com/openshift/installer/pkg/types/nutanix" 26 "github.com/openshift/installer/pkg/types/openstack" 27 "github.com/openshift/installer/pkg/types/ovirt" 28 "github.com/openshift/installer/pkg/types/powervs" 29 "github.com/openshift/installer/pkg/types/vsphere" 30 ) 31 32 // Image is location of RHCOS image. 33 // This stores the location of the image based on the platform. 34 // eg. on AWS this contains ami-id, on Livirt this can be the URI for QEMU image etc. 35 type Image struct { 36 ControlPlane string 37 Compute string 38 } 39 40 var _ asset.Asset = (*Image)(nil) 41 42 // Name returns the human-friendly name of the asset. 43 func (i *Image) Name() string { 44 return "Image" 45 } 46 47 // Dependencies returns dependencies used by the RHCOS asset. 48 func (i *Image) Dependencies() []asset.Asset { 49 return []asset.Asset{ 50 &installconfig.InstallConfig{}, 51 } 52 } 53 54 // Generate the RHCOS image location. 55 func (i *Image) Generate(ctx context.Context, p asset.Parents) error { 56 if oi, ok := os.LookupEnv("OPENSHIFT_INSTALL_OS_IMAGE_OVERRIDE"); ok && oi != "" { 57 logrus.Warn("Found override for OS Image. Please be warned, this is not advised") 58 *i = *MakeAsset(oi) 59 return nil 60 } 61 62 ic := &installconfig.InstallConfig{} 63 p.Get(ic) 64 config := ic.Config 65 osimageControlPlane, err := osImage(ctx, config, config.ControlPlane.Architecture) 66 if err != nil { 67 return err 68 } 69 arch := config.ControlPlane.Architecture 70 if len(config.Compute) > 0 { 71 arch = config.Compute[0].Architecture 72 } 73 osimageCompute, err := osImage(ctx, config, arch) 74 if err != nil { 75 return err 76 } 77 *i = Image{osimageControlPlane, osimageCompute} 78 return nil 79 } 80 81 //nolint:gocyclo 82 func osImage(ctx context.Context, config *types.InstallConfig, nodeArch types.Architecture) (string, error) { 83 ctx, cancel := context.WithTimeout(ctx, 30*time.Second) 84 defer cancel() 85 86 archName := arch.RpmArch(string(nodeArch)) 87 88 st, err := rhcos.FetchCoreOSBuild(ctx) 89 if err != nil { 90 return "", err 91 } 92 streamArch, err := st.GetArchitecture(archName) 93 if err != nil { 94 return "", err 95 } 96 switch config.Platform.Name() { 97 case aws.Name: 98 region := config.Platform.AWS.Region 99 if !rhcos.AMIRegions(nodeArch).Has(region) { 100 const globalResourceRegion = "us-east-1" 101 logrus.Debugf("No AMI found in %s. Using AMI from %s.", region, globalResourceRegion) 102 region = globalResourceRegion 103 } 104 osimage, err := st.GetAMI(archName, region) 105 if err != nil { 106 return "", err 107 } 108 if region != config.Platform.AWS.Region { 109 osimage = fmt.Sprintf("%s,%s", osimage, region) 110 } 111 return osimage, nil 112 case gcp.Name: 113 if streamArch.Images.Gcp != nil { 114 img := streamArch.Images.Gcp 115 return fmt.Sprintf("projects/%s/global/images/%s", img.Project, img.Name), nil 116 } 117 return "", fmt.Errorf("%s: No GCP build found", st.FormatPrefix(archName)) 118 case ibmcloud.Name: 119 if a, ok := streamArch.Artifacts["ibmcloud"]; ok { 120 return rhcos.FindArtifactURL(a) 121 } 122 return "", fmt.Errorf("%s: No ibmcloud build found", st.FormatPrefix(archName)) 123 case ovirt.Name, openstack.Name: 124 op := config.Platform.OpenStack 125 if op != nil { 126 if oi := op.ClusterOSImage; oi != "" { 127 return oi, nil 128 } 129 } 130 if a, ok := streamArch.Artifacts["openstack"]; ok { 131 return rhcos.FindArtifactURL(a) 132 } 133 return "", fmt.Errorf("%s: No openstack build found", st.FormatPrefix(archName)) 134 case azure.Name: 135 ext := streamArch.RHELCoreOSExtensions 136 if config.Platform.Azure.CloudName == azure.StackCloud { 137 return config.Platform.Azure.ClusterOSImage, nil 138 } 139 if ext == nil { 140 return "", fmt.Errorf("%s: No azure build found", st.FormatPrefix(archName)) 141 } 142 azd := ext.AzureDisk 143 if azd == nil { 144 return "", fmt.Errorf("%s: No azure build found", st.FormatPrefix(archName)) 145 } 146 return azd.URL, nil 147 case baremetal.Name: 148 // Check for image URL override 149 if oi := config.Platform.BareMetal.ClusterOSImage; oi != "" { 150 return oi, nil 151 } 152 // Use image from release payload 153 return "", nil 154 case vsphere.Name: 155 // Check for image URL override 156 if config.Platform.VSphere.ClusterOSImage != "" { 157 return config.Platform.VSphere.ClusterOSImage, nil 158 } 159 160 if a, ok := streamArch.Artifacts["vmware"]; ok { 161 // for an unknown reason vSphere OVAs are not 162 // integrity checked. Instead of going through 163 // FindArtifactURL just create the URL here. 164 artifact := a.Formats["ova"].Disk 165 u, err := url.Parse(artifact.Location) 166 if err != nil { 167 return "", err 168 } 169 170 // Add the sha256 query to the url 171 // This will later be used in pkg/rhcos/cache/cache.go 172 q := u.Query() 173 q.Set("sha256", artifact.Sha256) 174 175 u.RawQuery = q.Encode() 176 177 return u.String(), nil 178 } 179 return "", fmt.Errorf("%s: No vmware build found", st.FormatPrefix(archName)) 180 case powervs.Name: 181 // Check for image URL override 182 if config.Platform.PowerVS.ClusterOSImage != "" { 183 return config.Platform.PowerVS.ClusterOSImage, nil 184 } 185 186 if streamArch.Images.PowerVS != nil { 187 var ( 188 vpcRegion string 189 err error 190 ) 191 if config.Platform.PowerVS.VPCRegion != "" { 192 vpcRegion = config.Platform.PowerVS.VPCRegion 193 } else { 194 vpcRegion = powervs.Regions[config.Platform.PowerVS.Region].VPCRegion 195 } 196 vpcRegion, err = powervs.COSRegionForVPCRegion(vpcRegion) 197 if err != nil { 198 return "", fmt.Errorf("%s: No Power COS region found", st.FormatPrefix(archName)) 199 } 200 img := streamArch.Images.PowerVS.Regions[vpcRegion] 201 logrus.Debug("Power VS using image ", img.Object) 202 return fmt.Sprintf("%s/%s", img.Bucket, img.Object), nil 203 } 204 205 return "", fmt.Errorf("%s: No Power VS build found", st.FormatPrefix(archName)) 206 case external.Name: 207 return "", nil 208 case none.Name: 209 return "", nil 210 case nutanix.Name: 211 if config.Platform.Nutanix != nil && config.Platform.Nutanix.ClusterOSImage != "" { 212 return config.Platform.Nutanix.ClusterOSImage, nil 213 } 214 if a, ok := streamArch.Artifacts["nutanix"]; ok { 215 return rhcos.FindArtifactURL(a) 216 } 217 return "", fmt.Errorf("%s: No nutanix build found", st.FormatPrefix(archName)) 218 default: 219 return "", fmt.Errorf("invalid platform %v", config.Platform.Name()) 220 } 221 } 222 223 // MakeAsset returns an Image asset with the given os image. 224 func MakeAsset(osImage string) *Image { 225 return &Image{ 226 ControlPlane: osImage, 227 Compute: osImage, 228 } 229 }