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  }