Skip to content

Commit

Permalink
feat: add support for CloudStack projects
Browse files Browse the repository at this point in the history
  • Loading branch information
hrak committed Sep 12, 2024
1 parent 834a308 commit be60782
Show file tree
Hide file tree
Showing 26 changed files with 546 additions and 173 deletions.
4 changes: 2 additions & 2 deletions api/v1beta1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 11 additions & 7 deletions api/v1beta2/cloudstackfailuredomain_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,23 @@ type Network struct {
// +optional
ID string `json:"id,omitempty"`

// Cloudstack Network Name the cluster is built in.
Name string `json:"name"`

// Cloudstack Network Type the cluster is built in.
// +optional
Type string `json:"type,omitempty"`

// Cloudstack Network Name the cluster is built in.
Name string `json:"name"`
}

// CloudStackZoneSpec specifies a Zone's details.
type CloudStackZoneSpec struct {
// Name.
// Zone ID.
//+optional
Name string `json:"name,omitempty"`
ID string `json:"id,omitempty"`

// ID.
// Zone Name.
//+optional
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`

// The network within the Zone to use.
Network Network `json:"network"`
Expand All @@ -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"`
}
Expand Down
10 changes: 6 additions & 4 deletions api/v1beta2/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 11 additions & 7 deletions api/v1beta3/cloudstackfailuredomain_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ type Network struct {
//+optional
ID string `json:"id,omitempty"`

// Cloudstack Network Name the cluster is built in.
Name string `json:"name"`

// Cloudstack Network Type the cluster is built in.
//+optional
Type string `json:"type,omitempty"`

// Cloudstack Network Name the cluster is built in.
Name string `json:"name"`

// CIDR is the IP address range of the network.
//+optional
CIDR string `json:"cidr,omitempty"`
Expand All @@ -65,13 +65,13 @@ type Network struct {

// CloudStackZoneSpec specifies a Zone's details.
type CloudStackZoneSpec struct {
// Name.
// Zone ID.
//+optional
Name string `json:"name,omitempty"`
ID string `json:"id,omitempty"`

// ID.
// Zone Name.
//+optional
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`

// The network within the Zone to use.
Network Network `json:"network"`
Expand All @@ -93,6 +93,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"`
}
Expand Down
10 changes: 0 additions & 10 deletions api/v1beta3/cloudstackmachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,6 @@ func (r *CloudStackMachine) CompressUserdata() bool {
return r.Spec.UncompressedUserData == nil || !*r.Spec.UncompressedUserData
}

type CloudStackResourceIdentifier struct {
// Cloudstack resource ID.
//+optional
ID string `json:"id,omitempty"`

// Cloudstack resource Name.
//+optional
Name string `json:"name,omitempty"`
}

type CloudStackResourceDiskOffering struct {
CloudStackResourceIdentifier `json:",inline"`
// Desired disk size. Used if disk offering is customizable as indicated by the ACS field 'Custom Disk Size'.
Expand Down
11 changes: 11 additions & 0 deletions api/v1beta3/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ limitations under the License.

package v1beta3

// CloudStackResourceIdentifier is used to identify any CloudStack resource that can be referenced by either ID or Name.
type CloudStackResourceIdentifier struct {
// Cloudstack resource ID.
//+optional
ID string `json:"id,omitempty"`

// Cloudstack resource Name.
//+optional
Name string `json:"name,omitempty"`
}

// LoadBalancer represents basic information about the associated OpenStack LoadBalancer.
type LoadBalancer struct {
IPAddress string `json:"ipAddress"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,17 @@ 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:
id:
description: ID.
description: Zone ID.
type: string
name:
description: Name.
description: Zone Name.
type: string
network:
description: The network within the Zone to use.
Expand Down Expand Up @@ -427,14 +430,17 @@ 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:
id:
description: ID.
description: Zone ID.
type: string
name:
description: Name.
description: Zone Name.
type: string
network:
description: The network within the Zone to use.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,17 @@ 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:
id:
description: ID.
description: Zone ID.
type: string
name:
description: Name.
description: Zone Name.
type: string
network:
description: The network within the Zone to use.
Expand Down Expand Up @@ -163,14 +166,17 @@ 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:
id:
description: ID.
description: Zone ID.
type: string
name:
description: Name.
description: Zone Name.
type: string
network:
description: The network within the Zone to use.
Expand Down
41 changes: 41 additions & 0 deletions controllers/cloudstackaffinitygroup_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import (
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"

infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3"
"sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud"
dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3"
)

Expand All @@ -38,11 +40,34 @@ var _ = Describe("CloudStackAffinityGroupReconciler", func() {
// Modify failure domain name the same way the cluster controller would.
dummies.CSAffinityGroup.Spec.FailureDomainName = dummies.CSFailureDomain1.Spec.Name

mockCloudClient.EXPECT().GetOrCreateAffinityGroup(gomock.Any()).AnyTimes()

Ω(k8sClient.Create(ctx, dummies.CSFailureDomain1)).Should(Succeed())
Ω(k8sClient.Create(ctx, dummies.CSAffinityGroup)).Should(Succeed())

// Test that the AffinityGroup controller sets Status.Ready to true.
Eventually(func() bool {
nameSpaceFilter := &client.ListOptions{Namespace: dummies.ClusterNameSpace}
affinityGroups := &infrav1.CloudStackAffinityGroupList{}
if err := k8sClient.List(ctx, affinityGroups, nameSpaceFilter); err == nil {
if len(affinityGroups.Items) == 1 {
return affinityGroups.Items[0].Status.Ready
}
}

return false
}, timeout).WithPolling(pollInterval).Should(BeTrue())
})

It("Should remove affinity group finalizer if corresponding affinity group is not present on Cloudstack.", func() {
// Modify failure domain name the same way the cluster controller would.
dummies.CSAffinityGroup.Spec.FailureDomainName = dummies.CSFailureDomain1.Spec.Name

mockCloudClient.EXPECT().GetOrCreateAffinityGroup(gomock.Any()).AnyTimes()

Ω(k8sClient.Create(ctx, dummies.CSFailureDomain1)).Should(Succeed())
Ω(k8sClient.Create(ctx, dummies.CSAffinityGroup)).Should(Succeed())

// Test that the AffinityGroup controller sets Status.Ready to true.
Eventually(func() bool {
nameSpaceFilter := &client.ListOptions{Namespace: dummies.ClusterNameSpace}
Expand All @@ -52,7 +77,23 @@ var _ = Describe("CloudStackAffinityGroupReconciler", func() {
return affinityGroups.Items[0].Status.Ready
}
}
return false
}, timeout).WithPolling(pollInterval).Should(BeTrue())

mockCloudClient.EXPECT().FetchAffinityGroup(gomock.Any()).Do(func(arg1 interface{}) {
arg1.(*cloud.AffinityGroup).ID = ""
}).AnyTimes().Return(nil)
Ω(k8sClient.Delete(ctx, dummies.CSAffinityGroup)).Should(Succeed())

Ω(k8sClient.Delete(ctx, dummies.CSAffinityGroup)).Should(Succeed())

// Once the affinity group id was set to "" the controller should remove the finalizer and unblock deleting affinity group resource
Eventually(func() bool {
retrievedAffinityGroup := &infrav1.CloudStackAffinityGroup{}
affinityGroupKey := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.AffinityGroup.Name}
if err := k8sClient.Get(ctx, affinityGroupKey, retrievedAffinityGroup); err != nil {
return errors.IsNotFound(err)
}
return false
}, timeout).WithPolling(pollInterval).Should(BeTrue())
})
Expand Down
4 changes: 2 additions & 2 deletions controllers/utils/failuredomains.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,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, cloud.WithProject(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, cloud.WithProject(fdSpec.Project))
if err != nil {
return ctrl.Result{}, err
}
Expand Down
Loading

0 comments on commit be60782

Please sign in to comment.