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  }