Skip to content

Commit 8f644a0

Browse files
authored
[module-sdk] fix status apply (#46)
Signed-off-by: Pavel Okhlopkov <[email protected]>
1 parent 82f31d4 commit 8f644a0

File tree

3 files changed

+63
-57
lines changed

3 files changed

+63
-57
lines changed

examples/single-file-example/hooks/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ func HandlerHook(_ context.Context, input *pkg.HookInput) error {
5353
return nil
5454
}
5555

56-
func ReadinessFunc(_ context.Context, input *pkg.HookInput) error {
56+
func ReadinessFunc(ctx context.Context, input *pkg.HookInput) error {
5757
input.Logger.Info("start user logic for readiness probe")
5858

5959
c := input.DC.GetHTTPClient()
6060

61-
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1/readyz", nil)
61+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://127.0.0.1/readyz", nil)
6262
if err != nil {
6363
return fmt.Errorf("create request: %w", err)
6464
}

internal/common-hooks/readiness/hook.go

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package readiness
1818

1919
import (
2020
"context"
21+
"encoding/json"
2122
"errors"
2223
"fmt"
2324
"log/slog"
@@ -26,6 +27,7 @@ import (
2627
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2728
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2829
"k8s.io/apimachinery/pkg/runtime/schema"
30+
"k8s.io/apimachinery/pkg/types"
2931

3032
"github.com/deckhouse/module-sdk/pkg"
3133
)
@@ -72,6 +74,7 @@ const (
7274
conditionStatusIsReady = "IsReady"
7375
modulePhaseReconciling = "Reconciling"
7476
modulePhaseReady = "Ready"
77+
modulePhaseHookError = "Error"
7578
)
7679

7780
func CheckModuleReadiness(cfg *ReadinessHookConfig) func(ctx context.Context, input *pkg.HookInput) error {
@@ -128,7 +131,7 @@ func CheckModuleReadiness(cfg *ReadinessHookConfig) func(ctx context.Context, in
128131
return errors.New("can't find status.phase")
129132
}
130133

131-
if phase != modulePhaseReconciling && phase != modulePhaseReady {
134+
if phase != modulePhaseReconciling && phase != modulePhaseReady && phase != modulePhaseHookError {
132135
logger.Debug("waiting for sustainable phase", slog.String("phase", phase))
133136

134137
return nil
@@ -164,44 +167,45 @@ func CheckModuleReadiness(cfg *ReadinessHookConfig) func(ctx context.Context, in
164167
condIdx = len(uConditions) - 1
165168
}
166169

167-
if cond["message"] == probeMessage && probePhase == phase {
168-
logger.Debug("condition is unchanged")
169-
return nil
170-
}
170+
cond["lastProbeTime"] = input.DC.GetClock().Now().Format("2006-01-02T15:04:05Z")
171171

172-
if probeStatus != cond["status"] {
173-
cond["lastTransitionTime"] = input.DC.GetClock().Now().Format("2006-01-02T15:04:05Z")
174-
}
172+
if cond["message"] != probeMessage || probePhase != phase {
173+
// if probe status changed - update time
174+
if probeStatus != cond["status"] {
175+
cond["lastTransitionTime"] = input.DC.GetClock().Now().Format("2006-01-02T15:04:05Z")
176+
}
175177

176-
// Update condition
177-
cond["status"] = probeStatus
178+
cond["status"] = probeStatus
178179

179-
cond["message"] = probeMessage
180-
if probeMessage == "" {
181-
delete(cond, "message")
182-
}
180+
cond["message"] = probeMessage
181+
if probeMessage == "" {
182+
delete(cond, "message")
183+
}
183184

184-
cond["reason"] = probeReason
185-
if probeReason == "" {
186-
delete(cond, "reason")
185+
cond["reason"] = probeReason
186+
if probeReason == "" {
187+
delete(cond, "reason")
188+
}
189+
190+
// Update module status phase
191+
phase = probePhase
187192
}
188193

189194
uConditions[condIdx] = cond
190-
// Update module status phase
191-
phase = probePhase
192-
193-
// Update module status phase
194-
if err := unstructured.SetNestedField(uModule.Object, phase, "status", "phase"); err != nil {
195-
return fmt.Errorf("failed to change status.phase: %w", err)
196-
}
197195

198-
// Update module status conditions
199-
if err := unstructured.SetNestedSlice(uModule.Object, uConditions, "status", "conditions"); err != nil {
200-
return fmt.Errorf("failed to change status.conditions: %w", err)
196+
// creating patch
197+
patch, err := json.Marshal(map[string]any{
198+
"status": map[string]any{
199+
"conditions": uConditions,
200+
"phase": phase,
201+
},
202+
})
203+
if err != nil {
204+
return fmt.Errorf("patch marshal error: %w", err)
201205
}
202206

203-
if _, err = k8sClient.Dynamic().Resource(*GetModuleGVK()).UpdateStatus(ctx, uModule, metav1.UpdateOptions{}); err != nil {
204-
return fmt.Errorf("update module resource: %w", err)
207+
if _, err = k8sClient.Dynamic().Resource(*GetModuleGVK()).Patch(ctx, cfg.ModuleName, types.MergePatchType, patch, metav1.PatchOptions{}, "status"); err != nil {
208+
return fmt.Errorf("patch module resource: %w", err)
205209
}
206210

207211
return nil

internal/common-hooks/readiness/hook_test.go

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package readiness_test
1818

1919
import (
2020
"context"
21+
"encoding/json"
2122
"fmt"
2223
"testing"
2324
"time"
@@ -27,6 +28,7 @@ import (
2728
"github.com/stretchr/testify/assert"
2829
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2930
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
31+
"k8s.io/apimachinery/pkg/types"
3032

3133
"github.com/deckhouse/deckhouse/pkg/log"
3234

@@ -64,27 +66,27 @@ func Test_CheckModuleReadiness(t *testing.T) {
6466
},
6567
}
6668

67-
updatedResource := &unstructured.Unstructured{
68-
Object: map[string]interface{}{
69-
"status": map[string]interface{}{
70-
"conditions": []interface{}{
71-
map[string]interface{}{
72-
"type": "IsReady",
73-
"status": "True",
74-
"lastTransitionTime": "2006-01-02T15:04:05Z",
75-
},
69+
patch, err := json.Marshal(map[string]any{
70+
"status": map[string]interface{}{
71+
"conditions": []interface{}{
72+
map[string]interface{}{
73+
"type": "IsReady",
74+
"status": "True",
75+
"lastTransitionTime": "2006-01-02T15:04:05Z",
76+
"lastProbeTime": "2006-01-02T15:04:05Z",
7677
},
77-
"phase": "Ready",
7878
},
79+
"phase": "Ready",
7980
},
80-
}
81+
})
82+
assert.NoError(t, err)
8183

8284
resourceMock := mock.NewKubernetesNamespaceableResourceInterfaceMock(mc)
8385
resourceMock.GetMock.
8486
Expect(minimock.AnyContext, "stub", metav1.GetOptions{}).
8587
Return(resource, nil)
86-
resourceMock.UpdateStatusMock.
87-
Expect(minimock.AnyContext, updatedResource, metav1.UpdateOptions{}).
88+
resourceMock.PatchMock.
89+
Expect(minimock.AnyContext, "stub", types.MergePatchType, patch, metav1.PatchOptions{}, "status").
8890
Return(nil, nil)
8991

9092
dynamicClientMock := mock.NewKubernetesDynamicClientMock(mc)
@@ -215,27 +217,27 @@ func Test_CheckModuleReadiness(t *testing.T) {
215217
},
216218
}
217219

218-
updatedResource := &unstructured.Unstructured{
219-
Object: map[string]interface{}{
220-
"status": map[string]interface{}{
221-
"conditions": []interface{}{
222-
map[string]interface{}{
223-
"type": "IsReady",
224-
"status": "True",
225-
"lastTransitionTime": "2006-01-02T15:04:05Z",
226-
},
220+
patch, err := json.Marshal(map[string]any{
221+
"status": map[string]interface{}{
222+
"conditions": []interface{}{
223+
map[string]interface{}{
224+
"type": "IsReady",
225+
"status": "True",
226+
"lastTransitionTime": "2006-01-02T15:04:05Z",
227+
"lastProbeTime": "2006-01-02T15:04:05Z",
227228
},
228-
"phase": "Ready",
229229
},
230+
"phase": "Ready",
230231
},
231-
}
232+
})
233+
assert.NoError(t, err)
232234

233235
resourceMock := mock.NewKubernetesNamespaceableResourceInterfaceMock(mc)
234236
resourceMock.GetMock.
235237
Expect(minimock.AnyContext, "stub", metav1.GetOptions{}).
236238
Return(resource, nil)
237-
resourceMock.UpdateStatusMock.
238-
Expect(minimock.AnyContext, updatedResource, metav1.UpdateOptions{}).
239+
resourceMock.PatchMock.
240+
Expect(minimock.AnyContext, "stub", types.MergePatchType, patch, metav1.PatchOptions{}, "status").
239241
Return(nil, fmt.Errorf("update error"))
240242

241243
dynamicClientMock := mock.NewKubernetesDynamicClientMock(mc)

0 commit comments

Comments
 (0)