Skip to content

Commit e07659c

Browse files
committed
Refine default conditions and printer columns for instances,
initial work on bringing network subnet claim orchestration up to this operator.
1 parent cfcbd77 commit e07659c

File tree

5 files changed

+333
-74
lines changed

5 files changed

+333
-74
lines changed

api/v1alpha/instance_types.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,11 @@ const (
385385
InstanceReady = "Ready"
386386
)
387387

388+
const (
389+
// InstanceReadyReasonSchedulingGatesPresent indicates that the instance is not ready because scheduling gates are present.
390+
InstanceReadyReasonSchedulingGatesPresent = "SchedulingGatesPresent"
391+
)
392+
388393
type InstanceTemplateSpec struct {
389394
// Metadata of the instances created from this template
390395
//
@@ -401,8 +406,8 @@ type InstanceTemplateSpec struct {
401406

402407
// Instance is the Schema for the instances API
403408
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
404-
// +kubebuilder:printcolumn:name="Available",type=string,JSONPath=`.status.conditions[?(@.type=="Available")].status`
405-
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.conditions[?(@.type=="Available")].reason`
409+
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
410+
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
406411
// +kubebuilder:printcolumn:name="Network IP",type=string,JSONPath=`.status.networkInterfaces[0].assignments.networkIP`,priority=1
407412
// +kubebuilder:printcolumn:name="External IP",type=string,JSONPath=`.status.networkInterfaces[0].assignments.externalIP`,priority=1
408413
type Instance struct {
@@ -414,7 +419,7 @@ type Instance struct {
414419

415420
// Status defines the current state of an Instance.
416421
//
417-
// +kubebuilder:default={conditions:{{type:"Ready",status:"Unknown",reason:"Pending", message:"Waiting for controller", lastTransitionTime: "1970-01-01T00:00:00Z"}}}
422+
// +kubebuilder:default={conditions:{{type:"Running",status:"Unknown",reason:"Pending", message:"Waiting for controller", lastTransitionTime: "1970-01-01T00:00:00Z"},{type:"Ready",status:"Unknown",reason:"Pending", message:"Waiting for controller", lastTransitionTime: "1970-01-01T00:00:00Z"}}}
418423
Status InstanceStatus `json:"status,omitempty"`
419424
}
420425

config/crd/bases/compute.datumapis.com_instances.yaml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ spec:
1818
- jsonPath: .metadata.creationTimestamp
1919
name: Age
2020
type: date
21-
- jsonPath: .status.conditions[?(@.type=="Available")].status
22-
name: Available
21+
- jsonPath: .status.conditions[?(@.type=="Ready")].status
22+
name: Ready
2323
type: string
24-
- jsonPath: .status.conditions[?(@.type=="Available")].reason
24+
- jsonPath: .status.conditions[?(@.type=="Ready")].reason
2525
name: Reason
2626
type: string
2727
- jsonPath: .status.networkInterfaces[0].assignments.networkIP
@@ -811,6 +811,11 @@ spec:
811811
status:
812812
default:
813813
conditions:
814+
- lastTransitionTime: "1970-01-01T00:00:00Z"
815+
message: Waiting for controller
816+
reason: Pending
817+
status: Unknown
818+
type: Running
814819
- lastTransitionTime: "1970-01-01T00:00:00Z"
815820
message: Waiting for controller
816821
reason: Pending

internal/controller/instance_controller.go

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ package controller
55
import (
66
"context"
77
"fmt"
8+
"strings"
89

9-
"k8s.io/apimachinery/pkg/api/equality"
1010
apierrors "k8s.io/apimachinery/pkg/api/errors"
11+
apimeta "k8s.io/apimachinery/pkg/api/meta"
1112
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1213
ctrl "sigs.k8s.io/controller-runtime"
13-
"sigs.k8s.io/controller-runtime/pkg/client"
1414
"sigs.k8s.io/controller-runtime/pkg/log"
1515
mcbuilder "sigs.k8s.io/multicluster-runtime/pkg/builder"
1616
mccontext "sigs.k8s.io/multicluster-runtime/pkg/context"
@@ -53,42 +53,28 @@ func (r *InstanceReconciler) Reconcile(ctx context.Context, req mcreconcile.Requ
5353
logger.Info("reconciling instance")
5454
defer logger.Info("reconcile complete")
5555

56-
// Ensure the instance has labels necessary for being able to identify
57-
// instances associated with a specific workload or workload deployment via
58-
// label selectors.
59-
//
60-
// This logic will not be necessary once we complete the work defined in
61-
// https://github.com/datum-cloud/enhancements/issues/28
56+
if len(instance.Spec.Controller.SchedulingGates) > 0 {
57+
// Update Ready condition to False, Reason to "SchedulingGatesPresent"
58+
// and Message to "Scheduling gates present"
6259

63-
workloadDeploymentRef := metav1.GetControllerOf(&instance)
64-
if workloadDeploymentRef == nil {
65-
return ctrl.Result{}, fmt.Errorf("failed to get controller owner of Instance")
66-
}
67-
68-
var workloadDeployment computev1alpha.WorkloadDeployment
69-
workloadDeploymentObjectKey := client.ObjectKey{
70-
Namespace: instance.Namespace,
71-
Name: workloadDeploymentRef.Name,
72-
}
73-
if err := cl.GetClient().Get(ctx, workloadDeploymentObjectKey, &workloadDeployment); err != nil {
74-
return ctrl.Result{}, err
75-
}
76-
77-
workloadRef := metav1.GetControllerOf(&workloadDeployment)
78-
if workloadRef == nil {
79-
return ctrl.Result{}, fmt.Errorf("failed to get controller owner of WorkloadDeployment")
80-
}
81-
82-
updated := instance.DeepCopy()
83-
if updated.Labels == nil {
84-
updated.Labels = map[string]string{}
85-
}
86-
updated.Labels[computev1alpha.WorkloadUIDLabel] = string(workloadRef.UID)
87-
updated.Labels[computev1alpha.WorkloadDeploymentUIDLabel] = string(workloadDeploymentRef.UID)
60+
// Collect a list of scheduling gate names
61+
var schedulingGateNames []string
62+
for _, gate := range instance.Spec.Controller.SchedulingGates {
63+
schedulingGateNames = append(schedulingGateNames, gate.Name)
64+
}
8865

89-
if !equality.Semantic.DeepEqual(updated, instance) {
90-
if err := cl.GetClient().Update(ctx, updated); err != nil {
91-
return ctrl.Result{}, fmt.Errorf("failed updating instance: %w", err)
66+
changed := apimeta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{
67+
Type: computev1alpha.InstanceReady,
68+
Status: metav1.ConditionFalse,
69+
Reason: computev1alpha.InstanceReadyReasonSchedulingGatesPresent,
70+
Message: fmt.Sprintf("Scheduling gates present: %s", strings.Join(schedulingGateNames, ", ")),
71+
ObservedGeneration: instance.Generation,
72+
})
73+
74+
if changed {
75+
if err := cl.GetClient().Status().Update(ctx, &instance); err != nil {
76+
return ctrl.Result{}, fmt.Errorf("failed updating instance status: %w", err)
77+
}
9278
}
9379
}
9480

internal/controller/result.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package controller
2+
3+
import (
4+
"context"
5+
"errors"
6+
7+
apierrors "k8s.io/apimachinery/pkg/api/errors"
8+
ctrl "sigs.k8s.io/controller-runtime"
9+
"sigs.k8s.io/controller-runtime/pkg/client"
10+
)
11+
12+
type Result struct {
13+
// Result contains the result of a Reconciler invocation.
14+
ctrl.Result
15+
16+
// Err contains an error of a Reconciler invocation
17+
Err error
18+
19+
// StopProcessing indicates that the caller should not continue processing and
20+
// let the Reconciler go to sleep without an explicit requeue, expecting a
21+
// Watch to trigger a future reconciliation call.
22+
StopProcessing bool
23+
24+
syncStatus map[client.Object]client.Client
25+
}
26+
27+
func (r *Result) Merge(other Result) Result {
28+
if other.Err != nil {
29+
r.Err = errors.Join(r.Err, other.Err)
30+
}
31+
if other.Result != (ctrl.Result{}) {
32+
r.Result = other.Result
33+
}
34+
if other.StopProcessing {
35+
r.StopProcessing = true
36+
}
37+
if other.syncStatus != nil {
38+
if r.syncStatus == nil {
39+
r.syncStatus = make(map[client.Object]client.Client)
40+
}
41+
for k, v := range other.syncStatus {
42+
r.syncStatus[k] = v
43+
}
44+
}
45+
46+
return *r
47+
}
48+
49+
func (r *Result) AddStatusUpdate(c client.Client, obj client.Object) {
50+
if r.syncStatus == nil {
51+
r.syncStatus = make(map[client.Object]client.Client)
52+
}
53+
r.syncStatus[obj] = c
54+
}
55+
56+
func (r Result) ShouldReturn() bool {
57+
return r.Err != nil || !r.Result.IsZero() || r.StopProcessing
58+
}
59+
60+
func (r Result) Complete(ctx context.Context) (ctrl.Result, error) {
61+
if r.syncStatus != nil {
62+
var errs []error
63+
for obj, client := range r.syncStatus {
64+
if err := client.Status().Update(ctx, obj); err != nil {
65+
if r.Err == nil && apierrors.IsConflict(err) {
66+
r.Requeue = true
67+
} else {
68+
errs = append(errs, err)
69+
}
70+
}
71+
}
72+
73+
if len(errs) > 0 {
74+
r.Err = errors.Join(append([]error{r.Err}, errs...)...)
75+
}
76+
}
77+
78+
return r.Result, r.Err
79+
}

0 commit comments

Comments
 (0)