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 }