From 133f7cc63a20d190bcfba8b9026d6b4dc88a31f8 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Wed, 8 May 2024 01:59:24 +0530 Subject: [PATCH 1/8] Add support for project --- pkg/cloud/affinity_groups.go | 9 ++++++--- pkg/cloud/client.go | 1 + pkg/cloud/instance.go | 23 +++++++++++++---------- pkg/cloud/isolated_network.go | 10 +++++++--- pkg/cloud/network.go | 5 +++-- pkg/cloud/tags.go | 1 + pkg/cloud/zone.go | 5 +++-- 7 files changed, 34 insertions(+), 20 deletions(-) diff --git a/pkg/cloud/affinity_groups.go b/pkg/cloud/affinity_groups.go index dd4112fc..e25a7f49 100644 --- a/pkg/cloud/affinity_groups.go +++ b/pkg/cloud/affinity_groups.go @@ -17,6 +17,7 @@ limitations under the License. package cloud import ( + "github.com/apache/cloudstack-go/v2/cloudstack" "github.com/pkg/errors" infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" ) @@ -42,7 +43,7 @@ type AffinityGroupIface interface { func (c *client) FetchAffinityGroup(group *AffinityGroup) (reterr error) { if group.ID != "" { - affinityGroup, count, err := c.cs.AffinityGroup.GetAffinityGroupByID(group.ID) + affinityGroup, count, err := c.cs.AffinityGroup.GetAffinityGroupByID(group.ID, cloudstack.WithProject(c.config.ProjectID)) if err != nil { // handle via multierr c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -57,7 +58,7 @@ func (c *client) FetchAffinityGroup(group *AffinityGroup) (reterr error) { } } if group.Name != "" { - affinityGroup, count, err := c.cs.AffinityGroup.GetAffinityGroupByName(group.Name) + affinityGroup, count, err := c.cs.AffinityGroup.GetAffinityGroupByName(group.Name, cloudstack.WithProject(c.config.ProjectID)) if err != nil { // handle via multierr c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -78,6 +79,7 @@ func (c *client) GetOrCreateAffinityGroup(group *AffinityGroup) (retErr error) { if err := c.FetchAffinityGroup(group); err != nil { // Group not found? p := c.cs.AffinityGroup.NewCreateAffinityGroupParams(group.Name, group.Type) p.SetName(group.Name) + setIfNotEmpty(c.config.ProjectID, p.SetProjectid) resp, err := c.cs.AffinityGroup.CreateAffinityGroup(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -92,6 +94,7 @@ func (c *client) DeleteAffinityGroup(group *AffinityGroup) (retErr error) { p := c.cs.AffinityGroup.NewDeleteAffinityGroupParams() setIfNotEmpty(group.ID, p.SetId) setIfNotEmpty(group.Name, p.SetName) + setIfNotEmpty(c.config.ProjectID, p.SetProjectid) _, retErr = c.cs.AffinityGroup.DeleteAffinityGroup(p) c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(retErr) return retErr @@ -101,7 +104,7 @@ type affinityGroups []AffinityGroup func (c *client) getCurrentAffinityGroups(csMachine *infrav1.CloudStackMachine) (affinityGroups, error) { // Start by fetching VM details which includes an array of currently associated affinity groups. - if virtM, count, err := c.cs.VirtualMachine.GetVirtualMachineByID(*csMachine.Spec.InstanceID); err != nil { + if virtM, count, err := c.cs.VirtualMachine.GetVirtualMachineByID(*csMachine.Spec.InstanceID, cloudstack.WithProject(c.config.ProjectID)); err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return nil, err } else if count > 1 { diff --git a/pkg/cloud/client.go b/pkg/cloud/client.go index 3d83bbb5..8dd8b7c5 100644 --- a/pkg/cloud/client.go +++ b/pkg/cloud/client.go @@ -53,6 +53,7 @@ type Config struct { APIKey string `yaml:"api-key"` SecretKey string `yaml:"secret-key"` VerifySSL string `yaml:"verify-ssl"` + ProjectID string `yaml:"project-id"` } type client struct { diff --git a/pkg/cloud/instance.go b/pkg/cloud/instance.go index 10956fc0..2ab9e52b 100644 --- a/pkg/cloud/instance.go +++ b/pkg/cloud/instance.go @@ -58,7 +58,7 @@ func setMachineDataFromVMMetrics(vmResponse *cloudstack.VirtualMachinesMetric, c func (c *client) ResolveVMInstanceDetails(csMachine *infrav1.CloudStackMachine) error { // Attempt to fetch by ID. if csMachine.Spec.InstanceID != nil { - vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByID(*csMachine.Spec.InstanceID) + vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByID(*csMachine.Spec.InstanceID, cloudstack.WithProject(c.config.ProjectID)) if err != nil && !strings.Contains(strings.ToLower(err.Error()), "no match found") { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return err @@ -72,7 +72,7 @@ func (c *client) ResolveVMInstanceDetails(csMachine *infrav1.CloudStackMachine) // Attempt fetch by name. if csMachine.Name != "" { - vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByName(csMachine.Name) // add opts usage + vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByName(csMachine.Name, cloudstack.WithProject(c.config.ProjectID)) if err != nil && !strings.Contains(strings.ToLower(err.Error()), "no match") { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return err @@ -88,7 +88,7 @@ func (c *client) ResolveVMInstanceDetails(csMachine *infrav1.CloudStackMachine) func (c *client) ResolveServiceOffering(csMachine *infrav1.CloudStackMachine, zoneID string) (offering cloudstack.ServiceOffering, retErr error) { if len(csMachine.Spec.Offering.ID) > 0 { - csOffering, count, err := c.cs.ServiceOffering.GetServiceOfferingByID(csMachine.Spec.Offering.ID) + csOffering, count, err := c.cs.ServiceOffering.GetServiceOfferingByID(csMachine.Spec.Offering.ID, cloudstack.WithProject(c.config.ProjectID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return cloudstack.ServiceOffering{}, multierror.Append(retErr, errors.Wrapf( @@ -104,7 +104,7 @@ func (c *client) ResolveServiceOffering(csMachine *infrav1.CloudStackMachine, zo } return *csOffering, nil } - csOffering, count, err := c.cs.ServiceOffering.GetServiceOfferingByName(csMachine.Spec.Offering.Name, cloudstack.WithZone(zoneID)) + csOffering, count, err := c.cs.ServiceOffering.GetServiceOfferingByName(csMachine.Spec.Offering.Name, cloudstack.WithZone(zoneID), cloudstack.WithProject(c.config.ProjectID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return cloudstack.ServiceOffering{}, multierror.Append(retErr, errors.Wrapf( @@ -122,7 +122,7 @@ func (c *client) ResolveTemplate( zoneID string, ) (templateID string, retErr error) { if len(csMachine.Spec.Template.ID) > 0 { - csTemplate, count, err := c.cs.Template.GetTemplateByID(csMachine.Spec.Template.ID, "executable") + csTemplate, count, err := c.cs.Template.GetTemplateByID(csMachine.Spec.Template.ID, "executable", cloudstack.WithProject(c.config.ProjectID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return "", multierror.Append(retErr, errors.Wrapf( @@ -138,7 +138,7 @@ func (c *client) ResolveTemplate( } return csMachine.Spec.Template.ID, nil } - templateID, count, err := c.cs.Template.GetTemplateID(csMachine.Spec.Template.Name, "executable", zoneID) + templateID, count, err := c.cs.Template.GetTemplateID(csMachine.Spec.Template.Name, "executable", zoneID, cloudstack.WithProject(c.config.ProjectID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return "", multierror.Append(retErr, errors.Wrapf( @@ -156,7 +156,7 @@ func (c *client) ResolveTemplate( func (c *client) ResolveDiskOffering(csMachine *infrav1.CloudStackMachine, zoneID string) (diskOfferingID string, retErr error) { diskOfferingID = csMachine.Spec.DiskOffering.ID if len(csMachine.Spec.DiskOffering.Name) > 0 { - diskID, count, err := c.cs.DiskOffering.GetDiskOfferingID(csMachine.Spec.DiskOffering.Name, cloudstack.WithZone(zoneID)) + diskID, count, err := c.cs.DiskOffering.GetDiskOfferingID(csMachine.Spec.DiskOffering.Name, cloudstack.WithZone(zoneID), cloudstack.WithProject(c.config.ProjectID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return "", multierror.Append(retErr, errors.Wrapf( @@ -183,7 +183,7 @@ func (c *client) ResolveDiskOffering(csMachine *infrav1.CloudStackMachine, zoneI } func verifyDiskoffering(csMachine *infrav1.CloudStackMachine, c *client, diskOfferingID string, retErr error) (string, error) { - csDiskOffering, count, err := c.cs.DiskOffering.GetDiskOfferingByID(diskOfferingID) + csDiskOffering, count, err := c.cs.DiskOffering.GetDiskOfferingByID(diskOfferingID, cloudstack.WithProject(c.config.ProjectID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return "", multierror.Append(retErr, errors.Wrapf( @@ -300,6 +300,7 @@ func (c *client) DeployVM( setIfNotEmpty(csMachine.Name, p.SetName) setIfNotEmpty(capiMachine.Name, p.SetDisplayname) setIfNotEmpty(diskOfferingID, p.SetDiskofferingid) + setIfNotEmpty(c.config.ProjectID, p.SetProjectid) setIntIfPositive(csMachine.Spec.DiskOffering.CustomSize, p.SetSize) setIfNotEmpty(csMachine.Spec.SSHKey, p.SetKeypair) @@ -330,7 +331,7 @@ func (c *client) DeployVM( // CloudStack may have created the VM even though it reported an error. We attempt to // retrieve the VM so we can populate the CloudStackMachine for the user to manually // clean up. - vm, findErr := findVirtualMachine(c.cs.VirtualMachine, templateID, fd, csMachine) + vm, findErr := findVirtualMachine(c.cs.VirtualMachine, templateID, fd, csMachine, c.config.ProjectID) if findErr != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(findErr) return fmt.Errorf("%v; find virtual machine: %v", err, findErr) @@ -394,13 +395,14 @@ func findVirtualMachine( client cloudstack.VirtualMachineServiceIface, templateID string, failureDomain *infrav1.CloudStackFailureDomain, - machine *infrav1.CloudStackMachine, + machine *infrav1.CloudStackMachine, projectID string, ) (*cloudstack.VirtualMachine, error) { params := client.NewListVirtualMachinesParams() params.SetTemplateid(templateID) params.SetZoneid(failureDomain.Spec.Zone.ID) params.SetNetworkid(failureDomain.Spec.Zone.Network.ID) params.SetName(machine.Name) + setIfNotEmpty(projectID, params.SetProjectid) response, err := client.ListVirtualMachines(params) if err != nil { @@ -453,6 +455,7 @@ func (c *client) listVMInstanceDatadiskVolumeIDs(instanceID string) ([]string, e p.SetVirtualmachineid(instanceID) // VM root volumes are destroyed automatically, no need to explicitly include p.SetType("DATADISK") + setIfNotEmpty(c.config.ProjectID, p.SetProjectid) listVOLResp, err := c.csAsync.Volume.ListVolumes(p) if err != nil { diff --git a/pkg/cloud/isolated_network.go b/pkg/cloud/isolated_network.go index d1458637..5967740f 100644 --- a/pkg/cloud/isolated_network.go +++ b/pkg/cloud/isolated_network.go @@ -76,6 +76,7 @@ func (c *client) AssociatePublicIPAddress( p := c.cs.Address.NewAssociateIpAddressParams() p.SetIpaddress(isoNet.Spec.ControlPlaneEndpoint.Host) p.SetNetworkid(isoNet.Spec.ID) + setIfNotEmpty(c.config.ProjectID, p.SetProjectid) if _, err := c.cs.Address.AssociateIpAddress(p); err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return errors.Wrapf(err, @@ -102,6 +103,7 @@ func (c *client) CreateIsolatedNetwork(fd *infrav1.CloudStackFailureDomain, isoN // Do isolated network creation. p := c.cs.Network.NewCreateNetworkParams(isoNet.Spec.Name, offeringID, fd.Spec.Zone.ID) p.SetDisplaytext(isoNet.Spec.Name) + setIfNotEmpty(c.config.ProjectID, p.SetProjectid) resp, err := c.cs.Network.CreateNetwork(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -148,6 +150,7 @@ func (c *client) GetPublicIP( p.SetAllocatedonly(false) p.SetZoneid(fd.Spec.Zone.ID) setIfNotEmpty(ip, p.SetIpaddress) + setIfNotEmpty(c.config.ProjectID, p.SetProjectid) publicAddresses, err := c.cs.Address.ListPublicIpAddresses(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -168,7 +171,7 @@ func (c *client) GetPublicIP( // GetIsolatedNetwork gets an isolated network in the relevant Zone. func (c *client) GetIsolatedNetwork(isoNet *infrav1.CloudStackIsolatedNetwork) (retErr error) { - netDetails, count, err := c.cs.Network.GetNetworkByName(isoNet.Spec.Name) + netDetails, count, err := c.cs.Network.GetNetworkByName(isoNet.Spec.Name, cloudstack.WithProject(c.config.ProjectID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Network ID from %s", isoNet.Spec.Name)) @@ -180,7 +183,7 @@ func (c *client) GetIsolatedNetwork(isoNet *infrav1.CloudStackIsolatedNetwork) ( return nil } - netDetails, count, err = c.cs.Network.GetNetworkByID(isoNet.Spec.ID) + netDetails, count, err = c.cs.Network.GetNetworkByID(isoNet.Spec.ID, cloudstack.WithProject(c.config.ProjectID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return multierror.Append(retErr, errors.Wrapf(err, "could not get Network by ID %s", isoNet.Spec.ID)) @@ -199,6 +202,7 @@ func (c *client) ResolveLoadBalancerRuleDetails( ) error { p := c.cs.LoadBalancer.NewListLoadBalancerRulesParams() p.SetPublicipid(isoNet.Status.PublicIPID) + setIfNotEmpty(c.config.ProjectID, p.SetProjectid) loadBalancerRules, err := c.cs.LoadBalancer.ListLoadBalancerRules(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -370,7 +374,7 @@ func (c *client) DeleteNetworkIfNotInUse(csCluster *infrav1.CloudStackCluster, n func (c *client) DisassociatePublicIPAddressIfNotInUse(isoNet *infrav1.CloudStackIsolatedNetwork) (retError error) { if tagsAllowDisposal, err := c.DoClusterTagsAllowDisposal(ResourceTypeIPAddress, isoNet.Status.PublicIPID); err != nil { return err - } else if publicIP, _, err := c.cs.Address.GetPublicIpAddressByID(isoNet.Status.PublicIPID); err != nil { + } else if publicIP, _, err := c.cs.Address.GetPublicIpAddressByID(isoNet.Status.PublicIPID, cloudstack.WithProject(c.config.ProjectID)); err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return err } else if publicIP == nil || publicIP.Issourcenat { // Can't disassociate an address if it's the source NAT address. diff --git a/pkg/cloud/network.go b/pkg/cloud/network.go index 6828c13c..dde4200d 100644 --- a/pkg/cloud/network.go +++ b/pkg/cloud/network.go @@ -17,6 +17,7 @@ limitations under the License. package cloud import ( + "github.com/apache/cloudstack-go/v2/cloudstack" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" @@ -51,7 +52,7 @@ func (c *client) ResolveNetwork(net *infrav1.Network) (retErr error) { // TODO rebuild this to consider cases with networks in many zones. // Use ListNetworks instead. netName := net.Name - netDetails, count, err := c.cs.Network.GetNetworkByName(netName) + netDetails, count, err := c.cs.Network.GetNetworkByName(netName, cloudstack.WithProject(c.config.ProjectID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Network ID from %s", netName)) @@ -65,7 +66,7 @@ func (c *client) ResolveNetwork(net *infrav1.Network) (retErr error) { } // Now get network details. - netDetails, count, err = c.cs.Network.GetNetworkByID(net.ID) + netDetails, count, err = c.cs.Network.GetNetworkByID(net.ID, cloudstack.WithProject(c.config.ProjectID)) if err != nil { return multierror.Append(retErr, errors.Wrapf(err, "could not get Network by ID %s", net.ID)) } else if count != 1 { diff --git a/pkg/cloud/tags.go b/pkg/cloud/tags.go index a679b30b..1056195c 100644 --- a/pkg/cloud/tags.go +++ b/pkg/cloud/tags.go @@ -129,6 +129,7 @@ func (c *client) GetTags(resourceType ResourceType, resourceID string) (map[stri p.SetResourceid(resourceID) p.SetResourcetype(string(resourceType)) p.SetListall(true) + setIfNotEmpty(c.config.ProjectID, p.SetProjectid) listTagResponse, err := c.cs.Resourcetags.ListTags(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) diff --git a/pkg/cloud/zone.go b/pkg/cloud/zone.go index 73288bc5..7e895e4d 100644 --- a/pkg/cloud/zone.go +++ b/pkg/cloud/zone.go @@ -17,6 +17,7 @@ limitations under the License. package cloud import ( + "github.com/apache/cloudstack-go/v2/cloudstack" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" @@ -55,7 +56,7 @@ func (c *client) ResolveZone(zSpec *infrav1.CloudStackZoneSpec) (retErr error) { // ResolveNetworkForZone fetches details on Zone's specified network. func (c *client) ResolveNetworkForZone(zSpec *infrav1.CloudStackZoneSpec) (retErr error) { netName := zSpec.Network.Name - netDetails, count, err := c.cs.Network.GetNetworkByName(netName) + netDetails, count, err := c.cs.Network.GetNetworkByName(netName, cloudstack.WithProject(c.config.ProjectID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Network ID from %v", netName)) @@ -69,7 +70,7 @@ func (c *client) ResolveNetworkForZone(zSpec *infrav1.CloudStackZoneSpec) (retEr } // Now get network details. - netDetails, count, err = c.cs.Network.GetNetworkByID(zSpec.Network.ID) + netDetails, count, err = c.cs.Network.GetNetworkByID(zSpec.Network.ID, cloudstack.WithProject(c.config.ProjectID)) if err != nil { return multierror.Append(retErr, errors.Wrapf(err, "could not get Network by ID %s", zSpec.Network.ID)) } else if count != 1 { From 8129de8a31f8f088da7e0a555b1e98a40c114e77 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Wed, 8 May 2024 02:55:08 +0530 Subject: [PATCH 2/8] Fix tests --- pkg/cloud/affinity_groups_test.go | 18 ++--- pkg/cloud/instance_test.go | 116 ++++++++++++++--------------- pkg/cloud/isolated_network_test.go | 14 ++-- pkg/cloud/network_test.go | 10 +-- pkg/cloud/zone_test.go | 14 ++-- 5 files changed, 86 insertions(+), 86 deletions(-) diff --git a/pkg/cloud/affinity_groups_test.go b/pkg/cloud/affinity_groups_test.go index fa354b9a..179ca7f6 100644 --- a/pkg/cloud/affinity_groups_test.go +++ b/pkg/cloud/affinity_groups_test.go @@ -58,13 +58,13 @@ var _ = Describe("AffinityGroup Unit Tests", func() { Context("Fetch or Create Affinity group", func() { It("fetches an affinity group by Name", func() { dummies.AffinityGroup.ID = "" // Force name fetching. - ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name).Return(&cloudstack.AffinityGroup{}, 1, nil) + ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name, gomock.Any()).Return(&cloudstack.AffinityGroup{}, 1, nil) Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) }) It("fetches an affinity group by ID", func() { - ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID).Return(&cloudstack.AffinityGroup{}, 1, nil) + ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID, gomock.Any()).Return(&cloudstack.AffinityGroup{}, 1, nil) Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) }) @@ -72,7 +72,7 @@ var _ = Describe("AffinityGroup Unit Tests", func() { It("creates an affinity group", func() { // dummies.SetDummyDomainAndAccount() // dummies.SetDummyDomainID() - ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID).Return(nil, -1, fakeError) + ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID, gomock.Any()).Return(nil, -1, fakeError) ags.EXPECT().NewCreateAffinityGroupParams(dummies.AffinityGroup.Name, dummies.AffinityGroup.Type). Return(&cloudstack.CreateAffinityGroupParams{}) ags.EXPECT().CreateAffinityGroup(ParamMatch(And(NameEquals(dummies.AffinityGroup.Name)))). @@ -84,7 +84,7 @@ var _ = Describe("AffinityGroup Unit Tests", func() { It("creates an affinity group if Name provided returns more than one affinity group", func() { dummies.AffinityGroup.ID = "" // Force name fetching. agp := &cloudstack.CreateAffinityGroupParams{} - ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name).Return(&cloudstack.AffinityGroup{}, 2, nil) + ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name, gomock.Any()).Return(&cloudstack.AffinityGroup{}, 2, nil) ags.EXPECT().NewCreateAffinityGroupParams(gomock.Any(), gomock.Any()).Return(agp) ags.EXPECT().CreateAffinityGroup(agp).Return(&cloudstack.CreateAffinityGroupResponse{}, nil) @@ -94,7 +94,7 @@ var _ = Describe("AffinityGroup Unit Tests", func() { It("creates an affinity group if getting affinity group by name fails", func() { dummies.AffinityGroup.ID = "" // Force name fetching. agp := &cloudstack.CreateAffinityGroupParams{} - ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name).Return(nil, -1, fakeError) + ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name, gomock.Any()).Return(nil, -1, fakeError) ags.EXPECT().NewCreateAffinityGroupParams(gomock.Any(), gomock.Any()).Return(agp) ags.EXPECT().CreateAffinityGroup(agp).Return(&cloudstack.CreateAffinityGroupResponse{}, nil) @@ -103,7 +103,7 @@ var _ = Describe("AffinityGroup Unit Tests", func() { It("creates an affinity group if ID provided returns more than one affinity group", func() { agp := &cloudstack.CreateAffinityGroupParams{} - ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID).Return(&cloudstack.AffinityGroup{}, 2, nil) + ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID, gomock.Any()).Return(&cloudstack.AffinityGroup{}, 2, nil) ags.EXPECT().NewCreateAffinityGroupParams(gomock.Any(), gomock.Any()).Return(agp) ags.EXPECT().CreateAffinityGroup(agp).Return(&cloudstack.CreateAffinityGroupResponse{}, nil) @@ -112,7 +112,7 @@ var _ = Describe("AffinityGroup Unit Tests", func() { It("creates an affinity group if getting affinity group by ID fails", func() { agp := &cloudstack.CreateAffinityGroupParams{} - ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID).Return(nil, -1, fakeError) + ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID, gomock.Any()).Return(nil, -1, fakeError) ags.EXPECT().NewCreateAffinityGroupParams(gomock.Any(), gomock.Any()).Return(agp) ags.EXPECT().CreateAffinityGroup(agp).Return(&cloudstack.CreateAffinityGroupResponse{}, nil) @@ -164,7 +164,7 @@ var _ = Describe("AffinityGroup Unit Tests", func() { It("Associate affinity group", func() { uagp := &cloudstack.UpdateVMAffinityGroupParams{} vmp := &cloudstack.StartVirtualMachineParams{} - vms.EXPECT().GetVirtualMachineByID(*dummies.CSMachine1.Spec.InstanceID).Return(&cloudstack.VirtualMachine{}, 1, nil) + vms.EXPECT().GetVirtualMachineByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(&cloudstack.VirtualMachine{}, 1, nil) ags.EXPECT().NewUpdateVMAffinityGroupParams(*dummies.CSMachine1.Spec.InstanceID).Return(uagp) vms.EXPECT().NewStopVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID).Return(&cloudstack.StopVirtualMachineParams{}) vms.EXPECT().StopVirtualMachine(&cloudstack.StopVirtualMachineParams{}).Return(&cloudstack.StopVirtualMachineResponse{State: "Stopping"}, nil) @@ -177,7 +177,7 @@ var _ = Describe("AffinityGroup Unit Tests", func() { It("Disassociate affinity group", func() { uagp := &cloudstack.UpdateVMAffinityGroupParams{} vmp := &cloudstack.StartVirtualMachineParams{} - vms.EXPECT().GetVirtualMachineByID(*dummies.CSMachine1.Spec.InstanceID).Return(&cloudstack.VirtualMachine{}, 1, nil) + vms.EXPECT().GetVirtualMachineByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(&cloudstack.VirtualMachine{}, 1, nil) ags.EXPECT().NewUpdateVMAffinityGroupParams(*dummies.CSMachine1.Spec.InstanceID).Return(uagp) vms.EXPECT().NewStopVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID).Return(&cloudstack.StopVirtualMachineParams{}) vms.EXPECT().StopVirtualMachine(&cloudstack.StopVirtualMachineParams{}).Return(&cloudstack.StopVirtualMachineResponse{State: "Stopping"}, nil) diff --git a/pkg/cloud/instance_test.go b/pkg/cloud/instance_test.go index 77239496..f3ccb706 100644 --- a/pkg/cloud/instance_test.go +++ b/pkg/cloud/instance_test.go @@ -75,42 +75,42 @@ var _ = Describe("Instance", func() { Context("when fetching a VM instance", func() { It("Handles an unknown error when fetching by ID", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, unknownError) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, unknownError) Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).To(MatchError(unknownErrorMessage)) }) It("Handles finding more than one VM instance by ID", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, 2, nil) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, 2, nil) Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)). Should(MatchError("found more than one VM Instance with ID " + *dummies.CSMachine1.Spec.InstanceID)) }) It("sets dummies.CSMachine1 spec and status values when VM instance found by ID", func() { vmsResp := &cloudstack.VirtualMachinesMetric{Id: *dummies.CSMachine1.Spec.InstanceID} - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(vmsResp, 1, nil) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(vmsResp, 1, nil) Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should(Succeed()) Ω(dummies.CSMachine1.Spec.ProviderID).Should(Equal(pointer.String("cloudstack:///" + vmsResp.Id))) Ω(dummies.CSMachine1.Spec.InstanceID).Should(Equal(pointer.String(vmsResp.Id))) }) It("handles an unknown error when fetching by name", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, -1, unknownError) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, -1, unknownError) Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should(MatchError(unknownErrorMessage)) }) It("handles finding more than one VM instance by Name", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, 2, nil) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, 2, nil) Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should( MatchError("found more than one VM Instance with name " + dummies.CSMachine1.Name)) }) It("sets dummies.CSMachine1 spec and status values when VM instance found by Name", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{Id: *dummies.CSMachine1.Spec.InstanceID}, -1, nil) Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should(Succeed()) @@ -124,19 +124,19 @@ var _ = Describe("Instance", func() { vmMetricResp := &cloudstack.VirtualMachinesMetric{} expectVMNotFound := func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, -1, notFoundError) } It("doesn't re-create if one already exists.", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(vmMetricResp, -1, nil) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(vmMetricResp, -1, nil) Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). Should(Succeed()) }) It("returns unknown error while fetching VM instance", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, unknownError) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, unknownError) Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). Should(MatchError(unknownErrorMessage)) @@ -169,7 +169,7 @@ var _ = Describe("Instance", func() { Id: dummies.CSMachine1.Spec.Offering.ID, Name: dummies.CSMachine1.Spec.Offering.Name, }, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return("", -1, unknownError) Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). @@ -184,7 +184,7 @@ var _ = Describe("Instance", func() { Id: dummies.CSMachine1.Spec.Offering.ID, Name: dummies.CSMachine1.Spec.Offering.Name, }, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID).Return("", 2, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()).Return("", 2, nil) Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). ShouldNot(Succeed()) @@ -198,7 +198,7 @@ var _ = Describe("Instance", func() { Id: dummies.CSMachine1.Spec.Offering.ID, Name: dummies.CSMachine1.Spec.Offering.Name, }, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 2, nil) Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). @@ -212,9 +212,9 @@ var _ = Describe("Instance", func() { Id: dummies.CSMachine1.Spec.Offering.ID, Name: dummies.CSMachine1.Spec.Offering.Name, }, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, unknownError) + dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, unknownError) Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). ShouldNot(Succeed()) @@ -228,9 +228,9 @@ var _ = Describe("Instance", func() { Id: dummies.CSMachine1.Spec.Offering.ID, Name: dummies.CSMachine1.Spec.Offering.Name, }, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) + dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). ShouldNot(Succeed()) @@ -246,9 +246,9 @@ var _ = Describe("Instance", func() { Cpunumber: 1, Memory: 1024, }, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID).Return(&cloudstack.DiskOffering{Iscustomized: true}, 1, nil) + dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: true}, 1, nil) Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). ShouldNot(Succeed()) @@ -433,11 +433,11 @@ var _ = Describe("Instance", func() { Cpunumber: 1, Memory: 1024, }, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return(templateFakeID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()). Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID). + dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID, gomock.Any()). Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) vms.EXPECT().NewDeployVirtualMachineParams(offeringFakeID, templateFakeID, dummies.Zone1.ID). Return(&cloudstack.DeployVirtualMachineParams{}) @@ -452,12 +452,12 @@ var _ = Describe("Instance", func() { Context("when using UUIDs and/or names to locate service offerings and templates", func() { BeforeEach(func() { gomock.InOrder( - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(nil, -1, notFoundError), - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{}, 1, nil)) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, -1, notFoundError) }) ActionAndAssert := func() { @@ -503,8 +503,8 @@ var _ = Describe("Instance", func() { Memory: 1024, }, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return(templateFakeID, 1, nil) ActionAndAssert() @@ -522,7 +522,7 @@ var _ = Describe("Instance", func() { Cpunumber: 1, Memory: 1024, }, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return(templateFakeID, 1, nil) ActionAndAssert() @@ -535,15 +535,15 @@ var _ = Describe("Instance", func() { dummies.CSMachine1.Spec.Offering.Name = "" dummies.CSMachine1.Spec.Template.Name = "template" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID).Return(&cloudstack.ServiceOffering{ + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()).Return(&cloudstack.ServiceOffering{ Id: offeringFakeID, Cpunumber: 1, Memory: 1024, }, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return(templateFakeID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) + dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) ActionAndAssert() }) @@ -561,9 +561,9 @@ var _ = Describe("Instance", func() { Memory: 1024, }, 1, nil) - ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter).Return(&cloudstack.Template{Name: ""}, 1, nil) + ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter, gomock.Any()).Return(&cloudstack.Template{Name: ""}, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) + dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) ActionAndAssert() }) @@ -575,7 +575,7 @@ var _ = Describe("Instance", func() { dummies.CSMachine1.Spec.Offering.Name = "" dummies.CSMachine1.Spec.Template.Name = "" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID). + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()). Return(&cloudstack.ServiceOffering{ Id: offeringFakeID, Cpunumber: 1, @@ -583,9 +583,9 @@ var _ = Describe("Instance", func() { }, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()). Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID). + dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()). Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) - ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter). + ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter, gomock.Any()). Return(&cloudstack.Template{Name: "template"}, 1, nil) ActionAndAssert() @@ -598,15 +598,15 @@ var _ = Describe("Instance", func() { dummies.CSMachine1.Spec.Offering.Name = "offering" dummies.CSMachine1.Spec.Template.Name = "template" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID).Return(&cloudstack.ServiceOffering{ + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()).Return(&cloudstack.ServiceOffering{ Id: dummies.CSMachine1.Spec.Offering.ID, Name: dummies.CSMachine1.Spec.Offering.Name, Cpunumber: 1, Memory: 1024, }, 1, nil) - ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter).Return(&cloudstack.Template{Name: "template"}, 1, nil) + ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter, gomock.Any()).Return(&cloudstack.Template{Name: "template"}, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) + dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) ActionAndAssert() }) @@ -614,9 +614,9 @@ var _ = Describe("Instance", func() { Context("when using both UUIDs and names to locate service offerings and templates", func() { BeforeEach(func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, -1, notFoundError) }) It("works with Id and name both provided, offering name mismatch", func() { @@ -625,7 +625,7 @@ var _ = Describe("Instance", func() { dummies.CSMachine1.Spec.Offering.Name = "offering" dummies.CSMachine1.Spec.Template.Name = "template" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID).Return(&cloudstack.ServiceOffering{Name: "offering-not-match"}, 1, nil) + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()).Return(&cloudstack.ServiceOffering{Name: "offering-not-match"}, 1, nil) requiredRegexp := "offering name %s does not match name %s returned using UUID %s" Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). @@ -638,8 +638,8 @@ var _ = Describe("Instance", func() { dummies.CSMachine1.Spec.Offering.Name = "offering" dummies.CSMachine1.Spec.Template.Name = "template" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID).Return(&cloudstack.ServiceOffering{Name: "offering"}, 1, nil) - ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter).Return(&cloudstack.Template{Name: "template-not-match"}, 1, nil) + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()).Return(&cloudstack.ServiceOffering{Name: "offering"}, 1, nil) + ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter, gomock.Any()).Return(&cloudstack.Template{Name: "template-not-match"}, 1, nil) requiredRegexp := "template name %s does not match name %s returned using UUID %s" Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). @@ -654,8 +654,8 @@ var _ = Describe("Instance", func() { dummies.CSMachine1.Spec.Template.Name = "template" dummies.CSMachine1.Spec.DiskOffering.Name = "diskoffering" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID).Return(&cloudstack.ServiceOffering{Name: "offering"}, 1, nil) - ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter).Return(&cloudstack.Template{Name: "template"}, 1, nil) + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()).Return(&cloudstack.ServiceOffering{Name: "offering"}, 1, nil) + ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter, gomock.Any()).Return(&cloudstack.Template{Name: "template"}, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID+"-not-match", 1, nil) requiredRegexp := "diskOffering ID %s does not match ID %s returned using name %s" Ω(client.GetOrCreateVMInstance( @@ -673,13 +673,13 @@ var _ = Describe("Instance", func() { dummies.CSMachine1.Spec.UncompressedUserData = pointer.Bool(true) vms.EXPECT(). - GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(nil, -1, notFoundError) vms.EXPECT(). - GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{}, 1, nil) vms.EXPECT(). - GetVirtualMachinesMetricByName(dummies.CSMachine1.Name). + GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()). Return(nil, -1, notFoundError) sos.EXPECT(). @@ -693,10 +693,10 @@ var _ = Describe("Instance", func() { GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()). Return(diskOfferingFakeID, 1, nil) dos.EXPECT(). - GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID). + GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()). Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) ts.EXPECT(). - GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return(templateFakeID, 1, nil) vms.EXPECT(). NewDeployVirtualMachineParams(offeringFakeID, templateFakeID, dummies.Zone1.ID). @@ -779,8 +779,8 @@ var _ = Describe("Instance", func() { vms.EXPECT().DestroyVirtualMachine(expungeDestroyParams).Return(nil, nil) vs.EXPECT().NewListVolumesParams().Return(listVolumesParams) vs.EXPECT().ListVolumes(listVolumesParams).Return(listVolumesResponse, nil) - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, -1, notFoundError) Ω(client.DestroyVMInstance(dummies.CSMachine1)). Should(Succeed()) }) @@ -793,7 +793,7 @@ var _ = Describe("Instance", func() { vms.EXPECT().DestroyVirtualMachine(expungeDestroyParams).Return(nil, nil) vs.EXPECT().NewListVolumesParams().Return(listVolumesParams) vs.EXPECT().ListVolumes(listVolumesParams).Return(listVolumesResponse, nil) - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{ State: "Expunging", }, 1, nil) @@ -809,7 +809,7 @@ var _ = Describe("Instance", func() { vms.EXPECT().DestroyVirtualMachine(expungeDestroyParams).Return(nil, nil) vs.EXPECT().NewListVolumesParams().Return(listVolumesParams) vs.EXPECT().ListVolumes(listVolumesParams).Return(listVolumesResponse, nil) - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{ State: "Expunged", }, 1, nil) @@ -825,7 +825,7 @@ var _ = Describe("Instance", func() { vms.EXPECT().DestroyVirtualMachine(expungeDestroyParams).Return(nil, nil) vs.EXPECT().NewListVolumesParams().Return(listVolumesParams) vs.EXPECT().ListVolumes(listVolumesParams).Return(listVolumesResponse, nil) - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{ State: "Stopping", }, 1, nil) diff --git a/pkg/cloud/isolated_network_test.go b/pkg/cloud/isolated_network_test.go index 358c0768..3c6154c6 100644 --- a/pkg/cloud/isolated_network_test.go +++ b/pkg/cloud/isolated_network_test.go @@ -74,8 +74,8 @@ var _ = Describe("Network", func() { nos.EXPECT().GetNetworkOfferingID(gomock.Any()).Return("someOfferingID", 1, nil) ns.EXPECT().NewCreateNetworkParams(gomock.Any(), gomock.Any(), gomock.Any()). Return(&csapi.CreateNetworkParams{}) - ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name).Return(nil, 0, nil) - ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID).Return(nil, 0, nil) + ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name, gomock.Any()).Return(nil, 0, nil) + ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID, gomock.Any()).Return(nil, 0, nil) ns.EXPECT().CreateNetwork(gomock.Any()).Return(&csapi.CreateNetworkResponse{Id: dummies.ISONet1.ID}, nil) as.EXPECT().NewListPublicIpAddressesParams().Return(&csapi.ListPublicIpAddressesParams{}) as.EXPECT().ListPublicIpAddresses(gomock.Any()). @@ -125,8 +125,8 @@ var _ = Describe("Network", func() { }) It("fails to get network offering from CloudStack", func() { - ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name).Return(nil, 0, nil) - ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID).Return(nil, 0, nil) + ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name, gomock.Any()).Return(nil, 0, nil) + ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID, gomock.Any()).Return(nil, 0, nil) nos.EXPECT().GetNetworkOfferingID(gomock.Any()).Return("", -1, fakeError) err := client.GetOrCreateIsolatedNetwork(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster) @@ -430,7 +430,7 @@ var _ = Describe("Network", func() { rtlp := &csapi.ListTagsParams{} rs.EXPECT().NewListTagsParams().Return(rtlp).Times(4) rs.EXPECT().ListTags(rtlp).Return(&csapi.ListTagsResponse{}, nil).Times(4) - as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID).Return(&csapi.PublicIpAddress{}, 1, nil) + as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID, gomock.Any()).Return(&csapi.PublicIpAddress{}, 1, nil) Ω(client.DisposeIsoNetResources(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) }) @@ -446,7 +446,7 @@ var _ = Describe("Network", func() { rs.EXPECT().NewListTagsParams().Return(rtlp).Times(4) rs.EXPECT().ListTags(rtlp).Return(createdByCAPCResponse, nil).Times(3) rs.EXPECT().ListTags(rtlp).Return(&csapi.ListTagsResponse{}, nil).Times(1) - as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID).Return(&csapi.PublicIpAddress{}, 1, nil) + as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID, gomock.Any()).Return(&csapi.PublicIpAddress{}, 1, nil) as.EXPECT().NewDisassociateIpAddressParams(dummies.CSISONet1.Status.PublicIPID).Return(dap) as.EXPECT().DisassociateIpAddress(dap).Return(&csapi.DisassociateIpAddressResponse{}, nil) @@ -463,7 +463,7 @@ var _ = Describe("Network", func() { rs.EXPECT().DeleteTags(rtdp).Return(&csapi.DeleteTagsResponse{}, nil).Times(2) rs.EXPECT().NewListTagsParams().Return(rtlp).Times(2) rs.EXPECT().ListTags(rtlp).Return(createdByCAPCResponse, nil).Times(2) - as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID).Return(&csapi.PublicIpAddress{}, 1, nil) + as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID, gomock.Any()).Return(&csapi.PublicIpAddress{}, 1, nil) as.EXPECT().NewDisassociateIpAddressParams(dummies.CSISONet1.Status.PublicIPID).Return(dap) as.EXPECT().DisassociateIpAddress(dap).Return(nil, fakeError) diff --git a/pkg/cloud/network_test.go b/pkg/cloud/network_test.go index 10311b79..4bc844eb 100644 --- a/pkg/cloud/network_test.go +++ b/pkg/cloud/network_test.go @@ -53,21 +53,21 @@ var _ = Describe("Network", func() { Context("for an existing network", func() { It("resolves network by ID", func() { - ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name).Return(nil, 0, nil) - ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 1, nil) + ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name, gomock.Any()).Return(nil, 0, nil) + ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID, gomock.Any()).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 1, nil) Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(Succeed()) }) It("resolves network by Name", func() { - ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 1, nil) + ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name, gomock.Any()).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 1, nil) Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(Succeed()) }) It("When there exists more than one network with the same name", func() { - ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 2, nil) - ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID).Return(nil, 2, errors.New("There is more then one result for Network UUID")) + ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name, gomock.Any()).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 2, nil) + ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID, gomock.Any()).Return(nil, 2, errors.New("There is more then one result for Network UUID")) err := client.ResolveNetwork(&dummies.ISONet1) Ω(err).ShouldNot(Succeed()) Ω(err.Error()).Should(ContainSubstring(fmt.Sprintf("expected 1 Network with name %s, but got %d", dummies.ISONet1.Name, 2))) diff --git a/pkg/cloud/zone_test.go b/pkg/cloud/zone_test.go index 5217f3e4..59355f8b 100644 --- a/pkg/cloud/zone_test.go +++ b/pkg/cloud/zone_test.go @@ -85,14 +85,14 @@ var _ = Describe("Zone", func() { Context("Resolve network for zone", func() { It("get network by name specfied in zone spec", func() { - ns.EXPECT().GetNetworkByName(dummies.Zone1.Network.Name).Return(&csapi.Network{}, 1, nil) + ns.EXPECT().GetNetworkByName(dummies.Zone1.Network.Name, gomock.Any()).Return(&csapi.Network{}, 1, nil) Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain1.Spec.Zone)).Should(Succeed()) }) It("get network by name specfied in zone spec returns > 1 network", func() { - ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name).Return(&csapi.Network{}, 2, nil) - ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID).Return(&csapi.Network{}, 2, nil) + ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name, gomock.Any()).Return(&csapi.Network{}, 2, nil) + ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID, gomock.Any()).Return(&csapi.Network{}, 2, nil) Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain2.Spec.Zone)).Should(MatchError(And( ContainSubstring(fmt.Sprintf("expected 1 Network with name %s, but got %d", dummies.Zone2.Network.Name, 2)), @@ -100,14 +100,14 @@ var _ = Describe("Zone", func() { }) It("get network by id specfied in zone spec", func() { - ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name).Return(nil, -1, fakeError) - ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID).Return(&csapi.Network{}, 1, nil) + ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name, gomock.Any()).Return(nil, -1, fakeError) + ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID, gomock.Any()).Return(&csapi.Network{}, 1, nil) Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain2.Spec.Zone)).Should(Succeed()) }) It("get network by id fails", func() { - ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name).Return(nil, -1, fakeError) - ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID).Return(nil, -1, fakeError) + ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name, gomock.Any()).Return(nil, -1, fakeError) + ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID, gomock.Any()).Return(nil, -1, fakeError) Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain2.Spec.Zone).Error()).Should(ContainSubstring(fmt.Sprintf("could not get Network by ID %s", dummies.Zone2.Network.ID))) }) From 64d390ee2d24c4e3eb8eb867e7dc6c2f10769929 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Thu, 9 May 2024 12:33:39 +0530 Subject: [PATCH 3/8] Update docs --- docs/book/src/getting-started.md | 1 + test/e2e/README.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/book/src/getting-started.md b/docs/book/src/getting-started.md index 5c10878e..ff79528e 100644 --- a/docs/book/src/getting-started.md +++ b/docs/book/src/getting-started.md @@ -35,6 +35,7 @@ secret-key: api-url: verify-ssl: "false" + project-id: # Optional. If provided, the provider will use this project id for all operations ``` - Apply this secret to the management cluster: diff --git a/test/e2e/README.md b/test/e2e/README.md index d0ba67aa..ea5aa85b 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -51,9 +51,10 @@ stringData: secret-key: XXXX api-url: http://1.2.3.4:8080/client/api verify-ssl: "false" + project-id: ``` This will be applied to the kind cluster that hosts CAPI/CAPC for the test, allowing CAPC to access the cluster. -The api-key and secret-key can be found or generated at Home > Accounts > admin > Users > admin of the ACS management UI. `verify-ssl` is an optional flag and its default value is true. CAPC skips verifying the host SSL certificates when the flag is set to false. +The api-key and secret-key can be found or generated at Home > Accounts > admin > Users > admin of the ACS management UI. `verify-ssl` is an optional flag and its default value is true. CAPC skips verifying the host SSL certificates when the flag is set to false. `project-id` is also optional and is used to specify the project id to be used for the k8s cluster. ### Running the e2e tests From 659ff9a249663b6f33d50be5c79326e6586895c9 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Tue, 21 May 2024 14:47:00 +0530 Subject: [PATCH 4/8] Move project to CloudStackCluster Spec --- api/v1beta1/conversion.go | 3 +- api/v1beta3/cloudstackfailuredomain_types.go | 4 ++ ...e.cluster.x-k8s.io_cloudstackclusters.yaml | 3 ++ ...ter.x-k8s.io_cloudstackfailuredomains.yaml | 3 ++ controllers/utils/failuredomains.go | 4 +- pkg/cloud/affinity_groups.go | 10 ++--- pkg/cloud/client.go | 23 +++++----- pkg/cloud/client_test.go | 10 ++--- pkg/cloud/cloud_suite_test.go | 2 +- pkg/cloud/instance.go | 22 +++++----- pkg/cloud/isolated_network.go | 14 +++--- pkg/cloud/network.go | 4 +- pkg/cloud/tags.go | 2 +- pkg/cloud/user_credentials.go | 43 +++++++++++++++++++ pkg/cloud/user_credentials_test.go | 2 +- pkg/cloud/zone.go | 4 +- 16 files changed, 105 insertions(+), 48 deletions(-) diff --git a/api/v1beta1/conversion.go b/api/v1beta1/conversion.go index 6e0aac7f..236685f6 100644 --- a/api/v1beta1/conversion.go +++ b/api/v1beta1/conversion.go @@ -19,6 +19,7 @@ package v1beta1 import ( "context" "fmt" + corev1 "k8s.io/api/core/v1" conv "k8s.io/apimachinery/pkg/conversion" "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" @@ -156,7 +157,7 @@ func fetchZoneIDUsingK8s(namespace string, zoneName string) (string, error) { } func fetchZoneIDUsingCloudStack(secret *corev1.Secret, zoneName string) (string, error) { - client, err := cloud.NewClientFromK8sSecret(secret, nil) + client, err := cloud.NewClientFromK8sSecret(secret, nil, "") if err != nil { return "", err } diff --git a/api/v1beta3/cloudstackfailuredomain_types.go b/api/v1beta3/cloudstackfailuredomain_types.go index a0040fe8..d3af6fe7 100644 --- a/api/v1beta3/cloudstackfailuredomain_types.go +++ b/api/v1beta3/cloudstackfailuredomain_types.go @@ -85,6 +85,10 @@ type CloudStackFailureDomainSpec struct { // +optional Domain string `json:"domain,omitempty"` + // CloudStack project. + // +optional + Project string `json:"project,omitempty"` + // Apache CloudStack Endpoint secret reference. ACSEndpoint corev1.SecretReference `json:"acsEndpoint"` } diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackclusters.yaml index eb5eaf97..882a42eb 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackclusters.yaml @@ -376,6 +376,9 @@ spec: name: description: The failure domain unique name. type: string + project: + description: CloudStack project. + type: string zone: description: The ACS Zone for this failure domain. properties: diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackfailuredomains.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackfailuredomains.yaml index 57490cfc..4dcb3292 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackfailuredomains.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackfailuredomains.yaml @@ -154,6 +154,9 @@ spec: name: description: The failure domain unique name. type: string + project: + description: CloudStack project. + type: string zone: description: The ACS Zone for this failure domain. properties: diff --git a/controllers/utils/failuredomains.go b/controllers/utils/failuredomains.go index 542bb407..bf66c259 100644 --- a/controllers/utils/failuredomains.go +++ b/controllers/utils/failuredomains.go @@ -147,12 +147,12 @@ func (c *CloudClientImplementation) AsFailureDomainUser(fdSpec *infrav1.CloudSta _ = c.K8sClient.Get(c.RequestCtx, key, clientConfig) var err error - if c.CSClient, err = cloud.NewClientFromK8sSecret(endpointCredentials, clientConfig); err != nil { + if c.CSClient, err = cloud.NewClientFromK8sSecret(endpointCredentials, clientConfig, fdSpec.Project); err != nil { return ctrl.Result{}, errors.Wrapf(err, "parsing ACSEndpoint secret with ref: %v", fdSpec.ACSEndpoint) } if fdSpec.Account != "" { // Set r.CSUser CloudStack Client per Account and Domain. - client, err := c.CSClient.NewClientInDomainAndAccount(fdSpec.Domain, fdSpec.Account) + client, err := c.CSClient.NewClientInDomainAndAccount(fdSpec.Domain, fdSpec.Account, fdSpec.Project) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/cloud/affinity_groups.go b/pkg/cloud/affinity_groups.go index e25a7f49..8668d8bf 100644 --- a/pkg/cloud/affinity_groups.go +++ b/pkg/cloud/affinity_groups.go @@ -43,7 +43,7 @@ type AffinityGroupIface interface { func (c *client) FetchAffinityGroup(group *AffinityGroup) (reterr error) { if group.ID != "" { - affinityGroup, count, err := c.cs.AffinityGroup.GetAffinityGroupByID(group.ID, cloudstack.WithProject(c.config.ProjectID)) + affinityGroup, count, err := c.cs.AffinityGroup.GetAffinityGroupByID(group.ID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { // handle via multierr c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -58,7 +58,7 @@ func (c *client) FetchAffinityGroup(group *AffinityGroup) (reterr error) { } } if group.Name != "" { - affinityGroup, count, err := c.cs.AffinityGroup.GetAffinityGroupByName(group.Name, cloudstack.WithProject(c.config.ProjectID)) + affinityGroup, count, err := c.cs.AffinityGroup.GetAffinityGroupByName(group.Name, cloudstack.WithProject(c.user.Project.ID)) if err != nil { // handle via multierr c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -79,7 +79,7 @@ func (c *client) GetOrCreateAffinityGroup(group *AffinityGroup) (retErr error) { if err := c.FetchAffinityGroup(group); err != nil { // Group not found? p := c.cs.AffinityGroup.NewCreateAffinityGroupParams(group.Name, group.Type) p.SetName(group.Name) - setIfNotEmpty(c.config.ProjectID, p.SetProjectid) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) resp, err := c.cs.AffinityGroup.CreateAffinityGroup(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -94,7 +94,7 @@ func (c *client) DeleteAffinityGroup(group *AffinityGroup) (retErr error) { p := c.cs.AffinityGroup.NewDeleteAffinityGroupParams() setIfNotEmpty(group.ID, p.SetId) setIfNotEmpty(group.Name, p.SetName) - setIfNotEmpty(c.config.ProjectID, p.SetProjectid) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) _, retErr = c.cs.AffinityGroup.DeleteAffinityGroup(p) c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(retErr) return retErr @@ -104,7 +104,7 @@ type affinityGroups []AffinityGroup func (c *client) getCurrentAffinityGroups(csMachine *infrav1.CloudStackMachine) (affinityGroups, error) { // Start by fetching VM details which includes an array of currently associated affinity groups. - if virtM, count, err := c.cs.VirtualMachine.GetVirtualMachineByID(*csMachine.Spec.InstanceID, cloudstack.WithProject(c.config.ProjectID)); err != nil { + if virtM, count, err := c.cs.VirtualMachine.GetVirtualMachineByID(*csMachine.Spec.InstanceID, cloudstack.WithProject(c.user.Project.ID)); err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return nil, err } else if count > 1 { diff --git a/pkg/cloud/client.go b/pkg/cloud/client.go index 8dd8b7c5..0748308a 100644 --- a/pkg/cloud/client.go +++ b/pkg/cloud/client.go @@ -44,7 +44,7 @@ type Client interface { ZoneIFace IsoNetworkIface UserCredIFace - NewClientInDomainAndAccount(string, string) (Client, error) + NewClientInDomainAndAccount(string, string, string) (Client, error) } // cloud-config ini structure. @@ -53,7 +53,6 @@ type Config struct { APIKey string `yaml:"api-key"` SecretKey string `yaml:"secret-key"` VerifySSL string `yaml:"verify-ssl"` - ProjectID string `yaml:"project-id"` } type client struct { @@ -102,7 +101,7 @@ func UnmarshalAllSecretConfigs(in []byte, out *[]SecretConfig) error { } // NewClientFromK8sSecret returns a client from a k8s secret -func NewClientFromK8sSecret(endpointSecret *corev1.Secret, clientConfig *corev1.ConfigMap) (Client, error) { +func NewClientFromK8sSecret(endpointSecret *corev1.Secret, clientConfig *corev1.ConfigMap, project string) (Client, error) { endpointSecretStrings := map[string]string{} for k, v := range endpointSecret.Data { endpointSecretStrings[k] = string(v) @@ -111,11 +110,11 @@ func NewClientFromK8sSecret(endpointSecret *corev1.Secret, clientConfig *corev1. if err != nil { return nil, err } - return NewClientFromBytesConfig(bytes, clientConfig) + return NewClientFromBytesConfig(bytes, clientConfig, project) } // NewClientFromBytesConfig returns a client from a bytes array that unmarshals to a yaml config. -func NewClientFromBytesConfig(conf []byte, clientConfig *corev1.ConfigMap) (Client, error) { +func NewClientFromBytesConfig(conf []byte, clientConfig *corev1.ConfigMap, project string) (Client, error) { r := bytes.NewReader(conf) dec := yaml.NewDecoder(r) var config Config @@ -123,7 +122,7 @@ func NewClientFromBytesConfig(conf []byte, clientConfig *corev1.ConfigMap) (Clie return nil, err } - return NewClientFromConf(config, clientConfig) + return NewClientFromConf(config, clientConfig, project) } // NewClientFromYamlPath returns a client from a yaml config at path. @@ -147,11 +146,11 @@ func NewClientFromYamlPath(confPath string, secretName string) (Client, error) { return nil, errors.Errorf("config with secret name %s not found", secretName) } - return NewClientFromConf(conf, nil) + return NewClientFromConf(conf, nil, "") } // NewClientFromConf creates a new Cloud Client form a map of strings to strings. -func NewClientFromConf(conf Config, clientConfig *corev1.ConfigMap) (Client, error) { +func NewClientFromConf(conf Config, clientConfig *corev1.ConfigMap, project string) (Client, error) { cacheMutex.Lock() defer cacheMutex.Unlock() @@ -190,6 +189,9 @@ func NewClientFromConf(conf Config, clientConfig *corev1.ConfigMap) (Client, err ID: userResponse.Users[0].Domainid, }, }, + Project: Project{ + Name: project, + }, } if found, err := c.GetUserWithKeys(user); err != nil { return nil, err @@ -204,10 +206,11 @@ func NewClientFromConf(conf Config, clientConfig *corev1.ConfigMap) (Client, err } // NewClientInDomainAndAccount returns a new client in the specified domain and account. -func (c *client) NewClientInDomainAndAccount(domain string, account string) (Client, error) { +func (c *client) NewClientInDomainAndAccount(domain string, account string, project string) (Client, error) { user := &User{} user.Account.Domain.Path = domain user.Account.Name = account + user.Project.Name = project if found, err := c.GetUserWithKeys(user); err != nil { return nil, err } else if !found { @@ -218,7 +221,7 @@ func (c *client) NewClientInDomainAndAccount(domain string, account string) (Cli c.config.SecretKey = user.SecretKey c.user = user - return NewClientFromConf(c.config, nil) + return NewClientFromConf(c.config, nil, project) } // NewClientFromCSAPIClient creates a client from a CloudStack-Go API client. Used only for testing. diff --git a/pkg/cloud/client_test.go b/pkg/cloud/client_test.go index 99005b95..58807645 100644 --- a/pkg/cloud/client_test.go +++ b/pkg/cloud/client_test.go @@ -162,7 +162,7 @@ var _ = Describe("Client", func() { config := cloud.Config{ APIUrl: "http://1.1.1.1", } - result, err := cloud.NewClientFromConf(config, clientConfig) + result, err := cloud.NewClientFromConf(config, clientConfig, "") Ω(err).ShouldNot(HaveOccurred()) Ω(result).ShouldNot(BeNil()) }) @@ -174,8 +174,8 @@ var _ = Describe("Client", func() { config2 := cloud.Config{ APIUrl: "http://3.3.3.3", } - result1, _ := cloud.NewClientFromConf(config1, clientConfig) - result2, _ := cloud.NewClientFromConf(config2, clientConfig) + result1, _ := cloud.NewClientFromConf(config1, clientConfig, "") + result2, _ := cloud.NewClientFromConf(config2, clientConfig, "") Ω(result1).ShouldNot(Equal(result2)) }) @@ -186,8 +186,8 @@ var _ = Describe("Client", func() { config2 := cloud.Config{ APIUrl: "http://4.4.4.4", } - result1, _ := cloud.NewClientFromConf(config1, clientConfig) - result2, _ := cloud.NewClientFromConf(config2, clientConfig) + result1, _ := cloud.NewClientFromConf(config1, clientConfig, "") + result2, _ := cloud.NewClientFromConf(config2, clientConfig, "") Ω(result1).Should(Equal(result2)) }) }) diff --git a/pkg/cloud/cloud_suite_test.go b/pkg/cloud/cloud_suite_test.go index 0140c696..8f526fe0 100644 --- a/pkg/cloud/cloud_suite_test.go +++ b/pkg/cloud/cloud_suite_test.go @@ -70,7 +70,7 @@ func TestCloud(t *testing.T) { // Switch to test account user. realCloudClient, connectionErr = realCloudClient.NewClientInDomainAndAccount( - newAccount.Domain.Name, newAccount.Name) + newAccount.Domain.Name, newAccount.Name, "") Ω(connectionErr).ShouldNot(HaveOccurred()) } }) diff --git a/pkg/cloud/instance.go b/pkg/cloud/instance.go index 2ab9e52b..cba5750e 100644 --- a/pkg/cloud/instance.go +++ b/pkg/cloud/instance.go @@ -58,7 +58,7 @@ func setMachineDataFromVMMetrics(vmResponse *cloudstack.VirtualMachinesMetric, c func (c *client) ResolveVMInstanceDetails(csMachine *infrav1.CloudStackMachine) error { // Attempt to fetch by ID. if csMachine.Spec.InstanceID != nil { - vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByID(*csMachine.Spec.InstanceID, cloudstack.WithProject(c.config.ProjectID)) + vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByID(*csMachine.Spec.InstanceID, cloudstack.WithProject(c.user.Project.ID)) if err != nil && !strings.Contains(strings.ToLower(err.Error()), "no match found") { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return err @@ -72,7 +72,7 @@ func (c *client) ResolveVMInstanceDetails(csMachine *infrav1.CloudStackMachine) // Attempt fetch by name. if csMachine.Name != "" { - vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByName(csMachine.Name, cloudstack.WithProject(c.config.ProjectID)) + vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByName(csMachine.Name, cloudstack.WithProject(c.user.Project.ID)) if err != nil && !strings.Contains(strings.ToLower(err.Error()), "no match") { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return err @@ -88,7 +88,7 @@ func (c *client) ResolveVMInstanceDetails(csMachine *infrav1.CloudStackMachine) func (c *client) ResolveServiceOffering(csMachine *infrav1.CloudStackMachine, zoneID string) (offering cloudstack.ServiceOffering, retErr error) { if len(csMachine.Spec.Offering.ID) > 0 { - csOffering, count, err := c.cs.ServiceOffering.GetServiceOfferingByID(csMachine.Spec.Offering.ID, cloudstack.WithProject(c.config.ProjectID)) + csOffering, count, err := c.cs.ServiceOffering.GetServiceOfferingByID(csMachine.Spec.Offering.ID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return cloudstack.ServiceOffering{}, multierror.Append(retErr, errors.Wrapf( @@ -104,7 +104,7 @@ func (c *client) ResolveServiceOffering(csMachine *infrav1.CloudStackMachine, zo } return *csOffering, nil } - csOffering, count, err := c.cs.ServiceOffering.GetServiceOfferingByName(csMachine.Spec.Offering.Name, cloudstack.WithZone(zoneID), cloudstack.WithProject(c.config.ProjectID)) + csOffering, count, err := c.cs.ServiceOffering.GetServiceOfferingByName(csMachine.Spec.Offering.Name, cloudstack.WithZone(zoneID), cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return cloudstack.ServiceOffering{}, multierror.Append(retErr, errors.Wrapf( @@ -122,7 +122,7 @@ func (c *client) ResolveTemplate( zoneID string, ) (templateID string, retErr error) { if len(csMachine.Spec.Template.ID) > 0 { - csTemplate, count, err := c.cs.Template.GetTemplateByID(csMachine.Spec.Template.ID, "executable", cloudstack.WithProject(c.config.ProjectID)) + csTemplate, count, err := c.cs.Template.GetTemplateByID(csMachine.Spec.Template.ID, "executable", cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return "", multierror.Append(retErr, errors.Wrapf( @@ -138,7 +138,7 @@ func (c *client) ResolveTemplate( } return csMachine.Spec.Template.ID, nil } - templateID, count, err := c.cs.Template.GetTemplateID(csMachine.Spec.Template.Name, "executable", zoneID, cloudstack.WithProject(c.config.ProjectID)) + templateID, count, err := c.cs.Template.GetTemplateID(csMachine.Spec.Template.Name, "executable", zoneID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return "", multierror.Append(retErr, errors.Wrapf( @@ -156,7 +156,7 @@ func (c *client) ResolveTemplate( func (c *client) ResolveDiskOffering(csMachine *infrav1.CloudStackMachine, zoneID string) (diskOfferingID string, retErr error) { diskOfferingID = csMachine.Spec.DiskOffering.ID if len(csMachine.Spec.DiskOffering.Name) > 0 { - diskID, count, err := c.cs.DiskOffering.GetDiskOfferingID(csMachine.Spec.DiskOffering.Name, cloudstack.WithZone(zoneID), cloudstack.WithProject(c.config.ProjectID)) + diskID, count, err := c.cs.DiskOffering.GetDiskOfferingID(csMachine.Spec.DiskOffering.Name, cloudstack.WithZone(zoneID), cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return "", multierror.Append(retErr, errors.Wrapf( @@ -183,7 +183,7 @@ func (c *client) ResolveDiskOffering(csMachine *infrav1.CloudStackMachine, zoneI } func verifyDiskoffering(csMachine *infrav1.CloudStackMachine, c *client, diskOfferingID string, retErr error) (string, error) { - csDiskOffering, count, err := c.cs.DiskOffering.GetDiskOfferingByID(diskOfferingID, cloudstack.WithProject(c.config.ProjectID)) + csDiskOffering, count, err := c.cs.DiskOffering.GetDiskOfferingByID(diskOfferingID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return "", multierror.Append(retErr, errors.Wrapf( @@ -300,7 +300,7 @@ func (c *client) DeployVM( setIfNotEmpty(csMachine.Name, p.SetName) setIfNotEmpty(capiMachine.Name, p.SetDisplayname) setIfNotEmpty(diskOfferingID, p.SetDiskofferingid) - setIfNotEmpty(c.config.ProjectID, p.SetProjectid) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) setIntIfPositive(csMachine.Spec.DiskOffering.CustomSize, p.SetSize) setIfNotEmpty(csMachine.Spec.SSHKey, p.SetKeypair) @@ -331,7 +331,7 @@ func (c *client) DeployVM( // CloudStack may have created the VM even though it reported an error. We attempt to // retrieve the VM so we can populate the CloudStackMachine for the user to manually // clean up. - vm, findErr := findVirtualMachine(c.cs.VirtualMachine, templateID, fd, csMachine, c.config.ProjectID) + vm, findErr := findVirtualMachine(c.cs.VirtualMachine, templateID, fd, csMachine, c.user.Project.ID) if findErr != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(findErr) return fmt.Errorf("%v; find virtual machine: %v", err, findErr) @@ -455,7 +455,7 @@ func (c *client) listVMInstanceDatadiskVolumeIDs(instanceID string) ([]string, e p.SetVirtualmachineid(instanceID) // VM root volumes are destroyed automatically, no need to explicitly include p.SetType("DATADISK") - setIfNotEmpty(c.config.ProjectID, p.SetProjectid) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) listVOLResp, err := c.csAsync.Volume.ListVolumes(p) if err != nil { diff --git a/pkg/cloud/isolated_network.go b/pkg/cloud/isolated_network.go index 5967740f..af61636f 100644 --- a/pkg/cloud/isolated_network.go +++ b/pkg/cloud/isolated_network.go @@ -76,7 +76,7 @@ func (c *client) AssociatePublicIPAddress( p := c.cs.Address.NewAssociateIpAddressParams() p.SetIpaddress(isoNet.Spec.ControlPlaneEndpoint.Host) p.SetNetworkid(isoNet.Spec.ID) - setIfNotEmpty(c.config.ProjectID, p.SetProjectid) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) if _, err := c.cs.Address.AssociateIpAddress(p); err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return errors.Wrapf(err, @@ -103,7 +103,7 @@ func (c *client) CreateIsolatedNetwork(fd *infrav1.CloudStackFailureDomain, isoN // Do isolated network creation. p := c.cs.Network.NewCreateNetworkParams(isoNet.Spec.Name, offeringID, fd.Spec.Zone.ID) p.SetDisplaytext(isoNet.Spec.Name) - setIfNotEmpty(c.config.ProjectID, p.SetProjectid) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) resp, err := c.cs.Network.CreateNetwork(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -150,7 +150,7 @@ func (c *client) GetPublicIP( p.SetAllocatedonly(false) p.SetZoneid(fd.Spec.Zone.ID) setIfNotEmpty(ip, p.SetIpaddress) - setIfNotEmpty(c.config.ProjectID, p.SetProjectid) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) publicAddresses, err := c.cs.Address.ListPublicIpAddresses(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -171,7 +171,7 @@ func (c *client) GetPublicIP( // GetIsolatedNetwork gets an isolated network in the relevant Zone. func (c *client) GetIsolatedNetwork(isoNet *infrav1.CloudStackIsolatedNetwork) (retErr error) { - netDetails, count, err := c.cs.Network.GetNetworkByName(isoNet.Spec.Name, cloudstack.WithProject(c.config.ProjectID)) + netDetails, count, err := c.cs.Network.GetNetworkByName(isoNet.Spec.Name, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Network ID from %s", isoNet.Spec.Name)) @@ -183,7 +183,7 @@ func (c *client) GetIsolatedNetwork(isoNet *infrav1.CloudStackIsolatedNetwork) ( return nil } - netDetails, count, err = c.cs.Network.GetNetworkByID(isoNet.Spec.ID, cloudstack.WithProject(c.config.ProjectID)) + netDetails, count, err = c.cs.Network.GetNetworkByID(isoNet.Spec.ID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return multierror.Append(retErr, errors.Wrapf(err, "could not get Network by ID %s", isoNet.Spec.ID)) @@ -202,7 +202,7 @@ func (c *client) ResolveLoadBalancerRuleDetails( ) error { p := c.cs.LoadBalancer.NewListLoadBalancerRulesParams() p.SetPublicipid(isoNet.Status.PublicIPID) - setIfNotEmpty(c.config.ProjectID, p.SetProjectid) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) loadBalancerRules, err := c.cs.LoadBalancer.ListLoadBalancerRules(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -374,7 +374,7 @@ func (c *client) DeleteNetworkIfNotInUse(csCluster *infrav1.CloudStackCluster, n func (c *client) DisassociatePublicIPAddressIfNotInUse(isoNet *infrav1.CloudStackIsolatedNetwork) (retError error) { if tagsAllowDisposal, err := c.DoClusterTagsAllowDisposal(ResourceTypeIPAddress, isoNet.Status.PublicIPID); err != nil { return err - } else if publicIP, _, err := c.cs.Address.GetPublicIpAddressByID(isoNet.Status.PublicIPID, cloudstack.WithProject(c.config.ProjectID)); err != nil { + } else if publicIP, _, err := c.cs.Address.GetPublicIpAddressByID(isoNet.Status.PublicIPID, cloudstack.WithProject(c.user.Project.ID)); err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return err } else if publicIP == nil || publicIP.Issourcenat { // Can't disassociate an address if it's the source NAT address. diff --git a/pkg/cloud/network.go b/pkg/cloud/network.go index dde4200d..53637a70 100644 --- a/pkg/cloud/network.go +++ b/pkg/cloud/network.go @@ -52,7 +52,7 @@ func (c *client) ResolveNetwork(net *infrav1.Network) (retErr error) { // TODO rebuild this to consider cases with networks in many zones. // Use ListNetworks instead. netName := net.Name - netDetails, count, err := c.cs.Network.GetNetworkByName(netName, cloudstack.WithProject(c.config.ProjectID)) + netDetails, count, err := c.cs.Network.GetNetworkByName(netName, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Network ID from %s", netName)) @@ -66,7 +66,7 @@ func (c *client) ResolveNetwork(net *infrav1.Network) (retErr error) { } // Now get network details. - netDetails, count, err = c.cs.Network.GetNetworkByID(net.ID, cloudstack.WithProject(c.config.ProjectID)) + netDetails, count, err = c.cs.Network.GetNetworkByID(net.ID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { return multierror.Append(retErr, errors.Wrapf(err, "could not get Network by ID %s", net.ID)) } else if count != 1 { diff --git a/pkg/cloud/tags.go b/pkg/cloud/tags.go index 1056195c..28dc7907 100644 --- a/pkg/cloud/tags.go +++ b/pkg/cloud/tags.go @@ -129,7 +129,7 @@ func (c *client) GetTags(resourceType ResourceType, resourceID string) (map[stri p.SetResourceid(resourceID) p.SetResourcetype(string(resourceType)) p.SetListall(true) - setIfNotEmpty(c.config.ProjectID, p.SetProjectid) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) listTagResponse, err := c.cs.Resourcetags.ListTags(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) diff --git a/pkg/cloud/user_credentials.go b/pkg/cloud/user_credentials.go index 15d2d22f..b6c2a7f2 100644 --- a/pkg/cloud/user_credentials.go +++ b/pkg/cloud/user_credentials.go @@ -55,6 +55,15 @@ type Account struct { VMAvailable string } +// Project contains specifications that identify a project. +type Project struct { + Name string + ID string + CPUAvailable string + MemoryAvailable string + VMAvailable string +} + // User contains information uniquely identifying and scoping a user. type User struct { ID string @@ -62,6 +71,7 @@ type User struct { APIKey string SecretKey string Account + Project } // ResolveDomain resolves a domain's information. @@ -168,6 +178,35 @@ func (c *client) ResolveAccount(account *Account) error { return nil } +// ResolveAccount resolves an account's information. +func (c *client) ResolveProject(user *User) error { + if user.Project.Name == "" { + return nil + } + + p := c.cs.Project.NewListProjectsParams() + p.SetListall(true) + p.SetDomainid(user.Domain.ID) + p.SetName(user.Project.Name) + setIfNotEmpty(user.Project.ID, p.SetId) + resp, retErr := c.cs.Project.ListProjects(p) + if retErr != nil { + c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(retErr) + return retErr + } else if resp.Count == 0 { + return errors.Errorf("could not find account %s", user.Project.Name) + } else if resp.Count != 1 { + return errors.Errorf("expected 1 Project with name %s in domain ID %s, but got %d", + user.Project.Name, user.Domain.ID, resp.Count) + } + user.Project.ID = resp.Projects[0].Id + user.Project.Name = resp.Projects[0].Name + user.Project.CPUAvailable = resp.Projects[0].Cpuavailable + user.Project.MemoryAvailable = resp.Projects[0].Memoryavailable + user.Project.VMAvailable = resp.Projects[0].Vmavailable + return nil +} + // ResolveUser resolves a user's information. func (c *client) ResolveUser(user *User) error { // Resolve account prior to any user resolution activity. @@ -220,6 +259,10 @@ func (c *client) GetUserWithKeys(user *User) (bool, error) { return false, errors.Wrapf(err, "resolving account %s details", user.Account.Name) } + if err := c.ResolveProject(user); err != nil { + return false, errors.Wrapf(err, "resolving project %s details", user.Project.Name) + } + // List users and take first user that has already has api keys. p := c.cs.User.NewListUsersParams() p.SetAccount(user.Account.Name) diff --git a/pkg/cloud/user_credentials_test.go b/pkg/cloud/user_credentials_test.go index d25dc731..589523ac 100644 --- a/pkg/cloud/user_credentials_test.go +++ b/pkg/cloud/user_credentials_test.go @@ -480,7 +480,7 @@ var _ = Describe("User Credentials", func() { Ω(err).ShouldNot(HaveOccurred()) Ω(found).Should(BeTrue()) Ω(user.APIKey).ShouldNot(BeEmpty()) - newClient, err := client.NewClientInDomainAndAccount(user.Account.Domain.Name, user.Account.Name) + newClient, err := client.NewClientInDomainAndAccount(user.Account.Domain.Name, user.Account.Name, "") Ω(err).ShouldNot(HaveOccurred()) Ω(newClient).ShouldNot(BeNil()) }) diff --git a/pkg/cloud/zone.go b/pkg/cloud/zone.go index 7e895e4d..356f3956 100644 --- a/pkg/cloud/zone.go +++ b/pkg/cloud/zone.go @@ -56,7 +56,7 @@ func (c *client) ResolveZone(zSpec *infrav1.CloudStackZoneSpec) (retErr error) { // ResolveNetworkForZone fetches details on Zone's specified network. func (c *client) ResolveNetworkForZone(zSpec *infrav1.CloudStackZoneSpec) (retErr error) { netName := zSpec.Network.Name - netDetails, count, err := c.cs.Network.GetNetworkByName(netName, cloudstack.WithProject(c.config.ProjectID)) + netDetails, count, err := c.cs.Network.GetNetworkByName(netName, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Network ID from %v", netName)) @@ -70,7 +70,7 @@ func (c *client) ResolveNetworkForZone(zSpec *infrav1.CloudStackZoneSpec) (retEr } // Now get network details. - netDetails, count, err = c.cs.Network.GetNetworkByID(zSpec.Network.ID, cloudstack.WithProject(c.config.ProjectID)) + netDetails, count, err = c.cs.Network.GetNetworkByID(zSpec.Network.ID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { return multierror.Append(retErr, errors.Wrapf(err, "could not get Network by ID %s", zSpec.Network.ID)) } else if count != 1 { From b61ae4ce79fc05749f942f0196cdabc5f11125dd Mon Sep 17 00:00:00 2001 From: Vishesh Date: Tue, 21 May 2024 15:03:23 +0530 Subject: [PATCH 5/8] Add project resource limit checks --- pkg/cloud/instance.go | 34 +++++++++ pkg/cloud/instance_test.go | 138 +++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) diff --git a/pkg/cloud/instance.go b/pkg/cloud/instance.go index cba5750e..f7564d3c 100644 --- a/pkg/cloud/instance.go +++ b/pkg/cloud/instance.go @@ -257,6 +257,35 @@ func (c *client) CheckDomainLimits(fd *infrav1.CloudStackFailureDomain, offering return nil } +// CheckProjectLimits Checks the project's limit of VM, CPU & Memory +func (c *client) CheckProjectLimits(fd *infrav1.CloudStackFailureDomain, offering *cloudstack.ServiceOffering) error { + if c.user.Project.ID == "" { + return nil + } + + if c.user.Project.CPUAvailable != "Unlimited" { + cpuAvailable, err := strconv.ParseInt(c.user.Project.CPUAvailable, 10, 0) + if err == nil && int64(offering.Cpunumber) > cpuAvailable { + return fmt.Errorf("CPU available (%d) in project can't fulfil the requirement: %d", cpuAvailable, offering.Cpunumber) + } + } + + if c.user.Project.MemoryAvailable != "Unlimited" { + memoryAvailable, err := strconv.ParseInt(c.user.Project.MemoryAvailable, 10, 0) + if err == nil && int64(offering.Memory) > memoryAvailable { + return fmt.Errorf("memory available (%d) in project can't fulfil the requirement: %d", memoryAvailable, offering.Memory) + } + } + + if c.user.Project.VMAvailable != "Unlimited" { + vmAvailable, err := strconv.ParseInt(c.user.Project.VMAvailable, 10, 0) + if err == nil && vmAvailable < 1 { + return fmt.Errorf("VM Limit in project has reached it's maximum value") + } + } + return nil +} + // CheckLimits will check the account & domain limits func (c *client) CheckLimits( fd *infrav1.CloudStackFailureDomain, @@ -272,6 +301,11 @@ func (c *client) CheckLimits( return err } + err = c.CheckProjectLimits(fd, offering) + if err != nil { + return err + } + return nil } diff --git a/pkg/cloud/instance_test.go b/pkg/cloud/instance_test.go index f3ccb706..839c0c1f 100644 --- a/pkg/cloud/instance_test.go +++ b/pkg/cloud/instance_test.go @@ -276,6 +276,12 @@ var _ = Describe("Instance", func() { MemoryAvailable: "2048", VMAvailable: "20", }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, } c := cloud.NewClientFromCSAPIClient(mockClient, user) Ω(c.GetOrCreateVMInstance( @@ -304,6 +310,12 @@ var _ = Describe("Instance", func() { MemoryAvailable: "2048", VMAvailable: "20", }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, } c := cloud.NewClientFromCSAPIClient(mockClient, user) Ω(c.GetOrCreateVMInstance( @@ -311,6 +323,40 @@ var _ = Describe("Instance", func() { Should(MatchError(MatchRegexp("CPU available .* in domain can't fulfil the requirement:.*"))) }) + It("returns errors when there are not enough available CPU in project", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "1", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(MatchError(MatchRegexp("CPU available .* in project can't fulfil the requirement:.*"))) + }) + It("returns errors when there is not enough available memory in account", func() { expectVMNotFound() dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 @@ -332,6 +378,12 @@ var _ = Describe("Instance", func() { MemoryAvailable: "512", VMAvailable: "20", }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, } c := cloud.NewClientFromCSAPIClient(mockClient, user) Ω(c.GetOrCreateVMInstance( @@ -360,6 +412,12 @@ var _ = Describe("Instance", func() { MemoryAvailable: "2048", VMAvailable: "20", }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, } c := cloud.NewClientFromCSAPIClient(mockClient, user) Ω(c.GetOrCreateVMInstance( @@ -367,6 +425,40 @@ var _ = Describe("Instance", func() { Should(MatchError(MatchRegexp("memory available .* in domain can't fulfil the requirement:.*"))) }) + It("returns errors when there is not enough available memory in project", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "512", + VMAvailable: "20", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(MatchError(MatchRegexp("memory available .* in project can't fulfil the requirement:.*"))) + }) + It("returns errors when there is not enough available VM limit in account", func() { expectVMNotFound() dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 @@ -388,6 +480,12 @@ var _ = Describe("Instance", func() { MemoryAvailable: "2048", VMAvailable: "0", }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, } c := cloud.NewClientFromCSAPIClient(mockClient, user) Ω(c.GetOrCreateVMInstance( @@ -416,12 +514,52 @@ var _ = Describe("Instance", func() { MemoryAvailable: "2048", VMAvailable: "10", }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, } c := cloud.NewClientFromCSAPIClient(mockClient, user) Ω(c.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). Should(MatchError("VM Limit in domain has reached it's maximum value")) }) + + It("returns errors when there is not enough available VM limit in project", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "10", + }, + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "10", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "0", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(MatchError("VM Limit in project has reached it's maximum value")) + }) }) It("handles deployment errors", func() { From 5eea6e8322e134c4e6957a21cd88c9fe6d035b71 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Tue, 21 May 2024 15:27:29 +0530 Subject: [PATCH 6/8] fixup unit tests --- controllers/cloudstackaffinitygroup_controller_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/cloudstackaffinitygroup_controller_test.go b/controllers/cloudstackaffinitygroup_controller_test.go index c74ecec1..ad35b986 100644 --- a/controllers/cloudstackaffinitygroup_controller_test.go +++ b/controllers/cloudstackaffinitygroup_controller_test.go @@ -78,10 +78,10 @@ var _ = Describe("CloudStackAffinityGroupReconciler", func() { return false }, timeout).WithPolling(pollInterval).Should(BeTrue()) - Ω(k8sClient.Delete(ctx, dummies.CSAffinityGroup)) mockCloudClient.EXPECT().FetchAffinityGroup(gomock.Any()).Do(func(arg1 interface{}) { arg1.(*cloud.AffinityGroup).ID = "" }).AnyTimes().Return(nil) + Ω(k8sClient.Delete(ctx, dummies.CSAffinityGroup)) // Once the affinity group id was set to "" the controller should remove the finalizer and unblock deleting affinity group resource Eventually(func() bool { From eba72cfb81f828c2a275b0bb853d76a71596de8f Mon Sep 17 00:00:00 2001 From: Vishesh Date: Tue, 21 May 2024 17:29:58 +0530 Subject: [PATCH 7/8] Fixup --- pkg/cloud/user_credentials.go | 5 +++-- test/e2e/README.md | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/cloud/user_credentials.go b/pkg/cloud/user_credentials.go index b6c2a7f2..d6d73225 100644 --- a/pkg/cloud/user_credentials.go +++ b/pkg/cloud/user_credentials.go @@ -178,7 +178,7 @@ func (c *client) ResolveAccount(account *Account) error { return nil } -// ResolveAccount resolves an account's information. +// ResolveProject resolves a project's information. func (c *client) ResolveProject(user *User) error { if user.Project.Name == "" { return nil @@ -187,6 +187,7 @@ func (c *client) ResolveProject(user *User) error { p := c.cs.Project.NewListProjectsParams() p.SetListall(true) p.SetDomainid(user.Domain.ID) + p.SetAccount(user.Account.Name) p.SetName(user.Project.Name) setIfNotEmpty(user.Project.ID, p.SetId) resp, retErr := c.cs.Project.ListProjects(p) @@ -194,7 +195,7 @@ func (c *client) ResolveProject(user *User) error { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(retErr) return retErr } else if resp.Count == 0 { - return errors.Errorf("could not find account %s", user.Project.Name) + return errors.Errorf("could not find project %s", user.Project.Name) } else if resp.Count != 1 { return errors.Errorf("expected 1 Project with name %s in domain ID %s, but got %d", user.Project.Name, user.Domain.ID, resp.Count) diff --git a/test/e2e/README.md b/test/e2e/README.md index ea5aa85b..d0ba67aa 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -51,10 +51,9 @@ stringData: secret-key: XXXX api-url: http://1.2.3.4:8080/client/api verify-ssl: "false" - project-id: ``` This will be applied to the kind cluster that hosts CAPI/CAPC for the test, allowing CAPC to access the cluster. -The api-key and secret-key can be found or generated at Home > Accounts > admin > Users > admin of the ACS management UI. `verify-ssl` is an optional flag and its default value is true. CAPC skips verifying the host SSL certificates when the flag is set to false. `project-id` is also optional and is used to specify the project id to be used for the k8s cluster. +The api-key and secret-key can be found or generated at Home > Accounts > admin > Users > admin of the ACS management UI. `verify-ssl` is an optional flag and its default value is true. CAPC skips verifying the host SSL certificates when the flag is set to false. ### Running the e2e tests From 28faaac585809618c44b01a644c23fe0446b347f Mon Sep 17 00:00:00 2001 From: Vishesh Date: Tue, 21 May 2024 19:11:39 +0530 Subject: [PATCH 8/8] Add e2e test for project --- docs/book/src/getting-started.md | 1 - test/e2e/common.go | 20 +++- test/e2e/config/cloudstack.yaml | 2 + .../cloudstack-cluster.yaml | 18 +++ .../kustomization.yaml | 7 ++ .../v1beta3/cluster-template-project/md.yaml | 28 +++++ test/e2e/project.go | 107 ++++++++++++++++++ test/e2e/project_test.go | 39 +++++++ 8 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/cloudstack-cluster.yaml create mode 100644 test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/kustomization.yaml create mode 100644 test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/md.yaml create mode 100644 test/e2e/project.go create mode 100644 test/e2e/project_test.go diff --git a/docs/book/src/getting-started.md b/docs/book/src/getting-started.md index ff79528e..5c10878e 100644 --- a/docs/book/src/getting-started.md +++ b/docs/book/src/getting-started.md @@ -35,7 +35,6 @@ secret-key: api-url: verify-ssl: "false" - project-id: # Optional. If provided, the provider will use this project id for all operations ``` - Apply this secret to the management cluster: diff --git a/test/e2e/common.go b/test/e2e/common.go index c040443a..53518904 100644 --- a/test/e2e/common.go +++ b/test/e2e/common.go @@ -280,12 +280,16 @@ func DestroyOneMachine(client *cloudstack.CloudStackClient, clusterName string, } func CheckAffinityGroupsDeleted(client *cloudstack.CloudStackClient, affinityIds []string) error { + return CheckAffinityGroupsDeletedInProject(client, affinityIds, "") +} + +func CheckAffinityGroupsDeletedInProject(client *cloudstack.CloudStackClient, affinityIds []string, project string) error { if len(affinityIds) == 0 { return errors.New("affinityIds are empty") } for _, affinityId := range affinityIds { - affinity, count, _ := client.AffinityGroup.GetAffinityGroupByID(affinityId) + affinity, count, _ := client.AffinityGroup.GetAffinityGroupByID(affinityId, cloudstack.WithProject(project)) if count > 0 { return errors.New("Affinity group " + affinity.Name + " still exists") } @@ -312,9 +316,21 @@ func GetHostCount(client *cloudstack.CloudStackClient, zoneName string) int { } func CheckAffinityGroup(client *cloudstack.CloudStackClient, clusterName string, affinityType string) []string { + return CheckAffinityGroupInProject(client, clusterName, affinityType, "") +} + +func CheckAffinityGroupInProject(client *cloudstack.CloudStackClient, clusterName string, affinityType string, project string) []string { By("Listing all machines") p := client.VirtualMachine.NewListVirtualMachinesParams() p.SetListall(true) + + if project != "" { + projectID, _, err := client.Project.GetProjectID(project) + if err != nil { + Fail("Failed to get project: " + err.Error()) + } + p.SetProjectid(projectID) + } listResp, err := client.VirtualMachine.ListVirtualMachines(p) if err != nil { Fail("Failed to list machines: " + err.Error()) @@ -334,7 +350,7 @@ func CheckAffinityGroup(client *cloudstack.CloudStackClient, clusterName string, for _, affinity := range vm.Affinitygroup { affinityIds = append(affinityIds, affinity.Id) - affinity, _, _ := client.AffinityGroup.GetAffinityGroupByID(affinity.Id) + affinity, _, _ := client.AffinityGroup.GetAffinityGroupByID(affinity.Id, cloudstack.WithProject(project)) if err != nil { Fail("Failed to get affinity group for " + affinity.Id + " : " + err.Error()) } diff --git a/test/e2e/config/cloudstack.yaml b/test/e2e/config/cloudstack.yaml index 11ef0e3f..ee06f42c 100644 --- a/test/e2e/config/cloudstack.yaml +++ b/test/e2e/config/cloudstack.yaml @@ -94,6 +94,7 @@ providers: - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-disk-offering.yaml" - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-custom-disk-offering.yaml" - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-subdomain.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-project.yaml" - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-ip.yaml" - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before.yaml" - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after.yaml" @@ -124,6 +125,7 @@ variables: CLOUDSTACK_INVALID_ZONE_NAME: zoneXXXX CLOUDSTACK_INVALID_NETWORK_NAME: networkXXXX CLOUDSTACK_ACCOUNT_NAME: admin + CLOUDSTACK_PROJECT_NAME: capc-e2e-test CLOUDSTACK_INVALID_ACCOUNT_NAME: accountXXXX CLOUDSTACK_DOMAIN_NAME: ROOT CLOUDSTACK_INVALID_DOMAIN_NAME: domainXXXX diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/cloudstack-cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/cloudstack-cluster.yaml new file mode 100644 index 00000000..510f3f97 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/cloudstack-cluster.yaml @@ -0,0 +1,18 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name : ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + project: ${CLOUDSTACK_PROJECT_NAME} + controlPlaneEndpoint: + host: "" + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/kustomization.yaml new file mode 100644 index 00000000..9953367a --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/kustomization.yaml @@ -0,0 +1,7 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cloudstack-cluster.yaml +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/md.yaml new file mode 100644 index 00000000..a1ee84b4 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/md.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} + affinity: pro +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} + affinity: pro diff --git a/test/e2e/project.go b/test/e2e/project.go new file mode 100644 index 00000000..bcbf7ce3 --- /dev/null +++ b/test/e2e/project.go @@ -0,0 +1,107 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + "fmt" + "os" + "path/filepath" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" + + "sigs.k8s.io/cluster-api/test/framework/clusterctl" + "sigs.k8s.io/cluster-api/util" +) + +// ProjectSpec implements a test that verifies that an app deployed to the workload cluster works. +func ProjectSpec(ctx context.Context, inputGetter func() CommonSpecInput) { + var ( + specName = "project" + projectName string + input CommonSpecInput + namespace *corev1.Namespace + cancelWatches context.CancelFunc + clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult + affinityIds []string + ) + + BeforeEach(func() { + Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName) + input = inputGetter() + Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName) + Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName) + Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName) + Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName) + + Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersion)) + + // Setup a Namespace where to host objects for this spec and create a watcher for the namespace events. + namespace, cancelWatches = setupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder) + clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult) + + Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName) + input = inputGetter() + + projectName = os.Getenv("CLOUDSTACK_PROJECT_NAME") + csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath()) + project, _, err := csClient.Project.GetProjectByName(projectName) + if (err != nil) || (project == nil) { + Skip("Failed to fetch project") + } + }) + + It("Should create a cluster in a project", func() { + clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{ + ClusterProxy: input.BootstrapClusterProxy, + CNIManifestPath: input.E2EConfig.GetVariable(CNIPath), + ConfigCluster: clusterctl.ConfigClusterInput{ + LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()), + ClusterctlConfigPath: input.ClusterctlConfigPath, + KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(), + InfrastructureProvider: clusterctl.DefaultInfrastructureProvider, + Flavor: specName, + Namespace: namespace.Name, + ClusterName: fmt.Sprintf("%s-%s", specName, util.RandomString(6)), + KubernetesVersion: input.E2EConfig.GetVariable(KubernetesVersion), + ControlPlaneMachineCount: pointer.Int64Ptr(1), + WorkerMachineCount: pointer.Int64Ptr(1), + }, + WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"), + WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"), + WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"), + }, clusterResources) + + csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath()) + affinityIds = CheckAffinityGroupInProject(csClient, clusterResources.Cluster.Name, "pro", projectName) + }) + + AfterEach(func() { + // Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself. + dumpSpecResourcesAndCleanup(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder, namespace, cancelWatches, clusterResources.Cluster, input.E2EConfig.GetIntervals, input.SkipCleanup) + + csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath()) + err := CheckAffinityGroupsDeletedInProject(csClient, affinityIds, projectName) + if err != nil { + Fail(err.Error()) + } + By("PASSED!") + }) +} diff --git a/test/e2e/project_test.go b/test/e2e/project_test.go new file mode 100644 index 00000000..de9605a6 --- /dev/null +++ b/test/e2e/project_test.go @@ -0,0 +1,39 @@ +//go:build e2e +// +build e2e + +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("When testing project", func() { + ProjectSpec(context.TODO(), func() CommonSpecInput { + return CommonSpecInput{ + E2EConfig: e2eConfig, + ClusterctlConfigPath: clusterctlConfigPath, + BootstrapClusterProxy: bootstrapClusterProxy, + ArtifactFolder: artifactFolder, + SkipCleanup: skipCleanup, + } + }) + +})