Skip to content

Commit 37e4e05

Browse files
author
Joshua Reed
committed
Close to being able to add/delete failure domains.
1 parent 7c9ebb2 commit 37e4e05

File tree

4 files changed

+68
-6
lines changed

4 files changed

+68
-6
lines changed

api/v1beta2/cloudstackcluster_webhook.go

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package v1beta2
1818

1919
import (
2020
"fmt"
21-
"reflect"
2221

2322
"k8s.io/apimachinery/pkg/api/errors"
2423
"k8s.io/apimachinery/pkg/runtime"
@@ -94,12 +93,12 @@ func (r *CloudStackCluster) ValidateUpdate(old runtime.Object) error {
9493
}
9594
oldSpec := oldCluster.Spec
9695

97-
// No spec fields may be updated.
9896
errorList := field.ErrorList(nil)
99-
if !reflect.DeepEqual(oldSpec.FailureDomains, spec.FailureDomains) {
100-
errorList = append(errorList, field.Forbidden(
101-
field.NewPath("spec", "FailureDomains"), "FailureDomains and sub-attributes may not be modified after creation"))
97+
98+
if err := ValidateFailureDomainUpdates(oldSpec.FailureDomains, spec.FailureDomains); err != nil {
99+
errorList = append(errorList, err)
102100
}
101+
103102
if oldSpec.ControlPlaneEndpoint.Host != "" { // Need to allow one time endpoint setting via CAPC cluster controller.
104103
errorList = webhookutil.EnsureStringFieldsAreEqual(
105104
spec.ControlPlaneEndpoint.Host, oldSpec.ControlPlaneEndpoint.Host, "controlplaneendpoint.host", errorList)
@@ -111,6 +110,43 @@ func (r *CloudStackCluster) ValidateUpdate(old runtime.Object) error {
111110
return webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList)
112111
}
113112

113+
// ValidateFailureDomainUpdates verifies that at least one failure domain has not been deleted, and
114+
// failure domains that are held over have not been modified.
115+
func ValidateFailureDomainUpdates(oldFDs, newFDs []CloudStackFailureDomainSpec) *field.Error {
116+
newFDsByName := map[string]CloudStackFailureDomainSpec{}
117+
for _, newFD := range newFDs {
118+
newFDsByName[newFD.Name] = newFD
119+
}
120+
121+
atLeastOneRemains := false
122+
for _, oldFD := range oldFDs {
123+
if newFD, present := newFDsByName[oldFD.Name]; present {
124+
atLeastOneRemains = true
125+
if !FailureDomainsEqual(newFD, oldFD) {
126+
return field.Forbidden(field.NewPath("spec", "FailureDomains"),
127+
fmt.Sprintf("Cannot change FailureDomain %s", oldFD.Name))
128+
}
129+
}
130+
}
131+
if !atLeastOneRemains {
132+
return field.Forbidden(field.NewPath("spec", "FailureDomains"), "At least one FailureDomain must be unchanged on udpate.")
133+
}
134+
return nil
135+
}
136+
137+
// FailureDomainsEqual is a manual deep equal on failure domains.
138+
func FailureDomainsEqual(fd1, fd2 CloudStackFailureDomainSpec) bool {
139+
return fd1.Name == fd2.Name &&
140+
fd1.ACSEndpoint == fd2.ACSEndpoint &&
141+
fd1.Account == fd2.Account &&
142+
fd1.Domain == fd2.Domain &&
143+
fd1.Zone.Name == fd2.Zone.Name &&
144+
fd1.Zone.ID == fd2.Zone.ID &&
145+
fd1.Zone.Network.Name == fd2.Zone.Network.Name &&
146+
fd1.Zone.Network.ID == fd2.Zone.Network.ID &&
147+
fd1.Zone.Network.Type == fd2.Zone.Network.Type
148+
}
149+
114150
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
115151
func (r *CloudStackCluster) ValidateDelete() error {
116152
cloudstackclusterlog.V(1).Info("entered validate delete webhook", "api resource name", r.Name)

controllers/cloudstackcluster_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ func (r *CloudStackClusterReconciliationRunner) Reconcile() (res ctrl.Result, re
9090
r.SetFailureDomainsStatusMap,
9191
r.CreateFailureDomains(r.ReconciliationSubject.Spec.FailureDomains),
9292
r.GetFailureDomains(r.FailureDomains),
93+
r.RemoveExtraneousFailureDomains(r.FailureDomains),
9394
r.VerifyFailureDomainCRDs,
9495
r.SetReady)
9596
}

controllers/utils/failuredomains.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,30 @@ func (r *ReconciliationRunner) GetFailureDomains(fds *infrav1.CloudStackFailureD
7272
}
7373
}
7474

75+
// RemoveExtraneousFailureDomains deletes failure domains no longer listed under the CloudStackCluster's spec.
76+
func (r *ReconciliationRunner) RemoveExtraneousFailureDomains(fds *infrav1.CloudStackFailureDomainList) CloudStackReconcilerMethod {
77+
return func() (ctrl.Result, error) {
78+
// Toss together a precense map.
79+
fdPresenceByName := map[string]bool{}
80+
for _, fdSpec := range r.CSCluster.Spec.FailureDomains {
81+
name := fdSpec.Name
82+
if !strings.HasSuffix(name, "-"+r.CAPICluster.ClusterName) { // Add cluster name suffix if missing.
83+
name = name + "-" + r.CAPICluster.Name
84+
}
85+
fdPresenceByName[name] = true
86+
}
87+
// Send a deletion request for each FailureDomain no speced for.
88+
for _, fd := range fds.Items {
89+
if _, present := fdPresenceByName[fd.Name]; !present {
90+
if err := r.K8sClient.Delete(r.RequestCtx, &fd); err != nil {
91+
return ctrl.Result{}, errors.Wrap(err, "failed to delete obsolete failure domain")
92+
}
93+
}
94+
}
95+
return ctrl.Result{}, nil
96+
}
97+
}
98+
7599
// GetFailureDomainsAndRequeueIfMissing gets CloudStackFailureDomains owned by a CloudStackCluster and requeues if none are found.
76100
func (r *ReconciliationRunner) GetFailureDomainsAndRequeueIfMissing(fds *infrav1.CloudStackFailureDomainList) CloudStackReconcilerMethod {
77101
return func() (ctrl.Result, error) {

pkg/cloud/instance.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,8 @@ func (c *client) DestroyVMInstance(csMachine *infrav1.CloudStackMachine) error {
319319
return err
320320
}
321321

322-
return errors.New("VM deletion in progress")
322+
return nil
323+
//return errors.New("VM deletion in progress")
323324
}
324325

325326
func (c *client) listVMInstanceDatadiskVolumeIDs(instanceID string) ([]string, error) {

0 commit comments

Comments
 (0)