From 2990f4507e53d0d81ac7e6432fd2b5b73e2e3e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BCdiger=20Schmitz?= Date: Mon, 27 Jan 2025 12:17:51 +0100 Subject: [PATCH] fix: correct parsing of maintenance window (#649) * fix: correct parsing of maintenance window * refactored dateparsing * chore: fix go lint warnings * fix: fix review finding * fix: another review finding --- .../internal/services/ske/cluster/resource.go | 14 +++-- .../services/ske/cluster/resource_test.go | 61 +++++++++++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/stackit/internal/services/ske/cluster/resource.go b/stackit/internal/services/ske/cluster/resource.go index b1f8b687..5d4ae2e9 100644 --- a/stackit/internal/services/ske/cluster/resource.go +++ b/stackit/internal/services/ske/cluster/resource.go @@ -1225,6 +1225,14 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error) }, nil } +func parseMaintenanceWindowTime(t string) (time.Time, error) { + v, err := time.Parse("15:04:05-07:00", t) + if err != nil { + v, err = time.Parse("15:04:05Z", t) + } + return v, err +} + func toMaintenancePayload(ctx context.Context, m *Model) (*ske.Maintenance, error) { if m.Maintenance.IsNull() || m.Maintenance.IsUnknown() { return nil, nil @@ -1238,8 +1246,7 @@ func toMaintenancePayload(ctx context.Context, m *Model) (*ske.Maintenance, erro var timeWindowStart *time.Time if !(maintenance.Start.IsNull() || maintenance.Start.IsUnknown()) { - // API expects RFC3339 datetime - tempTime, err := time.Parse(time.RFC3339, maintenance.Start.ValueString()) + tempTime, err := parseMaintenanceWindowTime(maintenance.Start.ValueString()) if err != nil { return nil, fmt.Errorf("converting maintenance object: %w", err) } @@ -1248,8 +1255,7 @@ func toMaintenancePayload(ctx context.Context, m *Model) (*ske.Maintenance, erro var timeWindowEnd *time.Time if !(maintenance.End.IsNull() || maintenance.End.IsUnknown()) { - // API expects RFC3339 datetime - tempTime, err := time.Parse(time.RFC3339, maintenance.End.ValueString()) + tempTime, err := parseMaintenanceWindowTime(maintenance.End.ValueString()) if err != nil { return nil, fmt.Errorf("converting maintenance object: %w", err) } diff --git a/stackit/internal/services/ske/cluster/resource_test.go b/stackit/internal/services/ske/cluster/resource_test.go index 5386ca60..8c47ecc6 100644 --- a/stackit/internal/services/ske/cluster/resource_test.go +++ b/stackit/internal/services/ske/cluster/resource_test.go @@ -2325,3 +2325,64 @@ func TestVerifySystemComponentNodepools(t *testing.T) { }) } } + +func TestMaintenanceWindow(t *testing.T) { + tc := []struct { + start string + end string + wantStart string + wantEnd string + }{ + {"01:00:00Z", "02:00:00Z", "01:00:00", "02:00:00"}, + {"01:00:00+00:00", "02:00:00+00:00", "01:00:00", "02:00:00"}, + {"01:00:00+05:00", "02:00:00+05:00", "01:00:00", "02:00:00"}, + {"01:00:00-05:00", "02:00:00-05:00", "01:00:00", "02:00:00"}, + } + for _, tt := range tc { + t.Run(fmt.Sprintf("from %s to %s", tt.start, tt.end), func(t *testing.T) { + attributeTypes := map[string]attr.Type{ + "start": types.StringType, + "end": types.StringType, + "enable_kubernetes_version_updates": types.BoolType, + "enable_machine_image_version_updates": types.BoolType, + } + + attributeValues := map[string]attr.Value{ + "start": basetypes.NewStringValue(tt.start), + "end": basetypes.NewStringValue(tt.end), + "enable_kubernetes_version_updates": basetypes.NewBoolValue(false), + "enable_machine_image_version_updates": basetypes.NewBoolValue(false), + } + + val, diag := basetypes.NewObjectValue(attributeTypes, attributeValues) + if diag.HasError() { + t.Fatalf("cannot create object value: %v", diag) + } + model := Model{ + Maintenance: val, + } + maintenance, err := toMaintenancePayload(context.Background(), &model) + if err != nil { + t.Fatalf("cannot create payload: %v", err) + } + + startLocation := maintenance.TimeWindow.Start.Location() + endLocation := maintenance.TimeWindow.End.Location() + wantStart, err := time.ParseInLocation(time.TimeOnly, tt.wantStart, startLocation) + if err != nil { + t.Fatalf("cannot parse start date %q: %v", tt.wantStart, err) + } + wantEnd, err := time.ParseInLocation(time.TimeOnly, tt.wantEnd, endLocation) + if err != nil { + t.Fatalf("cannot parse end date %q: %v", tt.wantEnd, err) + } + + if expected, actual := wantStart.In(startLocation), *maintenance.TimeWindow.Start; expected != actual { + t.Errorf("invalid start date. expected %s but got %s", expected, actual) + } + if expected, actual := wantEnd.In(endLocation), (*maintenance.TimeWindow.End); expected != actual { + t.Errorf("invalid End date. expected %s but got %s", expected, actual) + } + }) + } +}