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 }