Skip to content

Commit 3a1d0e2

Browse files
committed
Create external managed kubernetes cluster on CloudStack
1 parent 9a19e2e commit 3a1d0e2

16 files changed

+442
-16
lines changed

api/v1beta3/cloudstackcluster_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ type CloudStackClusterStatus struct {
4343
// +optional
4444
FailureDomains clusterv1.FailureDomains `json:"failureDomains,omitempty"`
4545

46+
// Id of CAPC managed kubernetes cluster created in CloudStack
47+
// +optional
48+
CloudStackClusterID string `json:"cloudStackClusterId"`
49+
4650
// Reflects the readiness of the CS cluster.
4751
Ready bool `json:"ready"`
4852
}

config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackclusters.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,9 @@ spec:
422422
status:
423423
description: The actual cluster state reported by CloudStack.
424424
properties:
425+
cloudStackClusterId:
426+
description: Id of CAPC managed kubernetes cluster created in CloudStack
427+
type: string
425428
failureDomains:
426429
additionalProperties:
427430
description: FailureDomainSpec is the Schema for Cluster API failure

controllers/cloudstackcluster_controller.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"reflect"
23+
"strings"
2324

2425
ctrl "sigs.k8s.io/controller-runtime"
2526
"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -94,9 +95,28 @@ func (r *CloudStackClusterReconciliationRunner) Reconcile() (res ctrl.Result, re
9495
r.GetFailureDomains(r.FailureDomains),
9596
r.RemoveExtraneousFailureDomains(r.FailureDomains),
9697
r.VerifyFailureDomainCRDs,
98+
r.GetOrCreateUnmanagedCluster,
9799
r.SetReady)
98100
}
99101

102+
// GetOrCreateUnmanagedCluster checks if an unmanaged cluster is present in Cloudstack else creates one.
103+
func (r *CloudStackClusterReconciliationRunner) GetOrCreateUnmanagedCluster() (ctrl.Result, error) {
104+
res, err := r.AsFailureDomainUser(&r.CSCluster.Spec.FailureDomains[0])()
105+
if r.ShouldReturn(res, err) {
106+
return res, err
107+
}
108+
err = r.CSUser.GetOrCreateUnmanagedCluster(r.CAPICluster, r.ReconciliationSubject, &r.FailureDomains.Items[0].Spec)
109+
if err != nil {
110+
if strings.Contains(err.Error(), "Kubernetes Service plugin is disabled") {
111+
r.Log.Info("Kubernetes Service plugin is disabled on CloudStack. Skipping ExternalManaged kubernetes cluster creation")
112+
return ctrl.Result{}, nil
113+
}
114+
// Not requeueing the failure to support CloudStack v4.18 and before
115+
r.Log.Info(fmt.Sprintf("Failed creating ExternalManaged kubernetes cluster on CloudStack. Error: %s", err.Error()))
116+
}
117+
return ctrl.Result{}, nil
118+
}
119+
100120
// SetReady adds a finalizer and sets the cluster status to ready.
101121
func (r *CloudStackClusterReconciliationRunner) SetReady() (ctrl.Result, error) {
102122
controllerutil.AddFinalizer(r.ReconciliationSubject, infrav1.ClusterFinalizer)
@@ -151,10 +171,30 @@ func (r *CloudStackClusterReconciliationRunner) ReconcileDelete() (ctrl.Result,
151171
}
152172
return r.RequeueWithMessage("Child FailureDomains still present, requeueing.")
153173
}
174+
if res, err := r.DeleteUnmanagedCluster(); r.ShouldReturn(res, err) {
175+
return res, err
176+
}
154177
controllerutil.RemoveFinalizer(r.ReconciliationSubject, infrav1.ClusterFinalizer)
155178
return ctrl.Result{}, nil
156179
}
157180

181+
// DeleteUnmanagedCluster checks if an unmanaged cluster is present in Cloudstack and then deletes it.
182+
func (r *CloudStackClusterReconciliationRunner) DeleteUnmanagedCluster() (ctrl.Result, error) {
183+
// If field is present and delete fails, then requeue
184+
res, err := r.AsFailureDomainUser(&r.CSCluster.Spec.FailureDomains[0])()
185+
if r.ShouldReturn(res, err) {
186+
return res, err
187+
}
188+
err = r.CSUser.DeleteUnmanagedCluster(r.ReconciliationSubject)
189+
if err != nil {
190+
if strings.Contains(err.Error(), " not found") {
191+
return ctrl.Result{}, nil
192+
}
193+
return r.RequeueWithMessage(fmt.Sprintf("Deleting unmanaged kubernetes cluster on CloudStack failed. error: %s", err.Error()))
194+
}
195+
return ctrl.Result{}, nil
196+
}
197+
158198
// Called in main, this registers the cluster reconciler to the CAPI controller manager.
159199
func (reconciler *CloudStackClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opts controller.Options) error {
160200
log := ctrl.LoggerFrom(ctx)

controllers/cloudstackfailuredomain_controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package controllers
1818

1919
import (
2020
"context"
21+
"sort"
22+
2123
"github.com/pkg/errors"
2224
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2325
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -26,7 +28,6 @@ import (
2628
"sigs.k8s.io/controller-runtime/pkg/client"
2729
"sigs.k8s.io/controller-runtime/pkg/controller"
2830
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
29-
"sort"
3031

3132
infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3"
3233
csCtrlrUtils "sigs.k8s.io/cluster-api-provider-cloudstack/controllers/utils"

controllers/cloudstackmachine_controller.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ func (r *CloudStackMachineReconciliationRunner) Reconcile() (retRes ctrl.Result,
129129
r.RequeueIfInstanceNotRunning,
130130
r.AddToLBIfNeeded,
131131
r.GetOrCreateMachineStateChecker,
132+
r.AttachVM,
132133
)
133134
}
134135

@@ -217,6 +218,20 @@ func (r *CloudStackMachineReconciliationRunner) DeleteMachineIfFailuredomainNotE
217218
return ctrl.Result{}, nil
218219
}
219220

221+
// AttachVM adds the VM to CloudStack Unmanaged kubernetes.
222+
// No action taken if it fails
223+
func (r *CloudStackMachineReconciliationRunner) AttachVM() (retRes ctrl.Result, reterr error) {
224+
_ = r.CSUser.AddVMToUnmanagedCluster(r.CSCluster, r.ReconciliationSubject)
225+
return ctrl.Result{}, nil
226+
}
227+
228+
// RemoveVM removes the VM from CloudStack Unmanaged kubernetes.
229+
// No action taken if it fails
230+
func (r *CloudStackMachineReconciliationRunner) RemoveVM() (retRes ctrl.Result, reterr error) {
231+
_ = r.CSUser.RemoveVMFromUnmanagedCluster(r.CSCluster, r.ReconciliationSubject)
232+
return ctrl.Result{}, nil
233+
}
234+
220235
// GetOrCreateVMInstance gets or creates a VM instance.
221236
// Implicitly it also fetches its bootstrap secret in order to create said instance.
222237
func (r *CloudStackMachineReconciliationRunner) GetOrCreateVMInstance() (retRes ctrl.Result, reterr error) {
@@ -344,6 +359,10 @@ func (r *CloudStackMachineReconciliationRunner) ReconcileDelete() (retRes ctrl.R
344359
return ctrl.Result{}, err
345360
}
346361

362+
res, err := r.RemoveVM()
363+
if r.ShouldReturn(res, err) {
364+
return res, err
365+
}
347366
controllerutil.RemoveFinalizer(r.ReconciliationSubject, infrav1.MachineFinalizer)
348367
r.Log.Info("VM Deleted", "instanceID", r.ReconciliationSubject.Spec.InstanceID)
349368
return ctrl.Result{}, nil

controllers/cloudstackmachine_controller_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@ var _ = Describe("CloudStackMachineReconciler", func() {
5454
// Setup a failure domain for the machine reconciler to find.
5555
Ω(k8sClient.Create(ctx, dummies.CSFailureDomain1)).Should(Succeed())
5656
setClusterReady(k8sClient)
57+
58+
mockCloudClient.EXPECT().GetOrCreateUnmanagedCluster(gomock.Any(), gomock.Any(), gomock.Any()).Do(
59+
func(arg1, _, _ interface{}) {
60+
arg1.(*infrav1.CloudStackCluster).Status.CloudStackClusterID = "cluster-id-123"
61+
}).AnyTimes().Return(nil)
62+
63+
mockCloudClient.EXPECT().AddVMToUnmanagedCluster(
64+
gomock.Any(), gomock.Any()).AnyTimes().Return(nil)
65+
66+
mockCloudClient.EXPECT().RemoveVMFromUnmanagedCluster(
67+
gomock.Any(), gomock.Any()).AnyTimes().Return(nil)
5768
})
5869

5970
It("Should call GetOrCreateVMInstance and set Status.Ready to true", func() {
@@ -240,6 +251,8 @@ var _ = Describe("CloudStackMachineReconciler", func() {
240251
func(arg1, _, _, _, _, _ interface{}) {
241252
arg1.(*infrav1.CloudStackMachine).Status.InstanceState = "Running"
242253
}).AnyTimes()
254+
mockCloudClient.EXPECT().AddVMToUnmanagedCluster(
255+
gomock.Any(), gomock.Any()).AnyTimes().Return(nil)
243256
Ω(fakeCtrlClient.Get(ctx, key, dummies.CSCluster)).Should(Succeed())
244257
Ω(fakeCtrlClient.Create(ctx, dummies.CAPIMachine)).Should(Succeed())
245258
Ω(fakeCtrlClient.Create(ctx, dummies.CSMachine1)).Should(Succeed())

controllers/controllers_suite_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,17 @@ import (
2121
"flag"
2222
"fmt"
2323
"go/build"
24-
"k8s.io/client-go/tools/record"
2524
"os"
2625
"os/exec"
2726
"path/filepath"
2827
"regexp"
29-
"sigs.k8s.io/cluster-api-provider-cloudstack/test/fakes"
3028
"strings"
3129
"testing"
3230
"time"
3331

32+
"k8s.io/client-go/tools/record"
33+
"sigs.k8s.io/cluster-api-provider-cloudstack/test/fakes"
34+
3435
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3536
"k8s.io/klog/v2"
3637
"k8s.io/klog/v2/klogr"

go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module sigs.k8s.io/cluster-api-provider-cloudstack
33
go 1.21
44

55
require (
6-
github.com/apache/cloudstack-go/v2 v2.15.0
6+
github.com/apache/cloudstack-go/v2 v2.16.0-rc.2
77
github.com/go-logr/logr v1.2.4
88
github.com/golang/mock v1.6.0
99
github.com/hashicorp/go-multierror v1.1.1
@@ -91,3 +91,5 @@ require (
9191
)
9292

9393
replace github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.0.0 // Indirect upgrade to address https://github.com/advisories/GHSA-w73w-5m7g-f7qc
94+
95+
replace github.com/apache/cloudstack-go/v2 => github.com/shapeblue/cloudstack-go/v2 v2.9.1-0.20230717062313-73e4efc8a510

go.sum

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,11 @@ github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYr
2222
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
2323
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
2424
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
25-
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
26-
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
2725
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
2826
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
2927
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
3028
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
3129
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
32-
github.com/apache/cloudstack-go/v2 v2.15.0 h1:oojn1qx0+wBwrFSSmA2rL8XjWd4BXqwYo0RVCrAXoHk=
33-
github.com/apache/cloudstack-go/v2 v2.15.0/go.mod h1:Mc+tXpujtslBuZFk5atoGT2LanVxOrXS2GGgidAoz1A=
3430
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
3531
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
3632
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
@@ -101,7 +97,6 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
10197
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
10298
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
10399
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
104-
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
105100
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
106101
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
107102
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -223,14 +218,12 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
223218
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
224219
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
225220
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
226-
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
227221
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
228222
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
229223
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
230224
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
231225
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
232226
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
233-
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
234227
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
235228
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
236229
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@@ -279,8 +272,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
279272
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
280273
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
281274
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
282-
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
283-
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
284275
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
285276
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
286277
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
@@ -324,6 +315,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
324315
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
325316
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
326317
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
318+
github.com/shapeblue/cloudstack-go/v2 v2.9.1-0.20230717062313-73e4efc8a510 h1:FPRBv784robz6sZSqDGfZDZMse31lj96i+enH02Xzds=
319+
github.com/shapeblue/cloudstack-go/v2 v2.9.1-0.20230717062313-73e4efc8a510/go.mod h1:Mc+tXpujtslBuZFk5atoGT2LanVxOrXS2GGgidAoz1A=
327320
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
328321
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
329322
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=

pkg/cloud/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
//go:generate ../../hack/tools/bin/mockgen -destination=../mocks/mock_client.go -package=mocks sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud Client
3838

3939
type Client interface {
40+
ClusterIface
4041
VMIface
4142
NetworkIface
4243
AffinityGroupIface

0 commit comments

Comments
 (0)