Skip to content

Commit bfcba44

Browse files
committed
chore: update .gitignore and modify ControlPlaneConfig to make Region optional; adjust WorkspaceTemplateApply and controller setup for improved handling of workspace status and event filtering
1 parent b5808ac commit bfcba44

27 files changed

+1951
-24
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,4 @@ refs
4646
.specstory
4747
.cursorindexingignore
4848
work/
49+
cc-kubeconfig

api/controlplane/v1beta1/captcontrolplane_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ type TimeoutConfig struct {
111111
// ControlPlaneConfig contains EKS-specific configuration
112112
type ControlPlaneConfig struct {
113113
// Region specifies the AWS region where the control plane will be created
114-
// +kubebuilder:validation:Required
114+
// +optional
115115
// +kubebuilder:validation:Pattern="^[a-z]{2}-[a-z]+-[0-9]$"
116-
Region string `json:"region"`
116+
Region string `json:"region,omitempty"`
117117

118118
// EndpointAccess defines the access configuration for the API server endpoint
119119
// +optional

config/clusterapi/controlplane/bases/controlplane.cluster.x-k8s.io_captcontrolplanes.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,6 @@ spec:
128128
minimum: 1
129129
type: integer
130130
type: object
131-
required:
132-
- region
133131
type: object
134132
controlPlaneEndpoint:
135133
description: ControlPlaneEndpoint represents the endpoint used to

internal/controller/captcluster/controller.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import (
66

77
"k8s.io/apimachinery/pkg/runtime"
88
ctrl "sigs.k8s.io/controller-runtime"
9+
"sigs.k8s.io/controller-runtime/pkg/builder"
910
"sigs.k8s.io/controller-runtime/pkg/client"
1011
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1112
"sigs.k8s.io/controller-runtime/pkg/handler"
1213
"sigs.k8s.io/controller-runtime/pkg/log"
14+
"sigs.k8s.io/controller-runtime/pkg/predicate"
1315
"sigs.k8s.io/controller-runtime/pkg/reconcile"
1416

1517
infrastructurev1beta1 "github.com/appthrust/capt/api/v1beta1"
@@ -255,7 +257,7 @@ func (r *Reconciler) cleanupWorkspaceTemplateApply(ctx context.Context, captClus
255257
// SetupWithManager sets up the controller with the Manager.
256258
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
257259
return ctrl.NewControllerManagedBy(mgr).
258-
For(&infrastructurev1beta1.CAPTCluster{}).
260+
For(&infrastructurev1beta1.CAPTCluster{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
259261
Owns(&infrastructurev1beta1.WorkspaceTemplateApply{}).
260262
// Watch Cluster and enqueue all CAPTClusters that belong to it (label match)
261263
Watches(
@@ -273,5 +275,6 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
273275
return reqs
274276
}),
275277
).
278+
WithEventFilter(predicate.GenerationChangedPredicate{}).
276279
Complete(r)
277280
}

internal/controller/captcluster/vpc.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ func (r *Reconciler) getOrCreateWorkspaceTemplateApply(ctx context.Context, capt
147147
// Get VPC name
148148
vpcName := r.getVPCName(captCluster)
149149

150+
// Derive cluster_name for templates: prefer owner CAPI Cluster name when available
151+
clusterNameVar := captCluster.Name
152+
if lbl := captCluster.Labels[clusterv1.ClusterNameLabel]; lbl != "" {
153+
clusterNameVar = lbl
154+
}
155+
150156
// Try to find existing WorkspaceTemplateApply
151157
workspaceApply := &infrastructurev1beta1.WorkspaceTemplateApply{}
152158
err := r.Get(ctx, types.NamespacedName{Name: applyName, Namespace: captCluster.Namespace}, workspaceApply)
@@ -161,7 +167,7 @@ func (r *Reconciler) getOrCreateWorkspaceTemplateApply(ctx context.Context, capt
161167
desiredSpec := infrastructurev1beta1.WorkspaceTemplateApplySpec{
162168
TemplateRef: *captCluster.Spec.VPCTemplateRef,
163169
Variables: map[string]string{
164-
"cluster_name": captCluster.Name,
170+
"cluster_name": clusterNameVar,
165171
"vpc_name": vpcName,
166172
// Some templates expect a generic "name" variable
167173
"name": vpcName,
@@ -213,7 +219,7 @@ func (r *Reconciler) getOrCreateWorkspaceTemplateApply(ctx context.Context, capt
213219
TemplateRef: *captCluster.Spec.VPCTemplateRef,
214220
Variables: func() map[string]string {
215221
vars := map[string]string{
216-
"cluster_name": captCluster.Name,
222+
"cluster_name": clusterNameVar,
217223
"vpc_name": vpcName,
218224
// Some templates expect a generic "name" variable
219225
"name": vpcName,

internal/controller/controlplane/setup.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ package controlplane
33
import (
44
controlplanev1beta1 "github.com/appthrust/capt/api/controlplane/v1beta1"
55
ctrl "sigs.k8s.io/controller-runtime"
6+
"sigs.k8s.io/controller-runtime/pkg/builder"
7+
"sigs.k8s.io/controller-runtime/pkg/predicate"
68
)
79

810
// SetupWithManager sets up the controller with the Manager.
911
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
1012
return ctrl.NewControllerManagedBy(mgr).
11-
For(&controlplanev1beta1.CAPTControlPlane{}).
13+
For(&controlplanev1beta1.CAPTControlPlane{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
14+
WithEventFilter(predicate.GenerationChangedPredicate{}).
1215
Complete(r)
1316
}

internal/controller/controlplane/workspace.go

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package controlplane
33
import (
44
"context"
55
"fmt"
6+
"strings"
67

78
controlplanev1beta1 "github.com/appthrust/capt/api/controlplane/v1beta1"
89
infrastructurev1beta1 "github.com/appthrust/capt/api/v1beta1"
@@ -12,6 +13,7 @@ import (
1213
"k8s.io/apimachinery/pkg/types"
1314
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
1415
ctrl "sigs.k8s.io/controller-runtime"
16+
"sigs.k8s.io/controller-runtime/pkg/client"
1517
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1618
)
1719

@@ -134,24 +136,50 @@ func (r *Reconciler) generateWorkspaceTemplateApplySpec(controlPlane *controlpla
134136
}
135137

136138
// Add VPC workspace dependency: use owner cluster name if available
137-
vpcOwnerName := controlPlane.Name
138-
if name, ok := controlPlane.Labels[clusterv1.ClusterNameLabel]; ok && name != "" {
139-
vpcOwnerName = name
140-
}
141-
vpcWorkspaceApplyName := fmt.Sprintf("%s-vpc", vpcOwnerName)
142-
vpcWorkspaceApply := &infrastructurev1beta1.WorkspaceTemplateApply{}
143-
err := r.Get(context.Background(), types.NamespacedName{
144-
Name: vpcWorkspaceApplyName,
145-
Namespace: controlPlane.Namespace,
146-
}, vpcWorkspaceApply)
147-
if err == nil {
148-
spec.WaitForWorkspaces = []infrastructurev1beta1.WorkspaceReference{
149-
{
150-
Name: vpcWorkspaceApplyName,
151-
Namespace: controlPlane.Namespace,
152-
},
139+
// Determine associated CAPTCluster to derive the actual VPC WorkspaceTemplateApply name
140+
clusterName := controlPlane.Labels[clusterv1.ClusterNameLabel]
141+
// Prefer owner cluster name for cluster_name variable when available
142+
if clusterName != "" {
143+
spec.Variables["cluster_name"] = clusterName
144+
}
145+
vpcWorkspaceApplyName := ""
146+
if clusterName != "" {
147+
// List CAPTClusters owned by this cluster
148+
captClusters := &infrastructurev1beta1.CAPTClusterList{}
149+
if err := r.List(context.Background(), captClusters, client.InNamespace(controlPlane.Namespace), client.MatchingLabels{clusterv1.ClusterNameLabel: clusterName}); err == nil {
150+
if len(captClusters.Items) > 0 {
151+
cc := captClusters.Items[0]
152+
if cc.Spec.WorkspaceTemplateApplyName != "" {
153+
vpcWorkspaceApplyName = cc.Spec.WorkspaceTemplateApplyName
154+
} else {
155+
vpcWorkspaceApplyName = fmt.Sprintf("%s-vpc", cc.Name)
156+
}
157+
}
158+
}
159+
}
160+
if vpcWorkspaceApplyName == "" {
161+
// Fallback to controlPlane-based naming (best-effort)
162+
owner := controlPlane.Name
163+
if clusterName != "" {
164+
owner = clusterName
153165
}
166+
vpcWorkspaceApplyName = fmt.Sprintf("%s-vpc", owner)
154167
}
155168

169+
// Wait for the VPC Workspace to be ready (its name equals applyName without "-apply")
170+
vpcWorkspaceName := strings.TrimSuffix(vpcWorkspaceApplyName, "-apply")
171+
spec.WaitForWorkspaces = []infrastructurev1beta1.WorkspaceReference{{
172+
Name: vpcWorkspaceName,
173+
Namespace: controlPlane.Namespace,
174+
}}
175+
176+
// Provide VPC workspace name to the template and wait for its connection secret as a required secret
177+
spec.Variables["vpc_workspace_name"] = vpcWorkspaceName
178+
vpcSecretName := fmt.Sprintf("%s-vpc-connection", vpcWorkspaceName)
179+
spec.WaitForSecrets = append(spec.WaitForSecrets, xpv1.SecretReference{
180+
Name: vpcSecretName,
181+
Namespace: controlPlane.Namespace,
182+
})
183+
156184
return spec
157185
}

internal/controller/workspacetemplateapply_controller.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,17 @@ func (r *workspaceTemplateApplyReconciler) reconcileWorkspaceStatus(ctx context.
378378
Name: cr.Status.WorkspaceName,
379379
Namespace: cr.Namespace,
380380
}, workspace); err != nil {
381+
// If the Workspace was deleted or not found, mark as not applied so that
382+
// the create path runs again on the next reconciliation.
383+
if apierrors.IsNotFound(err) {
384+
r.log.Debug(errGetWorkspace+": will recreate", "error", err)
385+
cr.Status.WorkspaceName = ""
386+
cr.Status.Applied = false
387+
if uerr := r.client.Status().Update(ctx, cr); uerr != nil {
388+
return ctrl.Result{}, uerr
389+
}
390+
return ctrl.Result{RequeueAfter: requeueAfterStatus}, nil
391+
}
381392
r.log.Debug(errGetWorkspace, "error", err)
382393
return ctrl.Result{}, err
383394
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apiVersion: cluster.x-k8s.io/v1beta1
2+
kind: Cluster
3+
metadata:
4+
name: bc2
5+
namespace: mgmt
6+
annotations:
7+
installation.appthrust.com/aws-primary-region: ap-northeast-1
8+
managed-by: "appthrust"
9+
labels:
10+
installation.appthrust.com/aws-load-balancer-controller: enabled
11+
installation.appthrust.com/eks-karpenter: enabled
12+
installation.appthrust.com/karpenter-provider-aws-default-nodepool: enabled
13+
installation.appthrust.com/grafana-k8s-monitoring: enabled
14+
spec:
15+
topology:
16+
class: capt-eks-vpc
17+
version: "1.30.0"
18+
variables:
19+
- name: region
20+
value: "ap-northeast-1"
21+
- name: tags
22+
value:
23+
Environment: "dev"
24+
ManagedBy: "capt"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
apiVersion: cluster.x-k8s.io/v1beta1
2+
kind: Cluster
3+
metadata:
4+
name: bc1
5+
namespace: mgmt
6+
annotations:
7+
installation.appthrust.com/aws-primary-region: ap-northeast-1
8+
managed-by: "appthrust"
9+
labels:
10+
installation.appthrust.com/aws-load-balancer-controller: enabled
11+
installation.appthrust.com/eks-karpenter: enabled
12+
installation.appthrust.com/karpenter-provider-aws-default-nodepool: enabled
13+
installation.appthrust.com/grafana-k8s-monitoring: enabled
14+
spec:
15+
topology:
16+
class: capt-eks-vpc
17+
version: "1.30.0"
18+
variables:
19+
- name: region
20+
value: "ap-northeast-1"
21+
- name: tags
22+
value:
23+
Environment: "dev"
24+
ManagedBy: "capt"
25+
26+

0 commit comments

Comments
 (0)