Skip to content

Commit b3dd8d6

Browse files
committed
fix for issue # 119
1 parent e568f6c commit b3dd8d6

File tree

3 files changed

+93
-2
lines changed

3 files changed

+93
-2
lines changed

api/v1alpha1/sandbox_types.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,38 @@ type SandboxSpec struct {
119119
Replicas *int32 `json:"replicas,omitempty"`
120120
}
121121

122+
// SandboxPhase is a label for the condition of a sandbox at the current time.
123+
type SandboxPhase string
124+
125+
// These are the valid phases of a sandbox.
126+
const (
127+
// SandboxPhasePending means the sandbox has been accepted by the system, but one or more of the components
128+
// has not been created. This includes time before pods have been scheduled as well as time spent
129+
// pulling images over the network, which can take a while.
130+
SandboxPhasePending SandboxPhase = "Pending"
131+
// SandboxPhaseRunning means the sandbox has been bound to a node and all of the components have been started.
132+
// At least one container is still running or is in the process of being restarted.
133+
SandboxPhaseRunning SandboxPhase = "Running"
134+
// SandboxPhasePaused means the sandbox has been paused.
135+
SandboxPhasePaused SandboxPhase = "Paused"
136+
// SandboxPhaseFailed means that all components in the sandbox have terminated, and at least one component has
137+
// terminated in a failure (exited with a non-zero exit code or was stopped by the system).
138+
SandboxPhaseFailed SandboxPhase = "Failed"
139+
// SandboxPhaseUnknown means that for reasons outside the system's control, the state of the sandbox could not be obtained,
140+
// typically due to an error in communicating with the host of the sandbox.
141+
SandboxPhaseUnknown SandboxPhase = "Unknown"
142+
)
143+
122144
// SandboxStatus defines the observed state of Sandbox.
123145
type SandboxStatus struct {
146+
// The phase of a Sandbox is a simple, high-level summary of where the Sandbox is in its lifecycle.
147+
// The conditions array, the reason and message fields, and the individual container status arrays contain
148+
// more detail about the pod's status.
149+
// If specified, must be one of the following values:
150+
// Pending, Running, Paused, Failed, Unknown
151+
// +optional
152+
Phase SandboxPhase `json:"phase,omitempty"`
153+
124154
// FQDN that is valid for default cluster settings
125155
// Limitation: Hardcoded to the domain .cluster.local
126156
// e.g. sandbox-example.default.svc.cluster.local

controllers/sandbox_controller.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ func (r *SandboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
107107
if expired {
108108
log.Info("Sandbox has expired, deleting pod and service")
109109
err = r.handleSandboxExpiry(ctx, sandbox)
110+
} else if *sandbox.Spec.Replicas == 0 {
111+
log.Info("Sandbox is paused")
112+
sandbox.Status.Phase = sandboxv1alpha1.SandboxPhasePaused
113+
err = r.reconcileChildResources(ctx, sandbox)
110114
} else {
111115
err = r.reconcileChildResources(ctx, sandbox)
112116
}
@@ -135,9 +139,11 @@ func (r *SandboxReconciler) reconcileChildResources(ctx context.Context, sandbox
135139
pod, err := r.reconcilePod(ctx, sandbox, nameHash)
136140
allErrors = errors.Join(allErrors, err)
137141
if pod == nil {
142+
sandbox.Status.Phase = sandboxv1alpha1.SandboxPhasePending
138143
sandbox.Status.Replicas = 0
139144
sandbox.Status.LabelSelector = ""
140145
} else {
146+
sandbox.Status.Phase = sandboxv1alpha1.SandboxPhase(pod.Status.Phase)
141147
sandbox.Status.Replicas = 1
142148
sandbox.Status.LabelSelector = fmt.Sprintf("%s=%s", sandboxLabel, NameHash(sandbox.Name))
143149
}
@@ -431,6 +437,7 @@ func (r *SandboxReconciler) handleSandboxExpiry(ctx context.Context, sandbox *sa
431437
Message: "Sandbox has expired",
432438
})
433439

440+
sandbox.Status.Phase = sandboxv1alpha1.SandboxPhaseFailed
434441
sandbox.Status.Replicas = 0
435442
sandbox.Status.LabelSelector = ""
436443

controllers/sandbox_controller_test.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ func TestReconcile(t *testing.T) {
227227
},
228228
// Verify Sandbox status
229229
wantStatus: sandboxv1alpha1.SandboxStatus{
230+
Phase: sandboxv1alpha1.SandboxPhasePending,
230231
Service: sandboxName,
231232
ServiceFQDN: "sandbox-name.sandbox-ns.svc.cluster.local",
232233
Replicas: 1,
@@ -237,7 +238,7 @@ func TestReconcile(t *testing.T) {
237238
Status: "False",
238239
ObservedGeneration: 1,
239240
Reason: "DependenciesNotReady",
240-
Message: "Pod exists with phase: ; Service Exists",
241+
Message: "Pod does not exist; Service Exists",
241242
},
242243
},
243244
},
@@ -320,6 +321,7 @@ func TestReconcile(t *testing.T) {
320321
},
321322
// Verify Sandbox status
322323
wantStatus: sandboxv1alpha1.SandboxStatus{
324+
Phase: sandboxv1alpha1.SandboxPhasePending,
323325
Service: sandboxName,
324326
ServiceFQDN: "sandbox-name.sandbox-ns.svc.cluster.local",
325327
Replicas: 1,
@@ -330,7 +332,7 @@ func TestReconcile(t *testing.T) {
330332
Status: "False",
331333
ObservedGeneration: 1,
332334
Reason: "DependenciesNotReady",
333-
Message: "Pod exists with phase: ; Service Exists",
335+
Message: "Pod does not exist; Service Exists",
334336
},
335337
},
336338
},
@@ -406,6 +408,58 @@ func TestReconcile(t *testing.T) {
406408
},
407409
},
408410
},
411+
{
412+
name: "sandbox is paused",
413+
// Input sandbox spec
414+
sandboxSpec: sandboxv1alpha1.SandboxSpec{
415+
Replicas: ptr.To(int32(0)),
416+
PodTemplate: sandboxv1alpha1.PodTemplate{
417+
Spec: corev1.PodSpec{
418+
Containers: []corev1.Container{
419+
{
420+
Name: "test-container",
421+
},
422+
},
423+
},
424+
},
425+
},
426+
// Verify Sandbox status
427+
wantStatus: sandboxv1alpha1.SandboxStatus{
428+
Phase: sandboxv1alpha1.SandboxPhasePaused,
429+
Service: sandboxName,
430+
ServiceFQDN: "sandbox-name.sandbox-ns.svc.cluster.local",
431+
Replicas: 0,
432+
Conditions: []metav1.Condition{
433+
{
434+
Type: "Ready",
435+
Status: "True",
436+
ObservedGeneration: 1,
437+
Reason: "DependenciesReady",
438+
Message: "Pod does not exist, replicas is 0; Service Exists",
439+
},
440+
},
441+
},
442+
wantObjs: []client.Object{
443+
// Verify Service
444+
&corev1.Service{
445+
ObjectMeta: metav1.ObjectMeta{
446+
Name: sandboxName,
447+
Namespace: sandboxNs,
448+
ResourceVersion: "1",
449+
Labels: map[string]string{
450+
"agents.x-k8s.io/sandbox-name-hash": "ab179450",
451+
},
452+
OwnerReferences: []metav1.OwnerReference{sandboxControllerRef(sandboxName)},
453+
},
454+
Spec: corev1.ServiceSpec{
455+
Selector: map[string]string{
456+
"agents.x-k8s.io/sandbox-name-hash": "ab179450",
457+
},
458+
ClusterIP: "None",
459+
},
460+
},
461+
},
462+
},
409463
}
410464

411465
for _, tc := range testCases {

0 commit comments

Comments
 (0)