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

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package oci_test
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	gomock "github.com/golang/mock/gomock"
    11  	"github.com/juju/errors"
    12  	ocitesting "github.com/juju/juju/provider/oci/testing"
    13  	jujutesting "github.com/juju/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	ociCore "github.com/oracle/oci-go-sdk/core"
    18  
    19  	"github.com/juju/juju/provider/oci"
    20  )
    21  
    22  type imagesSuite struct {
    23  	jujutesting.BaseSuite
    24  
    25  	testImageID     string
    26  	testCompartment string
    27  }
    28  
    29  var _ = gc.Suite(&imagesSuite{})
    30  
    31  func (i *imagesSuite) SetUpTest(c *gc.C) {
    32  	i.BaseSuite.SetUpTest(c)
    33  	oci.SetImageCache(&oci.ImageCache{})
    34  
    35  	i.testImageID = "ocid1.image.oc1.phx.aaaaaaaaa5mikpf5fktj4x47bx4p4ak4g5jyuyukkxpdg4nll36qzqwjzd2q"
    36  	i.testCompartment = "ocid1.compartment.oc1..aaaaaaaaakr75vvb5yx4nkm7ag7ekvluap7afa2y4zprswuprcnehqecwqga"
    37  }
    38  
    39  func (i *imagesSuite) TestNewImageVersion(c *gc.C) {
    40  	name := "Canonical-Ubuntu-14.04-2017.08.22-0"
    41  	img := ociCore.Image{
    42  		DisplayName: &name,
    43  	}
    44  	timeStamp, _ := time.Parse("2006.01.02", "2017.08.22")
    45  	version, err := oci.NewImageVersion(img)
    46  	c.Assert(err, gc.IsNil)
    47  	c.Assert(version.TimeStamp, gc.Equals, timeStamp)
    48  	c.Assert(version.Revision, gc.Equals, 0)
    49  }
    50  
    51  func (i *imagesSuite) TestNewImageVersionInvalidDate(c *gc.C) {
    52  	name := "Canonical-Ubuntu-14.04-NotARealDate-0"
    53  	img := ociCore.Image{
    54  		DisplayName: &name,
    55  	}
    56  	_, err := oci.NewImageVersion(img)
    57  	c.Assert(err, gc.ErrorMatches, "parsing time for.*")
    58  }
    59  
    60  func (i *imagesSuite) TestNewImageVersionInvalidRevision(c *gc.C) {
    61  	name := "Canonical-Ubuntu-14.04-2017.08.22-IShouldBeNumeric"
    62  	img := ociCore.Image{
    63  		DisplayName: &name,
    64  	}
    65  	_, err := oci.NewImageVersion(img)
    66  	c.Assert(err, gc.ErrorMatches, "parsing revision for.*")
    67  }
    68  
    69  func (i *imagesSuite) TestNewImageVersionInvalidName(c *gc.C) {
    70  	name := "fakeInvalidName"
    71  	img := ociCore.Image{
    72  		DisplayName: &name,
    73  	}
    74  	_, err := oci.NewImageVersion(img)
    75  	c.Assert(err, gc.ErrorMatches, "invalid image display name.*")
    76  
    77  	img = ociCore.Image{}
    78  	_, err = oci.NewImageVersion(img)
    79  	c.Assert(err, gc.ErrorMatches, "image does not have a display name")
    80  }
    81  
    82  func makeStringPointer(name string) *string {
    83  	return &name
    84  }
    85  
    86  func makeIntPointer(name int) *int {
    87  	return &name
    88  }
    89  
    90  func (i *imagesSuite) TestInstanceTypes(c *gc.C) {
    91  	ctrl := gomock.NewController(c)
    92  	compute := ocitesting.NewMockOCIComputeClient(ctrl)
    93  	defer ctrl.Finish()
    94  
    95  	request := ociCore.ListShapesRequest{
    96  		CompartmentId: &i.testCompartment,
    97  		ImageId:       &i.testImageID,
    98  	}
    99  
   100  	response := ociCore.ListShapesResponse{
   101  		Items: []ociCore.Shape{
   102  			{
   103  				Shape: makeStringPointer("VM.Standard1.1"),
   104  			},
   105  			{
   106  				Shape: makeStringPointer("VM.Standard2.1"),
   107  			},
   108  			{
   109  				Shape: makeStringPointer("VM.Standard1.2"),
   110  			},
   111  		},
   112  	}
   113  
   114  	compute.EXPECT().ListShapes(context.Background(), request).Return(response, nil)
   115  
   116  	types, err := oci.InstanceTypes(compute, &i.testCompartment, &i.testImageID)
   117  	c.Assert(err, gc.IsNil)
   118  	c.Check(types, gc.HasLen, 3)
   119  
   120  	spec, ok := oci.ShapeSpecs["VM.Standard1.1"]
   121  	c.Assert(ok, jc.IsTrue)
   122  	c.Check(int(types[0].Mem), gc.Equals, spec.Memory)
   123  	c.Check(int(types[0].CpuCores), gc.Equals, spec.Cpus)
   124  
   125  	spec, ok = oci.ShapeSpecs["VM.Standard2.1"]
   126  	c.Assert(ok, jc.IsTrue)
   127  	c.Check(int(types[1].Mem), gc.Equals, spec.Memory)
   128  	c.Check(int(types[1].CpuCores), gc.Equals, spec.Cpus)
   129  
   130  	spec, ok = oci.ShapeSpecs["VM.Standard1.2"]
   131  	c.Assert(ok, jc.IsTrue)
   132  	c.Check(int(types[2].Mem), gc.Equals, spec.Memory)
   133  	c.Check(int(types[2].CpuCores), gc.Equals, spec.Cpus)
   134  }
   135  
   136  func (i *imagesSuite) TestInstanceTypesNilClient(c *gc.C) {
   137  	_, err := oci.InstanceTypes(nil, &i.testCompartment, &i.testImageID)
   138  	c.Assert(err, gc.ErrorMatches, "cannot use nil client")
   139  }
   140  
   141  func (i *imagesSuite) TestInstanceTypesImageWithUnknownShape(c *gc.C) {
   142  	ctrl := gomock.NewController(c)
   143  	compute := ocitesting.NewMockOCIComputeClient(ctrl)
   144  	defer ctrl.Finish()
   145  
   146  	request := ociCore.ListShapesRequest{
   147  		CompartmentId: &i.testCompartment,
   148  		ImageId:       &i.testImageID,
   149  	}
   150  
   151  	response := ociCore.ListShapesResponse{
   152  		Items: []ociCore.Shape{
   153  			{
   154  				Shape: makeStringPointer("IDontExistInTheOCIProviderWasProbablyAddedLaterAndThatsWhyIHopeTheyWillAddResourceDetailsToShapesAPISoWeDontNeedToMaintainAMapping"),
   155  			},
   156  			{
   157  				Shape: makeStringPointer("VM.Standard2.1"),
   158  			},
   159  			{
   160  				Shape: makeStringPointer("VM.Standard1.2"),
   161  			},
   162  		},
   163  	}
   164  
   165  	compute.EXPECT().ListShapes(context.Background(), request).Return(response, nil)
   166  
   167  	types, err := oci.InstanceTypes(compute, &i.testCompartment, &i.testImageID)
   168  	c.Assert(err, gc.IsNil)
   169  	c.Check(types, gc.HasLen, 2)
   170  }
   171  
   172  func (i *imagesSuite) TestNewInstanceImage(c *gc.C) {
   173  	image := ociCore.Image{
   174  		CompartmentId:          &i.testCompartment,
   175  		Id:                     &i.testImageID,
   176  		OperatingSystem:        makeStringPointer("Canonical Ubuntu"),
   177  		OperatingSystemVersion: makeStringPointer("14.04"),
   178  		DisplayName:            makeStringPointer("Canonical-Ubuntu-14.04-2018.01.11-0"),
   179  	}
   180  
   181  	imgType, err := oci.NewInstanceImage(image, &i.testCompartment)
   182  	c.Assert(err, gc.IsNil)
   183  	c.Check(imgType.ImageType, gc.Equals, oci.ImageTypeGeneric)
   184  	c.Check(imgType.Series, gc.Equals, "trusty")
   185  	c.Check(imgType.CompartmentId, gc.NotNil)
   186  	c.Check(*imgType.CompartmentId, gc.Equals, i.testCompartment)
   187  	c.Check(imgType.Id, gc.Equals, i.testImageID)
   188  }
   189  
   190  func (i *imagesSuite) TestNewInstanceImageUnknownOS(c *gc.C) {
   191  	image := ociCore.Image{
   192  		CompartmentId:          &i.testCompartment,
   193  		Id:                     &i.testImageID,
   194  		OperatingSystem:        makeStringPointer("NotKnownToJuju"),
   195  		OperatingSystemVersion: makeStringPointer("14.04"),
   196  		DisplayName:            makeStringPointer("Canonical-Ubuntu-14.04-2018.01.11-0"),
   197  	}
   198  
   199  	_, err := oci.NewInstanceImage(image, &i.testCompartment)
   200  	c.Assert(err, gc.ErrorMatches, "os NotKnownToJuju not supported")
   201  }
   202  
   203  func (i *imagesSuite) TestRefreshImageCache(c *gc.C) {
   204  	ctrl := gomock.NewController(c)
   205  	compute := ocitesting.NewMockOCIComputeClient(ctrl)
   206  	defer ctrl.Finish()
   207  
   208  	fakeUbuntuID := "fakeUbuntu1"
   209  	fakeUbuntuIDSecond := "fakeUbuntu2"
   210  
   211  	listImageRequest, listImageResponse := makeListImageRequestResponse(
   212  		[]ociCore.Image{
   213  			{
   214  				CompartmentId:          &i.testCompartment,
   215  				Id:                     &fakeUbuntuID,
   216  				OperatingSystem:        makeStringPointer("Canonical Ubuntu"),
   217  				OperatingSystemVersion: makeStringPointer("14.04"),
   218  				DisplayName:            makeStringPointer("Canonical-Ubuntu-14.04-2018.01.11-0"),
   219  			},
   220  			{
   221  				CompartmentId:          &i.testCompartment,
   222  				Id:                     &fakeUbuntuIDSecond,
   223  				OperatingSystem:        makeStringPointer("Canonical Ubuntu"),
   224  				OperatingSystemVersion: makeStringPointer("14.04"),
   225  				DisplayName:            makeStringPointer("Canonical-Ubuntu-14.04-2018.01.12-0"),
   226  			},
   227  			{
   228  				CompartmentId:          &i.testCompartment,
   229  				Id:                     makeStringPointer("fakeCentOS"),
   230  				OperatingSystem:        makeStringPointer("CentOS"),
   231  				OperatingSystemVersion: makeStringPointer("7"),
   232  				DisplayName:            makeStringPointer("CentOS-7-2017.10.19-0"),
   233  			},
   234  		},
   235  	)
   236  	shapesRequestUbuntu, shapesResponseUbuntu := makeShapesRequestResponse(
   237  		i.testCompartment, fakeUbuntuID, []string{"VM.Standard2.1", "VM.Standard1.2"})
   238  
   239  	shapesRequestUbuntuSecond := ociCore.ListShapesRequest{
   240  		CompartmentId: &i.testCompartment,
   241  		ImageId:       &fakeUbuntuIDSecond,
   242  	}
   243  
   244  	shapesRequestCentOS, shapesResponseCentOS := makeShapesRequestResponse(
   245  		i.testCompartment, "fakeCentOS", []string{"VM.Standard1.2"})
   246  
   247  	gomock.InOrder(
   248  		compute.EXPECT().ListImages(context.Background(), listImageRequest).Return(listImageResponse, nil),
   249  		compute.EXPECT().ListShapes(context.Background(), shapesRequestUbuntu).Return(shapesResponseUbuntu, nil),
   250  		compute.EXPECT().ListShapes(context.Background(), shapesRequestUbuntuSecond).Return(shapesResponseUbuntu, nil),
   251  		compute.EXPECT().ListShapes(context.Background(), shapesRequestCentOS).Return(shapesResponseCentOS, nil),
   252  	)
   253  
   254  	imgCache, err := oci.RefreshImageCache(compute, &i.testCompartment)
   255  	c.Assert(err, gc.IsNil)
   256  	c.Assert(imgCache, gc.NotNil)
   257  	c.Check(imgCache.ImageMap(), gc.HasLen, 2)
   258  
   259  	imageMap := imgCache.ImageMap()
   260  	c.Check(imageMap["trusty"], gc.HasLen, 2)
   261  	c.Check(imageMap["centos7"], gc.HasLen, 1)
   262  
   263  	timeStamp, _ := time.Parse("2006.01.02", "2018.01.12")
   264  
   265  	// Check that the first image in the array is the newest one
   266  	c.Assert(imageMap["trusty"][0].Version.TimeStamp, gc.Equals, timeStamp)
   267  
   268  	// Check that InstanceTypes are set
   269  	c.Assert(imageMap["trusty"][0].InstanceTypes, gc.HasLen, 2)
   270  	c.Assert(imageMap["centos7"][0].InstanceTypes, gc.HasLen, 1)
   271  }
   272  
   273  func (i *imagesSuite) TestRefreshImageCacheFetchFromCache(c *gc.C) {
   274  	ctrl := gomock.NewController(c)
   275  	compute := ocitesting.NewMockOCIComputeClient(ctrl)
   276  	defer ctrl.Finish()
   277  
   278  	compute.EXPECT().ListImages(gomock.Any(), gomock.Any()).Return(ociCore.ListImagesResponse{}, nil)
   279  
   280  	imgCache, err := oci.RefreshImageCache(compute, &i.testCompartment)
   281  	c.Assert(err, gc.IsNil)
   282  	c.Assert(imgCache, gc.NotNil)
   283  
   284  	fromCache, err := oci.RefreshImageCache(compute, &i.testCompartment)
   285  	c.Assert(err, gc.IsNil)
   286  	c.Check(imgCache, gc.DeepEquals, fromCache)
   287  }
   288  
   289  func (i *imagesSuite) TestRefreshImageCacheStaleCache(c *gc.C) {
   290  	ctrl := gomock.NewController(c)
   291  	compute := ocitesting.NewMockOCIComputeClient(ctrl)
   292  	defer ctrl.Finish()
   293  
   294  	compute.EXPECT().ListImages(gomock.Any(), gomock.Any()).Return(ociCore.ListImagesResponse{}, nil).Times(2)
   295  
   296  	imgCache, err := oci.RefreshImageCache(compute, &i.testCompartment)
   297  	c.Assert(err, gc.IsNil)
   298  	c.Assert(imgCache, gc.NotNil)
   299  
   300  	now := time.Now()
   301  
   302  	// No need to check the value. gomock will assert if ListImages
   303  	// is not called twice
   304  	imgCache.SetLastRefresh(now.Add(-31 * time.Minute))
   305  	_, err = oci.RefreshImageCache(compute, &i.testCompartment)
   306  	c.Assert(err, gc.IsNil)
   307  }
   308  
   309  func (i *imagesSuite) TestRefreshImageCacheWithInvalidImage(c *gc.C) {
   310  	ctrl := gomock.NewController(c)
   311  	compute := ocitesting.NewMockOCIComputeClient(ctrl)
   312  	defer ctrl.Finish()
   313  
   314  	listImageRequest, listImageResponse := makeListImageRequestResponse(
   315  		[]ociCore.Image{
   316  			{
   317  				CompartmentId:          &i.testCompartment,
   318  				Id:                     makeStringPointer("fakeUbuntu1"),
   319  				OperatingSystem:        makeStringPointer("Canonical Ubuntu"),
   320  				OperatingSystemVersion: makeStringPointer("14.04"),
   321  				DisplayName:            makeStringPointer("Canonical-Ubuntu-14.04-2018.01.11-0"),
   322  			},
   323  			{
   324  				CompartmentId:          &i.testCompartment,
   325  				Id:                     makeStringPointer("fake image id for bad image"),
   326  				OperatingSystem:        makeStringPointer("CentOS"),
   327  				OperatingSystemVersion: makeStringPointer("7"),
   328  				DisplayName:            makeStringPointer("BadlyFormatedDisplayName_IshouldBeIgnored"),
   329  			},
   330  		},
   331  	)
   332  
   333  	shapesRequestUbuntu, shapesResponseUbuntu := makeShapesRequestResponse(
   334  		i.testCompartment, "fakeUbuntu1", []string{"VM.Standard2.1", "VM.Standard1.2"})
   335  
   336  	shapesRequestBadImage, shapesResponseBadImage := makeShapesRequestResponse(
   337  		i.testCompartment, "fake image id for bad image", []string{"VM.Standard1.2"})
   338  
   339  	gomock.InOrder(
   340  		compute.EXPECT().ListImages(context.Background(), listImageRequest).Return(listImageResponse, nil),
   341  		compute.EXPECT().ListShapes(context.Background(), shapesRequestUbuntu).Return(shapesResponseUbuntu, nil),
   342  		compute.EXPECT().ListShapes(context.Background(), shapesRequestBadImage).Return(shapesResponseBadImage, nil),
   343  	)
   344  
   345  	imgCache, err := oci.RefreshImageCache(compute, &i.testCompartment)
   346  	c.Assert(err, gc.IsNil)
   347  	c.Assert(imgCache, gc.NotNil)
   348  	c.Check(imgCache.ImageMap(), gc.HasLen, 1)
   349  	imageMap := imgCache.ImageMap()
   350  
   351  	c.Check(imageMap["trusty"][0].Id, gc.Equals, "fakeUbuntu1")
   352  }
   353  
   354  func (i *imagesSuite) TestRefreshImageCacheWithAPIError(c *gc.C) {
   355  	ctrl := gomock.NewController(c)
   356  	compute := ocitesting.NewMockOCIComputeClient(ctrl)
   357  	defer ctrl.Finish()
   358  
   359  	compute.EXPECT().ListImages(gomock.Any(), gomock.Any()).Return(ociCore.ListImagesResponse{}, errors.Errorf("awww snap!"))
   360  	_, err := oci.RefreshImageCache(compute, &i.testCompartment)
   361  	c.Assert(err, gc.ErrorMatches, "listing provider images.*awww snap!")
   362  }
   363  
   364  func (i *imagesSuite) TestImageMetadataFromCache(c *gc.C) {
   365  	image := ociCore.Image{
   366  		CompartmentId:          &i.testCompartment,
   367  		Id:                     &i.testImageID,
   368  		OperatingSystem:        makeStringPointer("Canonical Ubuntu"),
   369  		OperatingSystemVersion: makeStringPointer("14.04"),
   370  		DisplayName:            makeStringPointer("Canonical-Ubuntu-14.04-2018.01.11-0"),
   371  	}
   372  
   373  	imgType, err := oci.NewInstanceImage(image, &i.testCompartment)
   374  	c.Assert(err, gc.IsNil)
   375  
   376  	cache := &oci.ImageCache{}
   377  	images := map[string][]oci.InstanceImage{
   378  		"trusty": {
   379  			imgType,
   380  		},
   381  	}
   382  	cache.SetImages(images)
   383  	metadata := cache.ImageMetadata("trusty", "")
   384  	c.Assert(metadata, gc.HasLen, 1)
   385  	// generic images default to ImageTypeVM
   386  	c.Assert(metadata[0].VirtType, gc.Equals, string(oci.ImageTypeVM))
   387  
   388  	// explicitly set ImageTypeBM on generic images
   389  	metadata = cache.ImageMetadata("trusty", string(oci.ImageTypeBM))
   390  	c.Assert(metadata, gc.HasLen, 1)
   391  	c.Assert(metadata[0].VirtType, gc.Equals, string(oci.ImageTypeBM))
   392  }
   393  
   394  func (i *imagesSuite) TestImageMetadataSpecificImageType(c *gc.C) {
   395  	image := ociCore.Image{
   396  		CompartmentId:          &i.testCompartment,
   397  		Id:                     &i.testImageID,
   398  		OperatingSystem:        makeStringPointer("Canonical Ubuntu"),
   399  		OperatingSystemVersion: makeStringPointer("14.04"),
   400  		DisplayName:            makeStringPointer("Canonical-Ubuntu-14.04-Gen2-GPU-2018.01.11-0"),
   401  	}
   402  
   403  	imgType, err := oci.NewInstanceImage(image, &i.testCompartment)
   404  	c.Assert(err, gc.IsNil)
   405  
   406  	cache := &oci.ImageCache{}
   407  	images := map[string][]oci.InstanceImage{
   408  		"trusty": {
   409  			imgType,
   410  		},
   411  	}
   412  	cache.SetImages(images)
   413  	metadata := cache.ImageMetadata("trusty", "")
   414  	c.Assert(metadata, gc.HasLen, 1)
   415  	// generic images default to ImageTypeVM
   416  	c.Assert(metadata[0].VirtType, gc.Equals, string(oci.ImageTypeGPU))
   417  
   418  	// explicitly set ImageTypeBM on generic images
   419  	metadata = cache.ImageMetadata("trusty", string(oci.ImageTypeBM))
   420  	c.Assert(metadata, gc.HasLen, 1)
   421  	c.Assert(metadata[0].VirtType, gc.Equals, string(oci.ImageTypeGPU))
   422  }