From bcf0192f2d92dd4107646329f27f4253b19cb3c0 Mon Sep 17 00:00:00 2001 From: Jan Sykora Date: Fri, 19 Apr 2024 12:37:14 +0200 Subject: [PATCH] fix: lookup rebalancing scheduled job by schedule ID if deleted or recreated --- castai/resource_rebalancing_job.go | 45 +++++++- castai/resource_rebalancing_job_test.go | 130 ++++++++++++++++++++++++ castai/sdk/api.gen.go | 5 + castai/sdk/client.gen.go | 34 +++++-- castai/sdk/mock/client.go | 16 +-- 5 files changed, 213 insertions(+), 17 deletions(-) create mode 100644 castai/resource_rebalancing_job_test.go diff --git a/castai/resource_rebalancing_job.go b/castai/resource_rebalancing_job.go index 5bee32b1..05cffb50 100644 --- a/castai/resource_rebalancing_job.go +++ b/castai/resource_rebalancing_job.go @@ -65,6 +65,15 @@ func resourceRebalancingJobCreate(ctx context.Context, d *schema.ResourceData, m return diag.FromErr(err) } + jobByScheduleId, found, err := getRebalancingJobByScheduleId(ctx, client, *job.ClusterId, *job.RebalancingScheduleId) + if err != nil { + return diag.FromErr(err) + } + if found { + d.SetId(*jobByScheduleId.Id) + return resourceRebalancingJobUpdate(ctx, d, meta) + } + req := sdk.ScheduledRebalancingAPICreateRebalancingJobJSONRequestBody{ Id: job.Id, ClusterId: job.ClusterId, @@ -83,12 +92,25 @@ func resourceRebalancingJobCreate(ctx context.Context, d *schema.ResourceData, m } func resourceRebalancingJobRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*ProviderConfig).api clusterID := d.Get("cluster_id").(string) - job, found, err := getRebalancingJobById(ctx, meta.(*ProviderConfig).api, clusterID, d.Id()) + job, found, err := getRebalancingJobById(ctx, client, clusterID, d.Id()) if err != nil { return diag.FromErr(err) } if !d.IsNewResource() && !found { + rebalancingScheduleID := d.Get("rebalancing_schedule_id").(string) + jobByScheduleId, found, err := getRebalancingJobByScheduleId(ctx, client, clusterID, rebalancingScheduleID) + if err != nil { + return diag.FromErr(err) + } + if found { + if err := rebalancingJobToState(jobByScheduleId, d); err != nil { + return diag.FromErr(err) + } + return nil + } + tflog.Warn(ctx, "Rebalancing job not found, removing from state", map[string]any{"id": d.Id()}) d.SetId("") return nil @@ -189,7 +211,10 @@ func getRebalancingJobByScheduleName(ctx context.Context, client *sdk.ClientWith return nil, fmt.Errorf("getting schedule: %w", err) } - resp, err := client.ScheduledRebalancingAPIListRebalancingJobsWithResponse(ctx, clusterID) + params := sdk.ScheduledRebalancingAPIListRebalancingJobsParams{ + RebalancingScheduleId: schedule.Id, + } + resp, err := client.ScheduledRebalancingAPIListRebalancingJobsWithResponse(ctx, clusterID, ¶ms) if checkErr := sdk.CheckOKResponse(resp, err); checkErr != nil { return nil, checkErr } @@ -223,3 +248,19 @@ func getRebalancingJobById(ctx context.Context, client *sdk.ClientWithResponses, return resp.JSON200, true, nil } + +func getRebalancingJobByScheduleId(ctx context.Context, client *sdk.ClientWithResponses, clusterID, scheduleID string) (*sdk.ScheduledrebalancingV1RebalancingJob, bool, error) { + params := &sdk.ScheduledRebalancingAPIListRebalancingJobsParams{ + RebalancingScheduleId: lo.ToPtr(scheduleID), + } + listResp, err := client.ScheduledRebalancingAPIListRebalancingJobsWithResponse(ctx, clusterID, params) + if checkErr := sdk.CheckOKResponse(listResp, err); checkErr != nil { + return nil, false, checkErr + } + for _, j := range *listResp.JSON200.Jobs { + if *j.RebalancingScheduleId == scheduleID { + return &j, true, nil + } + } + return nil, false, nil +} diff --git a/castai/resource_rebalancing_job_test.go b/castai/resource_rebalancing_job_test.go new file mode 100644 index 00000000..b0690031 --- /dev/null +++ b/castai/resource_rebalancing_job_test.go @@ -0,0 +1,130 @@ +package castai + +import ( + "bytes" + "context" + "github.com/castai/terraform-provider-castai/castai/sdk" + mock_sdk "github.com/castai/terraform-provider-castai/castai/sdk/mock" + "github.com/golang/mock/gomock" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/samber/lo" + "github.com/stretchr/testify/require" + "io" + "net/http" + "strings" + "testing" +) + +func TestRebalancingJobResourceReadContext(t *testing.T) { + r := require.New(t) + mockctrl := gomock.NewController(t) + mockClient := mock_sdk.NewMockClientInterface(mockctrl) + + ctx := context.Background() + provider := &ProviderConfig{ + api: &sdk.ClientWithResponses{ + ClientInterface: mockClient, + }, + } + + id := "abc4192d-a400-48ed-9b22-5fcc9e045258" + clusterId := "b6bfc074-a267-400f-b8f1-db0850c369b1" + rebalancingScheduleId := "8155d717-ee9c-43b0-973f-55610beaf8c2" + body := io.NopCloser(bytes.NewReader([]byte(` + { + "id": "abc4192d-a400-48ed-9b22-5fcc9e045258", + "clusterId": "b6bfc074-a267-400f-b8f1-db0850c369b17", + "rebalancingScheduleId": "8155d717-ee9c-43b0-973f-55610beaf8c2", + "rebalancingPlanId": "87111656-1c69-4316-91a2-7bd5513641b3", + "enabled": true, + "lastTriggerAt": "2024-04-19T11:30:05.277333Z", + "nextTriggerAt": "2024-04-19T11:31:00Z", + "status": "JobStatusFailed" + } + `))) + mockClient.EXPECT(). + ScheduledRebalancingAPIGetRebalancingJob(gomock.Any(), clusterId, id). + Return(&http.Response{StatusCode: 200, Body: body, Header: map[string][]string{"Content-Type": {"json"}}}, nil) + + resource := resourceRebalancingJob() + val := cty.ObjectVal(map[string]cty.Value{ + FieldClusterId: cty.StringVal(clusterId), + "rebalancing_schedule_id": cty.StringVal(rebalancingScheduleId), + "id": cty.StringVal(id), + }) + state := terraform.NewInstanceStateShimmedFromValue(val, 0) + state.ID = id + + data := resource.Data(state) + result := resource.ReadContext(ctx, data, provider) + r.Nil(result) + r.False(result.HasError()) + r.ElementsMatch(strings.Split(`ID = abc4192d-a400-48ed-9b22-5fcc9e045258 +cluster_id = b6bfc074-a267-400f-b8f1-db0850c369b17 +enabled = true +rebalancing_schedule_id = 8155d717-ee9c-43b0-973f-55610beaf8c2 +Tainted = false +`, "\n"), + strings.Split(data.State().String(), "\n")) +} + +func TestRebalancingJobResourceReadContext_ByScheduledID(t *testing.T) { + r := require.New(t) + mockctrl := gomock.NewController(t) + mockClient := mock_sdk.NewMockClientInterface(mockctrl) + + ctx := context.Background() + provider := &ProviderConfig{ + api: &sdk.ClientWithResponses{ + ClientInterface: mockClient, + }, + } + + id := "abc4192d-a400-48ed-9b22-5fcc9e045258" + clusterId := "b6bfc074-a267-400f-b8f1-db0850c369b1" + rebalancingScheduleId := "8155d717-ee9c-43b0-973f-55610beaf8c2" + body := io.NopCloser(bytes.NewReader([]byte(` + { + "jobs": [ + { + "id": "abc4192d-a400-48ed-9b22-5fcc9e045258", + "clusterId": "b6bfc074-a267-400f-b8f1-db0850c369b1", + "rebalancingScheduleId": "8155d717-ee9c-43b0-973f-55610beaf8c2", + "rebalancingPlanId": "67f30f59-4eab-4f9c-b59e-8753ac5d6ed1", + "enabled": true, + "lastTriggerAt": "2024-04-19T11:24:07.501531Z", + "nextTriggerAt": "2024-04-19T11:25:00Z", + "status": "JobStatusFailed" + } + ] + } + `))) + mockClient.EXPECT(). + ScheduledRebalancingAPIGetRebalancingJob(gomock.Any(), clusterId, id). + Return(&http.Response{StatusCode: 404, Body: io.NopCloser(bytes.NewReader([]byte{})), Header: map[string][]string{"Content-Type": {"json"}}}, nil) + mockClient.EXPECT(). + ScheduledRebalancingAPIListRebalancingJobs(gomock.Any(), clusterId, &sdk.ScheduledRebalancingAPIListRebalancingJobsParams{RebalancingScheduleId: lo.ToPtr(rebalancingScheduleId)}). + Return(&http.Response{StatusCode: 200, Body: body, Header: map[string][]string{"Content-Type": {"json"}}}, nil) + + resource := resourceRebalancingJob() + val := cty.ObjectVal(map[string]cty.Value{ + FieldClusterId: cty.StringVal(clusterId), + "rebalancing_schedule_id": cty.StringVal(rebalancingScheduleId), + "id": cty.StringVal(id), + }) + state := terraform.NewInstanceStateShimmedFromValue(val, 0) + state.ID = id + + data := resource.Data(state) + result := resource.ReadContext(ctx, data, provider) + r.Nil(result) + r.False(result.HasError()) + r.ElementsMatch(strings.Split(`ID = abc4192d-a400-48ed-9b22-5fcc9e045258 +cluster_id = b6bfc074-a267-400f-b8f1-db0850c369b1 +enabled = true +rebalancing_schedule_id = 8155d717-ee9c-43b0-973f-55610beaf8c2 +Tainted = false +`, "\n"), + strings.Split(data.State().String(), "\n")) +} diff --git a/castai/sdk/api.gen.go b/castai/sdk/api.gen.go index 1b9b4ac1..32704aff 100644 --- a/castai/sdk/api.gen.go +++ b/castai/sdk/api.gen.go @@ -2617,6 +2617,11 @@ type NodeTemplatesAPIUpdateNodeTemplateJSONBody = NodetemplatesV1UpdateNodeTempl // PoliciesAPIUpsertClusterPoliciesJSONBody defines parameters for PoliciesAPIUpsertClusterPolicies. type PoliciesAPIUpsertClusterPoliciesJSONBody = PoliciesV1Policies +// ScheduledRebalancingAPIListRebalancingJobsParams defines parameters for ScheduledRebalancingAPIListRebalancingJobs. +type ScheduledRebalancingAPIListRebalancingJobsParams struct { + RebalancingScheduleId *string `form:"rebalancingScheduleId,omitempty" json:"rebalancingScheduleId,omitempty"` +} + // ScheduledRebalancingAPICreateRebalancingJobJSONBody defines parameters for ScheduledRebalancingAPICreateRebalancingJob. type ScheduledRebalancingAPICreateRebalancingJobJSONBody = ScheduledrebalancingV1RebalancingJob diff --git a/castai/sdk/client.gen.go b/castai/sdk/client.gen.go index 158c367d..a9f8f23a 100644 --- a/castai/sdk/client.gen.go +++ b/castai/sdk/client.gen.go @@ -194,7 +194,7 @@ type ClientInterface interface { PoliciesAPIUpsertClusterPolicies(ctx context.Context, clusterId string, body PoliciesAPIUpsertClusterPoliciesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // ScheduledRebalancingAPIListRebalancingJobs request - ScheduledRebalancingAPIListRebalancingJobs(ctx context.Context, clusterId string, reqEditors ...RequestEditorFn) (*http.Response, error) + ScheduledRebalancingAPIListRebalancingJobs(ctx context.Context, clusterId string, params *ScheduledRebalancingAPIListRebalancingJobsParams, reqEditors ...RequestEditorFn) (*http.Response, error) // ScheduledRebalancingAPICreateRebalancingJob request with any body ScheduledRebalancingAPICreateRebalancingJobWithBody(ctx context.Context, clusterId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -872,8 +872,8 @@ func (c *Client) PoliciesAPIUpsertClusterPolicies(ctx context.Context, clusterId return c.Client.Do(req) } -func (c *Client) ScheduledRebalancingAPIListRebalancingJobs(ctx context.Context, clusterId string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewScheduledRebalancingAPIListRebalancingJobsRequest(c.Server, clusterId) +func (c *Client) ScheduledRebalancingAPIListRebalancingJobs(ctx context.Context, clusterId string, params *ScheduledRebalancingAPIListRebalancingJobsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewScheduledRebalancingAPIListRebalancingJobsRequest(c.Server, clusterId, params) if err != nil { return nil, err } @@ -3012,7 +3012,7 @@ func NewPoliciesAPIUpsertClusterPoliciesRequestWithBody(server string, clusterId } // NewScheduledRebalancingAPIListRebalancingJobsRequest generates requests for ScheduledRebalancingAPIListRebalancingJobs -func NewScheduledRebalancingAPIListRebalancingJobsRequest(server string, clusterId string) (*http.Request, error) { +func NewScheduledRebalancingAPIListRebalancingJobsRequest(server string, clusterId string, params *ScheduledRebalancingAPIListRebalancingJobsParams) (*http.Request, error) { var err error var pathParam0 string @@ -3037,6 +3037,26 @@ func NewScheduledRebalancingAPIListRebalancingJobsRequest(server string, cluster return nil, err } + queryValues := queryURL.Query() + + if params.RebalancingScheduleId != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "rebalancingScheduleId", runtime.ParamLocationQuery, *params.RebalancingScheduleId); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + req, err := http.NewRequest("GET", queryURL.String(), nil) if err != nil { return nil, err @@ -5643,7 +5663,7 @@ type ClientWithResponsesInterface interface { PoliciesAPIUpsertClusterPoliciesWithResponse(ctx context.Context, clusterId string, body PoliciesAPIUpsertClusterPoliciesJSONRequestBody) (*PoliciesAPIUpsertClusterPoliciesResponse, error) // ScheduledRebalancingAPIListRebalancingJobs request - ScheduledRebalancingAPIListRebalancingJobsWithResponse(ctx context.Context, clusterId string) (*ScheduledRebalancingAPIListRebalancingJobsResponse, error) + ScheduledRebalancingAPIListRebalancingJobsWithResponse(ctx context.Context, clusterId string, params *ScheduledRebalancingAPIListRebalancingJobsParams) (*ScheduledRebalancingAPIListRebalancingJobsResponse, error) // ScheduledRebalancingAPICreateRebalancingJob request with any body ScheduledRebalancingAPICreateRebalancingJobWithBodyWithResponse(ctx context.Context, clusterId string, contentType string, body io.Reader) (*ScheduledRebalancingAPICreateRebalancingJobResponse, error) @@ -8813,8 +8833,8 @@ func (c *ClientWithResponses) PoliciesAPIUpsertClusterPoliciesWithResponse(ctx c } // ScheduledRebalancingAPIListRebalancingJobsWithResponse request returning *ScheduledRebalancingAPIListRebalancingJobsResponse -func (c *ClientWithResponses) ScheduledRebalancingAPIListRebalancingJobsWithResponse(ctx context.Context, clusterId string) (*ScheduledRebalancingAPIListRebalancingJobsResponse, error) { - rsp, err := c.ScheduledRebalancingAPIListRebalancingJobs(ctx, clusterId) +func (c *ClientWithResponses) ScheduledRebalancingAPIListRebalancingJobsWithResponse(ctx context.Context, clusterId string, params *ScheduledRebalancingAPIListRebalancingJobsParams) (*ScheduledRebalancingAPIListRebalancingJobsResponse, error) { + rsp, err := c.ScheduledRebalancingAPIListRebalancingJobs(ctx, clusterId, params) if err != nil { return nil, err } diff --git a/castai/sdk/mock/client.go b/castai/sdk/mock/client.go index a134930f..1a1fa6c9 100644 --- a/castai/sdk/mock/client.go +++ b/castai/sdk/mock/client.go @@ -1856,9 +1856,9 @@ func (mr *MockClientInterfaceMockRecorder) ScheduledRebalancingAPIListAvailableR } // ScheduledRebalancingAPIListRebalancingJobs mocks base method. -func (m *MockClientInterface) ScheduledRebalancingAPIListRebalancingJobs(ctx context.Context, clusterId string, reqEditors ...sdk.RequestEditorFn) (*http.Response, error) { +func (m *MockClientInterface) ScheduledRebalancingAPIListRebalancingJobs(ctx context.Context, clusterId string, params *sdk.ScheduledRebalancingAPIListRebalancingJobsParams, reqEditors ...sdk.RequestEditorFn) (*http.Response, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, clusterId} + varargs := []interface{}{ctx, clusterId, params} for _, a := range reqEditors { varargs = append(varargs, a) } @@ -1869,9 +1869,9 @@ func (m *MockClientInterface) ScheduledRebalancingAPIListRebalancingJobs(ctx con } // ScheduledRebalancingAPIListRebalancingJobs indicates an expected call of ScheduledRebalancingAPIListRebalancingJobs. -func (mr *MockClientInterfaceMockRecorder) ScheduledRebalancingAPIListRebalancingJobs(ctx, clusterId interface{}, reqEditors ...interface{}) *gomock.Call { +func (mr *MockClientInterfaceMockRecorder) ScheduledRebalancingAPIListRebalancingJobs(ctx, clusterId, params interface{}, reqEditors ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, clusterId}, reqEditors...) + varargs := append([]interface{}{ctx, clusterId, params}, reqEditors...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduledRebalancingAPIListRebalancingJobs", reflect.TypeOf((*MockClientInterface)(nil).ScheduledRebalancingAPIListRebalancingJobs), varargs...) } @@ -3814,18 +3814,18 @@ func (mr *MockClientWithResponsesInterfaceMockRecorder) ScheduledRebalancingAPIL } // ScheduledRebalancingAPIListRebalancingJobsWithResponse mocks base method. -func (m *MockClientWithResponsesInterface) ScheduledRebalancingAPIListRebalancingJobsWithResponse(ctx context.Context, clusterId string) (*sdk.ScheduledRebalancingAPIListRebalancingJobsResponse, error) { +func (m *MockClientWithResponsesInterface) ScheduledRebalancingAPIListRebalancingJobsWithResponse(ctx context.Context, clusterId string, params *sdk.ScheduledRebalancingAPIListRebalancingJobsParams) (*sdk.ScheduledRebalancingAPIListRebalancingJobsResponse, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ScheduledRebalancingAPIListRebalancingJobsWithResponse", ctx, clusterId) + ret := m.ctrl.Call(m, "ScheduledRebalancingAPIListRebalancingJobsWithResponse", ctx, clusterId, params) ret0, _ := ret[0].(*sdk.ScheduledRebalancingAPIListRebalancingJobsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ScheduledRebalancingAPIListRebalancingJobsWithResponse indicates an expected call of ScheduledRebalancingAPIListRebalancingJobsWithResponse. -func (mr *MockClientWithResponsesInterfaceMockRecorder) ScheduledRebalancingAPIListRebalancingJobsWithResponse(ctx, clusterId interface{}) *gomock.Call { +func (mr *MockClientWithResponsesInterfaceMockRecorder) ScheduledRebalancingAPIListRebalancingJobsWithResponse(ctx, clusterId, params interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduledRebalancingAPIListRebalancingJobsWithResponse", reflect.TypeOf((*MockClientWithResponsesInterface)(nil).ScheduledRebalancingAPIListRebalancingJobsWithResponse), ctx, clusterId) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduledRebalancingAPIListRebalancingJobsWithResponse", reflect.TypeOf((*MockClientWithResponsesInterface)(nil).ScheduledRebalancingAPIListRebalancingJobsWithResponse), ctx, clusterId, params) } // ScheduledRebalancingAPIListRebalancingSchedulesWithResponse mocks base method.