Skip to content

Commit 5a869c6

Browse files
authored
Merge pull request #24 from weaveworks/allow-non-ready-controlplane
Add support for detecting CAPI cluster provisioned.
2 parents 22f6c6e + ec5fa3d commit 5a869c6

File tree

4 files changed

+106
-20
lines changed

4 files changed

+106
-20
lines changed

api/v1alpha1/condition_types.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,16 @@ const (
2323
// CAPINotEnabled signals that CAPI component is not installed.
2424
CAPINotEnabled string = "CAPINotEnabled"
2525
)
26+
27+
const (
28+
// ClusterProvisionedCondition is a condition when CAPI clusters are
29+
// provisioned.
30+
//
31+
// This indicates that the cluster has been bootstrapped, but the
32+
// control-plane may not yet be ready.
33+
ClusterProvisionedCondition string = "ClusterProvisioned"
34+
35+
// ClusterProvisionedReason is the reason for the provisioned state being
36+
// set.
37+
ClusterProvisionedReason string = "ClusterProvisioned"
38+
)

controllers/gitopscluster_controller.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/fluxcd/pkg/runtime/conditions"
2727
corev1 "k8s.io/api/core/v1"
2828
apierrors "k8s.io/apimachinery/pkg/api/errors"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2930
"k8s.io/apimachinery/pkg/runtime"
3031
"k8s.io/apimachinery/pkg/types"
3132
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -43,6 +44,10 @@ import (
4344
// finalize a GitOps cluster.
4445
const GitOpsClusterFinalizer = "clusters.gitops.weave.works"
4546

47+
// GitOpsClusterProvisionedAnnotation if applied to a GitOpsCluster indicates
48+
// that it should have a ready Provisioned condition.
49+
const GitOpsClusterProvisionedAnnotation = "clusters.gitops.weave.works/provisioned"
50+
4651
const (
4752
// SecretNameIndexKey is the key used for indexing secret
4853
// resources based on their name.
@@ -145,6 +150,11 @@ func (r *GitopsClusterReconciler) Reconcile(ctx context.Context, req ctrl.Reques
145150
Namespace: cluster.GetNamespace(),
146151
Name: cluster.Spec.SecretRef.Name,
147152
}
153+
154+
if metav1.HasAnnotation(cluster.ObjectMeta, GitOpsClusterProvisionedAnnotation) {
155+
conditions.MarkTrue(cluster, gitopsv1alpha1.ClusterProvisionedCondition, gitopsv1alpha1.ClusterProvisionedReason, "Cluster Provisioned annotation detected")
156+
}
157+
148158
var secret corev1.Secret
149159
if err := r.Get(ctx, name, &secret); err != nil {
150160
e := fmt.Errorf("failed to get secret %q: %w", name, err)
@@ -196,8 +206,17 @@ func (r *GitopsClusterReconciler) Reconcile(ctx context.Context, req ctrl.Reques
196206

197207
log.Info("CAPI Cluster found", "CAPI cluster", name)
198208

209+
updated := false
199210
if !capiCluster.Status.ControlPlaneReady {
200211
conditions.MarkFalse(cluster, meta.ReadyCondition, gitopsv1alpha1.WaitingForControlPlaneReadyStatusReason, "Waiting for ControlPlaneReady status")
212+
updated = true
213+
}
214+
if clusterv1.ClusterPhase(capiCluster.Status.Phase) == clusterv1.ClusterPhaseProvisioned {
215+
conditions.MarkTrue(cluster, gitopsv1alpha1.ClusterProvisionedCondition, gitopsv1alpha1.ClusterProvisionedReason, "CAPI Cluster has been provisioned")
216+
updated = true
217+
}
218+
219+
if updated {
201220
if err := r.Status().Update(ctx, cluster); err != nil {
202221
log.Error(err, "failed to update Cluster status")
203222
return ctrl.Result{}, err

controllers/gitopscluster_controller_test.go

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func TestReconcile(t *testing.T) {
3737
opts controllers.Options
3838
requeueAfter time.Duration
3939
errString string
40+
wantCondition string
4041
wantStatus metav1.ConditionStatus
4142
wantStatusMessage string
4243
}{
@@ -54,6 +55,7 @@ func TestReconcile(t *testing.T) {
5455
CAPIEnabled: true,
5556
},
5657
requeueAfter: controllers.MissingSecretRequeueTime,
58+
wantCondition: meta.ReadyCondition,
5759
wantStatus: "False",
5860
wantStatusMessage: "failed to get secret \"testing/missing\": secrets \"missing\" not found",
5961
},
@@ -74,7 +76,28 @@ func TestReconcile(t *testing.T) {
7476
opts: controllers.Options{
7577
CAPIEnabled: true,
7678
},
77-
wantStatus: "True",
79+
wantCondition: meta.ReadyCondition,
80+
wantStatus: "True",
81+
},
82+
{
83+
name: "non-CAPI cluster has provisioned annotation",
84+
state: []runtime.Object{
85+
makeTestCluster(func(c *gitopsv1alpha1.GitopsCluster) {
86+
c.ObjectMeta.Annotations = map[string]string{
87+
controllers.GitOpsClusterProvisionedAnnotation: "true",
88+
}
89+
c.Spec.SecretRef = &meta.LocalObjectReference{
90+
Name: "dev",
91+
}
92+
}),
93+
},
94+
obj: types.NamespacedName{Namespace: testNamespace, Name: testName},
95+
// The referenced secret doesn't exist so we should still check for
96+
// it.
97+
requeueAfter: controllers.MissingSecretRequeueTime,
98+
wantCondition: gitopsv1alpha1.ClusterProvisionedCondition,
99+
wantStatus: "True",
100+
wantStatusMessage: "Cluster Provisioned annotation detected",
78101
},
79102
{
80103
name: "CAPI cluster does not exist",
@@ -90,6 +113,7 @@ func TestReconcile(t *testing.T) {
90113
CAPIEnabled: true,
91114
},
92115
errString: "failed to get CAPI cluster.*missing.*not found",
116+
wantCondition: meta.ReadyCondition,
93117
wantStatus: "False",
94118
wantStatusMessage: "failed to get CAPI cluster \"testing/missing\": clusters.cluster.x-k8s.io \"missing\" not found",
95119
},
@@ -110,9 +134,33 @@ func TestReconcile(t *testing.T) {
110134
opts: controllers.Options{
111135
CAPIEnabled: true,
112136
},
137+
wantCondition: meta.ReadyCondition,
113138
wantStatus: "False",
114139
wantStatusMessage: "Waiting for ControlPlaneReady status",
115140
},
141+
{
142+
name: "CAPI cluster exists and is provisioned",
143+
state: []runtime.Object{
144+
makeTestCluster(func(c *gitopsv1alpha1.GitopsCluster) {
145+
c.Spec.CAPIClusterRef = &meta.LocalObjectReference{
146+
Name: "dev",
147+
}
148+
}),
149+
makeTestCAPICluster(types.NamespacedName{
150+
Name: "dev",
151+
Namespace: testNamespace,
152+
}, func(c *clusterv1.Cluster) {
153+
c.Status.SetTypedPhase(clusterv1.ClusterPhaseProvisioned)
154+
}),
155+
},
156+
obj: types.NamespacedName{Namespace: testNamespace, Name: testName},
157+
opts: controllers.Options{
158+
CAPIEnabled: true,
159+
},
160+
wantCondition: gitopsv1alpha1.ClusterProvisionedCondition,
161+
wantStatus: "True",
162+
wantStatusMessage: "CAPI Cluster has been provisioned",
163+
},
116164
{
117165
name: "CAPI cluster exists and is ready",
118166
state: []runtime.Object{
@@ -132,10 +180,11 @@ func TestReconcile(t *testing.T) {
132180
opts: controllers.Options{
133181
CAPIEnabled: true,
134182
},
135-
wantStatus: "True",
183+
wantCondition: meta.ReadyCondition,
184+
wantStatus: "True",
136185
},
137186
{
138-
name: "CAPI compnent is not enabled",
187+
name: "CAPI component is not enabled",
139188
state: []runtime.Object{
140189
makeTestCluster(func(c *gitopsv1alpha1.GitopsCluster) {
141190
c.Spec.CAPIClusterRef = &meta.LocalObjectReference{
@@ -147,6 +196,7 @@ func TestReconcile(t *testing.T) {
147196
opts: controllers.Options{
148197
CAPIEnabled: false,
149198
},
199+
wantCondition: meta.ReadyCondition,
150200
wantStatus: "False",
151201
wantStatusMessage: "CAPIClusterRef \"dev\" found but CAPI support is disabled",
152202
},
@@ -168,19 +218,7 @@ func TestReconcile(t *testing.T) {
168218

169219
clsObjectKey := types.NamespacedName{Namespace: testNamespace, Name: testName}
170220
cls := testGetGitopsCluster(t, r.Client, clsObjectKey)
171-
cond := conditions.Get(cls, meta.ReadyCondition)
172-
173-
if cond == nil {
174-
t.Fatalf("Ready condition was nil")
175-
}
176-
177-
if cond.Message != tt.wantStatusMessage {
178-
t.Fatalf("got condition reason %q, want %q", cond.Message, tt.wantStatusMessage)
179-
}
180-
181-
if cond.Status != tt.wantStatus {
182-
t.Fatalf("got condition status %q, want %q", cond.Status, tt.wantStatus)
183-
}
221+
assertClusterStatus(t, cls, tt.wantCondition, tt.wantStatus, tt.wantStatusMessage)
184222
})
185223
}
186224
}
@@ -478,3 +516,22 @@ func testGetGitopsCluster(t *testing.T, c client.Client, k client.ObjectKey) *gi
478516
}
479517
return &cluster
480518
}
519+
520+
func assertClusterStatus(t *testing.T, cls *gitopsv1alpha1.GitopsCluster, condType string, status metav1.ConditionStatus, msg string) {
521+
t.Helper()
522+
cond := conditions.Get(cls, condType)
523+
if status == "" {
524+
return
525+
}
526+
527+
if cond == nil && status != "" {
528+
t.Fatalf("%s condition was nil", condType)
529+
}
530+
531+
if cond.Status != status {
532+
t.Fatalf("got condition status %q, want %q", cond.Status, status)
533+
}
534+
if cond.Message != msg {
535+
t.Fatalf("got condition reason %q, want %q", cond.Message, msg)
536+
}
537+
}

controllers/suite_test.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"k8s.io/client-go/kubernetes/scheme"
2626
"sigs.k8s.io/controller-runtime/pkg/client"
2727
"sigs.k8s.io/controller-runtime/pkg/envtest"
28-
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
2928
logf "sigs.k8s.io/controller-runtime/pkg/log"
3029
"sigs.k8s.io/controller-runtime/pkg/log/zap"
3130

@@ -42,9 +41,7 @@ var testEnv *envtest.Environment
4241
func TestAPIs(t *testing.T) {
4342
RegisterFailHandler(Fail)
4443

45-
RunSpecsWithDefaultAndCustomReporters(t,
46-
"Controller Suite",
47-
[]Reporter{printer.NewlineReporter{}})
44+
RunSpecs(t, "Controller Suite")
4845
}
4946

5047
var _ = BeforeSuite(func() {

0 commit comments

Comments
 (0)