github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/oracle/images.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package oracle
     5  
     6  import (
     7  	"fmt"
     8  	"net/url"
     9  	"strings"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/os/series"
    13  
    14  	"github.com/juju/juju/environs/imagemetadata"
    15  	"github.com/juju/juju/environs/instances"
    16  )
    17  
    18  var windowsServerMap = map[string]string{
    19  	"Microsoft_Windows_Server_2012_R2": "win2012r2",
    20  	"Microsoft_Windows_Server_2008_R2": "win2018r2",
    21  }
    22  
    23  // instanceTypes returns all oracle cloud shapes and wraps them into instance.InstanceType
    24  // For more information about oracle cloud shapes, please see:
    25  // https://docs.oracle.com/cloud/latest/stcomputecs/STCSA/api-Shapes.html
    26  // https://docs.oracle.com/cloud/latest/stcomputecs/STCSG/GUID-1DD0FA71-AC7B-461C-B8C1-14892725AA69.htm#OCSUG210
    27  func instanceTypes(c EnvironAPI) ([]instances.InstanceType, error) {
    28  	if c == nil {
    29  		return nil, errors.Errorf("cannot use nil client")
    30  	}
    31  
    32  	// fetch all shapes from the provider
    33  	shapes, err := c.AllShapes(nil)
    34  	if err != nil {
    35  		return nil, errors.Trace(err)
    36  	}
    37  
    38  	// convert shapes to InstanceType
    39  	onlyArch := []string{"amd64"}
    40  	types := make([]instances.InstanceType, len(shapes.Result), len(shapes.Result))
    41  	for key, val := range shapes.Result {
    42  		types[key].Name = val.Name
    43  		types[key].Arches = onlyArch
    44  		types[key].Mem = val.Ram
    45  		types[key].CpuCores = uint64(val.Cpus)
    46  		types[key].RootDisk = val.Root_disk_size
    47  	}
    48  
    49  	return types, nil
    50  }
    51  
    52  // findInstanceSpec returns an *InstanceSpec, imagelist name
    53  // satisfying the supplied instanceConstraint
    54  func findInstanceSpec(
    55  	c EnvironAPI,
    56  	allImageMetadata []*imagemetadata.ImageMetadata,
    57  	instanceType []instances.InstanceType,
    58  	ic *instances.InstanceConstraint,
    59  ) (*instances.InstanceSpec, string, error) {
    60  
    61  	logger.Debugf("received %d image(s): %v", len(allImageMetadata), allImageMetadata)
    62  	version, err := series.SeriesVersion(ic.Series)
    63  	if err != nil {
    64  		return nil, "", errors.Trace(err)
    65  	}
    66  	filtered := []*imagemetadata.ImageMetadata{}
    67  	for _, val := range allImageMetadata {
    68  		if val.Version != version {
    69  			continue
    70  		}
    71  		filtered = append(filtered, val)
    72  	}
    73  
    74  	images := instances.ImageMetadataToImages(filtered)
    75  	spec, err := instances.FindInstanceSpec(images, ic, instanceType)
    76  	if err != nil {
    77  		return nil, "", errors.Trace(err)
    78  	}
    79  
    80  	imagelist, err := getImageName(c, spec.Image.Id)
    81  	if err != nil {
    82  		return nil, "", errors.Trace(err)
    83  	}
    84  
    85  	return spec, imagelist, nil
    86  }
    87  
    88  func parseImageName(name string, uri *url.URL) (*imagemetadata.ImageMetadata, error) {
    89  	var id, arch, version string
    90  	if strings.HasPrefix(name, "Ubuntu") {
    91  		meta := strings.Split(name, ".")
    92  		if len(meta) < 4 {
    93  			return nil, errors.Errorf("invalid ubuntu image name: %s", name)
    94  		}
    95  		id = meta[len(meta)-1]
    96  		arch = meta[3]
    97  		version = meta[1] + "." + strings.TrimSuffix(meta[2], "-LTS")
    98  	} else if strings.HasPrefix(name, "Microsoft") {
    99  		if ver, ok := windowsServerMap[name]; ok {
   100  			version = ver
   101  			id = ver
   102  			arch = "amd64"
   103  		} else {
   104  			return nil, errors.Errorf("unknown windows version: %q", name)
   105  		}
   106  	} else {
   107  		return nil, errors.Errorf("could not determine OS from image name: %q", name)
   108  	}
   109  
   110  	tmp := strings.Split(uri.Host, ".")
   111  	region := tmp[0]
   112  	if len(tmp) > 1 {
   113  		region = tmp[1]
   114  	}
   115  	return &imagemetadata.ImageMetadata{
   116  		Id:         id,
   117  		Arch:       arch,
   118  		Endpoint:   fmt.Sprintf("%s://%s", uri.Scheme, uri.Host),
   119  		RegionName: region,
   120  		Version:    version,
   121  	}, nil
   122  }
   123  
   124  // checkImageList creates image metadata from the oracle image list
   125  func checkImageList(c EnvironAPI) ([]*imagemetadata.ImageMetadata, error) {
   126  	if c == nil {
   127  		return nil, errors.NotFoundf("oracle client")
   128  	}
   129  
   130  	// take a list of all images that are in the oracle cloud account
   131  	resp, err := c.AllImageLists(nil)
   132  	if err != nil {
   133  		return nil, errors.Trace(err)
   134  	}
   135  
   136  	// if we don't have any images that are in
   137  	// the oracle cloud account under your username namespace
   138  	// we should let the user know this
   139  	n := len(resp.Result)
   140  	if n == 0 {
   141  		return nil, errors.NotFoundf(
   142  			"images under the current client username are",
   143  		)
   144  	}
   145  
   146  	images := make([]*imagemetadata.ImageMetadata, 0, n)
   147  	for _, val := range resp.Result {
   148  		uri, err := url.Parse(val.Uri)
   149  		if err != nil {
   150  			logger.Warningf("image with ID %q had invalid resource URI %q", val.Name, val.Uri)
   151  			continue
   152  		}
   153  		requestUri := strings.Split(uri.RequestURI(), "/")
   154  		if len(requestUri) == 0 {
   155  			continue
   156  		}
   157  		name := requestUri[len(requestUri)-1]
   158  		metadata, err := parseImageName(name, uri)
   159  		if err != nil {
   160  			logger.Warningf("failed to parse image name %s. Error was: %q", name, err)
   161  			continue
   162  		}
   163  		logger.Infof("adding image %v to metadata", metadata.String())
   164  		images = append(images, metadata)
   165  	}
   166  	return images, nil
   167  }
   168  
   169  // getImageName gets the name of the image represented by the supplied ID
   170  func getImageName(c EnvironAPI, id string) (string, error) {
   171  	if id == "" {
   172  		return "", errors.NotFoundf("empty id")
   173  	}
   174  
   175  	resp, err := c.AllImageLists(nil)
   176  	if err != nil {
   177  		return "", errors.Trace(err)
   178  	}
   179  
   180  	// if we don't have any images that are in
   181  	// the oracle cloud account under your username namespace
   182  	// we should let the user know this
   183  	if resp.Result == nil {
   184  		return "", errors.NotFoundf(
   185  			"no usable images found in your account. Please add images from the oracle market",
   186  		)
   187  	}
   188  
   189  	for _, val := range resp.Result {
   190  		if strings.Contains(val.Name, id) {
   191  			s := strings.Split(val.Name, "/")
   192  			return s[len(s)-1], nil
   193  		}
   194  	}
   195  
   196  	return "", errors.NotFoundf("image not found: %q", id)
   197  }