github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/vsphere/environ_availzones_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package vsphere_test 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 "github.com/vmware/govmomi/object" 9 "github.com/vmware/govmomi/vim25/mo" 10 gc "gopkg.in/check.v1" 11 12 "github.com/juju/juju/core/instance" 13 "github.com/juju/juju/environs" 14 "github.com/juju/juju/environs/context" 15 "github.com/juju/juju/provider/common" 16 "github.com/juju/juju/provider/vsphere/internal/vsphereclient" 17 ) 18 19 type environAvailzonesSuite struct { 20 EnvironFixture 21 } 22 23 var _ = gc.Suite(&environAvailzonesSuite{}) 24 25 func makeFolders(host string) *object.DatacenterFolders { 26 return &object.DatacenterFolders{ 27 HostFolder: &object.Folder{ 28 Common: object.Common{ 29 InventoryPath: host, 30 }, 31 }, 32 } 33 } 34 35 func (s *environAvailzonesSuite) TestAvailabilityZones(c *gc.C) { 36 emptyResource := newComputeResource("empty") 37 emptyResource.Summary.(*mockSummary).EffectiveCpu = 0 38 s.client.folders = makeFolders("/DC/host") 39 s.client.computeResources = []vsphereclient.ComputeResource{ 40 {Resource: emptyResource, Path: "/DC/host/empty"}, 41 {Resource: newComputeResource("z1"), Path: "/DC/host/z1"}, 42 {Resource: newComputeResource("z2"), Path: "/DC/host/z2"}, 43 } 44 s.client.resourcePools = map[string][]*object.ResourcePool{ 45 "/DC/host/z1/...": { 46 makeResourcePool("pool-1", "/DC/host/z1/Resources"), 47 }, 48 "/DC/host/z2/...": { 49 // Check we don't get broken by trailing slashes. 50 makeResourcePool("pool-2", "/DC/host/z2/Resources/"), 51 makeResourcePool("pool-3", "/DC/host/z2/Resources/child"), 52 makeResourcePool("pool-4", "/DC/host/z2/Resources/child/nested"), 53 makeResourcePool("pool-5", "/DC/host/z2/Resources/child/nested/other/"), 54 makeResourcePool("pool-6", "/DC/host/z2/Other/thing"), 55 }, 56 } 57 58 c.Assert(s.env, gc.Implements, new(common.ZonedEnviron)) 59 zonedEnviron := s.env.(common.ZonedEnviron) 60 zones, err := zonedEnviron.AvailabilityZones(s.callCtx) 61 c.Assert(err, jc.ErrorIsNil) 62 c.Assert(len(zones), gc.Equals, 6) 63 // No zones for the empty resource. 64 c.Assert(zones[0].Name(), gc.Equals, "z1") 65 c.Assert(zones[1].Name(), gc.Equals, "z2") 66 c.Assert(zones[2].Name(), gc.Equals, "z2/child") 67 c.Assert(zones[3].Name(), gc.Equals, "z2/child/nested") 68 c.Assert(zones[4].Name(), gc.Equals, "z2/child/nested/other") 69 c.Assert(zones[5].Name(), gc.Equals, "z2/Other/thing") 70 } 71 72 func (s *environAvailzonesSuite) TestAvailabilityZonesInFolder(c *gc.C) { 73 s.client.folders = makeFolders("/DC/host") 74 s.client.computeResources = []vsphereclient.ComputeResource{ 75 {Resource: newComputeResource("z1"), Path: "/DC/host/Folder/z1"}, 76 } 77 s.client.resourcePools = map[string][]*object.ResourcePool{ 78 "/DC/host/Folder/z1/...": { 79 makeResourcePool("pool-1", "/DC/host/Folder/z1/Resources"), 80 makeResourcePool("pool-2", "/DC/host/Folder/z1/Resources/ResPool1"), 81 makeResourcePool("pool-3", "/DC/host/Folder/z1/Resources/ResPool2"), 82 }, 83 } 84 85 c.Assert(s.env, gc.Implements, new(common.ZonedEnviron)) 86 zonedEnviron := s.env.(common.ZonedEnviron) 87 zones, err := zonedEnviron.AvailabilityZones(s.callCtx) 88 c.Assert(err, jc.ErrorIsNil) 89 c.Assert(len(zones), gc.Equals, 3) 90 c.Assert(zones[0].Name(), gc.Equals, "Folder/z1") 91 c.Assert(zones[1].Name(), gc.Equals, "Folder/z1/ResPool1") 92 c.Assert(zones[2].Name(), gc.Equals, "Folder/z1/ResPool2") 93 } 94 95 func (s *environAvailzonesSuite) TestInstanceAvailabilityZoneNames(c *gc.C) { 96 z1 := newComputeResource("z1") 97 z2 := newComputeResource("z2") 98 z3 := newComputeResource("z3") 99 s.client.folders = makeFolders("/DC/host") 100 s.client.computeResources = []vsphereclient.ComputeResource{ 101 {Resource: z1, Path: "/DC/host/z1"}, 102 {Resource: z2, Path: "/DC/host/z2"}, 103 {Resource: z3, Path: "/DC/host/z3"}, 104 } 105 106 childPool := makeResourcePool("rp-child", "/DC/host/z3/Resources/child") 107 childRef := childPool.Reference() 108 s.client.resourcePools = map[string][]*object.ResourcePool{ 109 "/DC/host/z1/...": {makeResourcePool("rp-z1", "/DC/host/z1/Resources")}, 110 "/DC/host/z2/...": {makeResourcePool("rp-z2", "/DC/host/z2/Resources")}, 111 "/DC/host/z3/...": { 112 makeResourcePool("rp-z3", "/DC/host/z3/Resources"), 113 childPool, 114 }, 115 } 116 117 s.client.virtualMachines = []*mo.VirtualMachine{ 118 buildVM("inst-0").resourcePool(z2.ResourcePool).vm(), 119 buildVM("inst-1").resourcePool(z1.ResourcePool).vm(), 120 buildVM("inst-2").vm(), 121 buildVM("inst-3").resourcePool(&childRef).vm(), 122 } 123 ids := []instance.Id{"inst-0", "inst-1", "inst-2", "inst-3", "inst-4"} 124 125 zonedEnviron := s.env.(common.ZonedEnviron) 126 zones, err := zonedEnviron.InstanceAvailabilityZoneNames(s.callCtx, ids) 127 c.Assert(err, gc.Equals, environs.ErrPartialInstances) 128 c.Assert(zones, jc.DeepEquals, map[instance.Id]string{ 129 "inst-0": "z2", 130 "inst-1": "z1", 131 "inst-3": "z3/child", 132 }) 133 } 134 135 func (s *environAvailzonesSuite) TestInstanceAvailabilityZoneNamesNoInstances(c *gc.C) { 136 s.client.folders = makeFolders("/DC/host") 137 zonedEnviron := s.env.(common.ZonedEnviron) 138 _, err := zonedEnviron.InstanceAvailabilityZoneNames(s.callCtx, []instance.Id{"inst-0"}) 139 c.Assert(err, gc.Equals, environs.ErrNoInstances) 140 } 141 142 func (s *environAvailzonesSuite) TestDeriveAvailabilityZones(c *gc.C) { 143 s.client.folders = makeFolders("/DC/host") 144 s.client.computeResources = []vsphereclient.ComputeResource{ 145 {Resource: newComputeResource("test-available"), Path: "/DC/host/test-available"}, 146 } 147 s.client.resourcePools = map[string][]*object.ResourcePool{ 148 "/DC/host/test-available/...": {makeResourcePool("pool-23", "/DC/host/test-available/Resources")}, 149 } 150 151 c.Assert(s.env, gc.Implements, new(common.ZonedEnviron)) 152 zonedEnviron := s.env.(common.ZonedEnviron) 153 154 zones, err := zonedEnviron.DeriveAvailabilityZones( 155 s.callCtx, 156 environs.StartInstanceParams{Placement: "zone=test-available"}) 157 c.Assert(err, jc.ErrorIsNil) 158 c.Assert(zones, gc.DeepEquals, []string{"test-available"}) 159 } 160 161 func (s *environAvailzonesSuite) TestDeriveAvailabilityZonesUnknown(c *gc.C) { 162 s.client.folders = makeFolders("/DC/host") 163 c.Assert(s.env, gc.Implements, new(common.ZonedEnviron)) 164 zonedEnviron := s.env.(common.ZonedEnviron) 165 166 zones, err := zonedEnviron.DeriveAvailabilityZones( 167 s.callCtx, 168 environs.StartInstanceParams{Placement: "zone=test-unknown"}) 169 c.Assert(err, gc.ErrorMatches, `availability zone "test-unknown" not found`) 170 c.Assert(zones, gc.HasLen, 0) 171 } 172 173 func (s *environAvailzonesSuite) TestDeriveAvailabilityZonesInvalidPlacement(c *gc.C) { 174 s.client.folders = makeFolders("/DC/host") 175 c.Assert(s.env, gc.Implements, new(common.ZonedEnviron)) 176 zonedEnviron := s.env.(common.ZonedEnviron) 177 178 zones, err := zonedEnviron.DeriveAvailabilityZones( 179 s.callCtx, 180 environs.StartInstanceParams{ 181 Placement: "invalid-placement", 182 }) 183 c.Assert(err, gc.ErrorMatches, `unknown placement directive: invalid-placement`) 184 c.Assert(zones, gc.HasLen, 0) 185 } 186 187 func (s *environAvailzonesSuite) TestAvailabilityZonesPermissionError(c *gc.C) { 188 AssertInvalidatesCredential(c, s.client, func(ctx context.ProviderCallContext) error { 189 zonedEnv := s.env.(common.ZonedEnviron) 190 _, err := zonedEnv.AvailabilityZones(ctx) 191 return err 192 }) 193 }