github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/vsphere/vm_template_test.go (about)

     1  // Copyright 2021 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package vsphere_test
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	"github.com/juju/clock/testclock"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/vmware/govmomi/object"
    15  	"github.com/vmware/govmomi/vim25/mo"
    16  	"github.com/vmware/govmomi/vim25/types"
    17  	gc "gopkg.in/check.v1"
    18  
    19  	"github.com/juju/juju/environs"
    20  	"github.com/juju/juju/environs/imagemetadata"
    21  	"github.com/juju/juju/provider/vsphere"
    22  	"github.com/juju/juju/provider/vsphere/internal/ovatest"
    23  	"github.com/juju/juju/provider/vsphere/internal/vsphereclient"
    24  	coretesting "github.com/juju/juju/testing"
    25  )
    26  
    27  type vmTemplateSuite struct {
    28  	EnvironFixture
    29  	statusCallbackStub testing.Stub
    30  
    31  	statusUpdateParams vsphereclient.StatusUpdateParams
    32  	datastore          *object.Datastore
    33  	mockTemplate       *object.VirtualMachine
    34  }
    35  
    36  var _ = gc.Suite(&vmTemplateSuite{})
    37  
    38  func (v *vmTemplateSuite) SetUpTest(c *gc.C) {
    39  	v.EnvironFixture.SetUpTest(c)
    40  	v.statusCallbackStub.ResetCalls()
    41  	v.statusUpdateParams = vsphereclient.StatusUpdateParams{
    42  		UpdateProgressInterval: time.Second,
    43  		UpdateProgress:         func(status string) {},
    44  		Clock:                  testclock.NewClock(time.Time{}),
    45  	}
    46  	v.client.folders = makeFolders("/DC/host")
    47  	v.client.computeResources = []vsphereclient.ComputeResource{
    48  		{Resource: newComputeResource("z1"), Path: "/DC/host/z1"},
    49  		{Resource: newComputeResource("z2"), Path: "/DC/host/z2"},
    50  	}
    51  	v.client.resourcePools = map[string][]*object.ResourcePool{
    52  		"/DC/host/z1/...": {makeResourcePool("pool-1", "/DC/host/z1/Resources")},
    53  		"/DC/host/z2/...": {makeResourcePool("pool-2", "/DC/host/z2/Resources")},
    54  	}
    55  
    56  	v.client.createdVirtualMachine = buildVM("new-vm").vm()
    57  	v.client.datastores = []mo.Datastore{{
    58  		ManagedEntity: mo.ManagedEntity{Name: "foo"},
    59  	}, {
    60  		ManagedEntity: mo.ManagedEntity{Name: "bar"},
    61  		Summary: types.DatastoreSummary{
    62  			Accessible: true,
    63  		},
    64  	}, {
    65  		ManagedEntity: mo.ManagedEntity{Name: "baz"},
    66  		Summary: types.DatastoreSummary{
    67  			Accessible: true,
    68  		},
    69  	}}
    70  	v.datastore = object.NewDatastore(nil, types.ManagedObjectReference{
    71  		Type:  "Datastore",
    72  		Value: "bar",
    73  	})
    74  
    75  	v.mockTemplate = object.NewVirtualMachine(nil, types.ManagedObjectReference{
    76  		Type:  "VirtualMachine",
    77  		Value: "juju-template-" + ovatest.FakeOVASHA256(),
    78  	})
    79  }
    80  
    81  func (v *vmTemplateSuite) addMockLocalTemplateToClient() {
    82  	args := vsphereclient.ImportOVAParameters{
    83  		OVASHA256:    ovatest.FakeOVASHA256(),
    84  		Series:       "jammy",
    85  		Arch:         "amd64",
    86  		TemplateName: "juju-template-" + ovatest.FakeOVASHA256(),
    87  		DestinationFolder: object.NewFolder(nil, types.ManagedObjectReference{
    88  			Type:  "Folder",
    89  			Value: "custom-templates/jammy",
    90  		}),
    91  	}
    92  	v.client.virtualMachineTemplates = []mockTemplateVM{
    93  		{
    94  			vm: object.NewVirtualMachine(nil, types.ManagedObjectReference{
    95  				Type:  "VirtualMachine",
    96  				Value: args.TemplateName,
    97  			}),
    98  			args: args,
    99  		},
   100  	}
   101  }
   102  
   103  func (v *vmTemplateSuite) addMockDownloadedTemplateToClient() {
   104  	v.mockDownloadedTemplateToClient("amd64")
   105  }
   106  
   107  func (v *vmTemplateSuite) addMockDownloadedTemplateToClientNoArch() {
   108  	v.mockDownloadedTemplateToClient("")
   109  }
   110  
   111  func (v *vmTemplateSuite) mockDownloadedTemplateToClient(arch string) {
   112  	args := vsphereclient.ImportOVAParameters{
   113  		OVASHA256:    ovatest.FakeOVASHA256(),
   114  		Series:       "jammy",
   115  		Arch:         arch,
   116  		TemplateName: "juju-template-" + ovatest.FakeOVASHA256(),
   117  		DestinationFolder: object.NewFolder(nil, types.ManagedObjectReference{
   118  			Type: "Folder",
   119  			// The mocked client does a strings.HasPrefix() on this path when listing templates.
   120  			// We do a greedy search when looking for already imported templates.
   121  			Value: "Juju Controller (deadbeef-1bad-500d-9000-4b1d0d06f00d)/templates/jammy/*",
   122  		}),
   123  	}
   124  	v.client.virtualMachineTemplates = []mockTemplateVM{
   125  		{
   126  			vm: object.NewVirtualMachine(nil, types.ManagedObjectReference{
   127  				Type:  "VirtualMachine",
   128  				Value: args.TemplateName,
   129  			}),
   130  			args: args,
   131  		},
   132  	}
   133  }
   134  
   135  func (v *vmTemplateSuite) TestEnsureTemplateNoImageMetadataSuppliedButImageExistsUpstream(c *gc.C) {
   136  	resPool := v.client.resourcePools["/DC/host/z1/..."][0]
   137  	tplMgr := vsphere.NewVMTemplateManager(
   138  		nil, v.env, v.client, resPool.Reference(),
   139  		v.datastore, v.statusUpdateParams, "",
   140  		coretesting.FakeControllerConfig().ControllerUUID(),
   141  	)
   142  
   143  	tpl, arch, err := tplMgr.EnsureTemplate(context.Background(), "jammy", "amd64")
   144  	c.Assert(err, jc.ErrorIsNil)
   145  	c.Assert(tpl, gc.NotNil)
   146  	c.Assert(arch, gc.Equals, "amd64")
   147  	v.client.CheckCallNames(c, "ListVMTemplates", "EnsureVMFolder", "CreateTemplateVM")
   148  }
   149  
   150  func (v *vmTemplateSuite) TestEnsureTemplateNoImageMetadataSuppliedAndImageDoesNotExistUpstream(c *gc.C) {
   151  	resPool := v.client.resourcePools["/DC/host/z1/..."][0]
   152  	tplMgr := vsphere.NewVMTemplateManager(
   153  		nil, v.env, v.client, resPool.Reference(),
   154  		v.datastore, v.statusUpdateParams, "",
   155  		coretesting.FakeControllerConfig().ControllerUUID(),
   156  	)
   157  
   158  	_, _, err := tplMgr.EnsureTemplate(context.Background(), "xenial", "amd64")
   159  	c.Assert(errors.Is(err, environs.ErrAvailabilityZoneIndependent), jc.IsTrue)
   160  	c.Assert(err.Error(), gc.Matches, "no matching images found for given constraints.*")
   161  	v.client.CheckCallNames(c, "ListVMTemplates", "EnsureVMFolder")
   162  }
   163  
   164  func (v *vmTemplateSuite) TestEnsureTemplateWithImageMetadataSupplied(c *gc.C) {
   165  	imgMeta := []*imagemetadata.ImageMetadata{
   166  		{
   167  			Id:         "custom-templates/jammy",
   168  			RegionName: "/datacenter1",
   169  			Endpoint:   "host1",
   170  			Arch:       "amd64",
   171  		},
   172  	}
   173  	v.addMockLocalTemplateToClient()
   174  	resPool := v.client.resourcePools["/DC/host/z1/..."][0]
   175  	tplMgr := vsphere.NewVMTemplateManager(
   176  		imgMeta, v.env, v.client, resPool.Reference(),
   177  		v.datastore, v.statusUpdateParams, "",
   178  		coretesting.FakeControllerConfig().ControllerUUID(),
   179  	)
   180  	tpl, arch, err := tplMgr.EnsureTemplate(context.Background(), "jammy", "amd64")
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	c.Assert(tpl, jc.DeepEquals, v.client.virtualMachineTemplates[0].vm)
   183  	c.Assert(arch, gc.Equals, "amd64")
   184  	v.client.CheckCallNames(c, "ListVMTemplates")
   185  }
   186  
   187  func (v *vmTemplateSuite) TestEnsureTemplateImageNotFoundLocally(c *gc.C) {
   188  	imgMeta := []*imagemetadata.ImageMetadata{
   189  		{
   190  			// this image ID does not exist in our mocked templates.
   191  			Id:         "custom-templates/xenial",
   192  			RegionName: "/datacenter1",
   193  			Endpoint:   "host1",
   194  			Arch:       "amd64",
   195  		},
   196  	}
   197  
   198  	v.addMockLocalTemplateToClient()
   199  	resPool := v.client.resourcePools["/DC/host/z1/..."][0]
   200  	tplMgr := vsphere.NewVMTemplateManager(
   201  		imgMeta, v.env, v.client, resPool.Reference(),
   202  		v.datastore, v.statusUpdateParams, "",
   203  		coretesting.FakeControllerConfig().ControllerUUID(),
   204  	)
   205  	// jammy exists in the image-download simplestreams
   206  	tpl, arch, err := tplMgr.EnsureTemplate(context.Background(), "jammy", "amd64")
   207  	c.Assert(err, jc.ErrorIsNil)
   208  	c.Assert(tpl, gc.NotNil)
   209  	c.Assert(arch, gc.Equals, "amd64")
   210  	// List of calls should be identical to when no custom simplestreams are supplied.
   211  	v.client.CheckCallNames(c, "ListVMTemplates", "ListVMTemplates", "EnsureVMFolder", "CreateTemplateVM")
   212  }
   213  
   214  func (v *vmTemplateSuite) TestEnsureTemplateImageCachedImage(c *gc.C) {
   215  	v.addMockDownloadedTemplateToClient()
   216  	resPool := v.client.resourcePools["/DC/host/z1/..."][0]
   217  	tplMgr := vsphere.NewVMTemplateManager(
   218  		nil, v.env, v.client, resPool.Reference(),
   219  		v.datastore, v.statusUpdateParams, "",
   220  		coretesting.FakeControllerConfig().ControllerUUID(),
   221  	)
   222  
   223  	tpl, arch, err := tplMgr.EnsureTemplate(context.Background(), "jammy", "amd64")
   224  	c.Assert(err, jc.ErrorIsNil)
   225  	c.Assert(tpl, gc.NotNil)
   226  	c.Assert(arch, gc.Equals, "amd64")
   227  	v.client.CheckCallNames(c, "ListVMTemplates", "VirtualMachineObjectToManagedObject")
   228  }
   229  
   230  func (v *vmTemplateSuite) TestEnsureTemplateImageCachedImageNoArch(c *gc.C) {
   231  	v.addMockDownloadedTemplateToClientNoArch()
   232  	resPool := v.client.resourcePools["/DC/host/z1/..."][0]
   233  	tplMgr := vsphere.NewVMTemplateManager(
   234  		nil, v.env, v.client, resPool.Reference(),
   235  		v.datastore, v.statusUpdateParams, "",
   236  		coretesting.FakeControllerConfig().ControllerUUID(),
   237  	)
   238  
   239  	tpl, arch, err := tplMgr.EnsureTemplate(context.Background(), "jammy", "amd64")
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	c.Assert(tpl, gc.NotNil)
   242  	c.Assert(arch, gc.Equals, "")
   243  	v.client.CheckCallNames(c, "ListVMTemplates", "VirtualMachineObjectToManagedObject")
   244  }