diff --git a/stackit/internal/services/argus/argus_acc_test.go b/stackit/internal/services/argus/argus_acc_test.go index 467b1cd4..08d28870 100644 --- a/stackit/internal/services/argus/argus_acc_test.go +++ b/stackit/internal/services/argus/argus_acc_test.go @@ -269,6 +269,71 @@ func TestAccResource(t *testing.T) { resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "password"), ), }, + // Creation with empty ACL + { + Config: resourceConfig( + utils.Ptr("[]"), + instanceResource["name"], + instanceResource["plan_name"], + scrapeConfigResource["urls"], + scrapeConfigResource["saml2_enable_url_parameters"], + ), + Check: resource.ComposeAggregateTestCheckFunc( + // Instance data + resource.TestCheckResourceAttr("stackit_argus_instance.instance", "project_id", instanceResource["project_id"]), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "instance_id"), + resource.TestCheckResourceAttr("stackit_argus_instance.instance", "name", instanceResource["name"]), + resource.TestCheckResourceAttr("stackit_argus_instance.instance", "plan_name", instanceResource["plan_name"]), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "dashboard_url"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "is_updatable"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_public_read_access"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_url"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_initial_admin_user"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_initial_admin_password"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_retention_days"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_retention_days_5m_downsampling"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_retention_days_1h_downsampling"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_url"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_push_url"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "targets_url"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "alerting_url"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "logs_url"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "logs_push_url"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "jaeger_traces_url"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "jaeger_ui_url"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "otlp_traces_url"), + resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "zipkin_spans_url"), + + // ACL + resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.#", "0"), + + // scrape config data + resource.TestCheckResourceAttrPair( + "stackit_argus_instance.instance", "project_id", + "stackit_argus_scrapeconfig.scrapeconfig", "project_id", + ), + resource.TestCheckResourceAttrPair( + "stackit_argus_instance.instance", "instance_id", + "stackit_argus_scrapeconfig.scrapeconfig", "instance_id", + ), + resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]), + resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "targets.0.urls.#", "2"), + resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]), + resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]), + resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]), + resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]), + resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", scrapeConfigResource["saml2_enable_url_parameters"]), + + // credentials + resource.TestCheckResourceAttr("stackit_argus_credential.credential", "project_id", credentialResource["project_id"]), + resource.TestCheckResourceAttrPair( + "stackit_argus_instance.instance", "instance_id", + "stackit_argus_credential.credential", "instance_id", + ), + resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "username"), + resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "password"), + ), + }, { // Data source Config: fmt.Sprintf(` diff --git a/stackit/internal/services/argus/instance/datasource.go b/stackit/internal/services/argus/instance/datasource.go index 9563ccae..a38eca7f 100644 --- a/stackit/internal/services/argus/instance/datasource.go +++ b/stackit/internal/services/argus/instance/datasource.go @@ -227,11 +227,17 @@ func (d *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques return } - err = mapFields(ctx, instanceResponse, aclList, &model) + // Map response body to schema + err = mapFields(ctx, instanceResponse, &model) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err)) return } + err = mapACLField(aclList, &model) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API response for the ACL: %v", err)) + return + } diags = resp.State.Set(ctx, model) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { diff --git a/stackit/internal/services/argus/instance/resource.go b/stackit/internal/services/argus/instance/resource.go index 6b611bdd..15ceb7fa 100644 --- a/stackit/internal/services/argus/instance/resource.go +++ b/stackit/internal/services/argus/instance/resource.go @@ -314,7 +314,7 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques } // Map response body to schema - err = mapFields(ctx, waitResp, nil, &model) + err = mapFields(ctx, waitResp, &model) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API payload: %v", err)) return @@ -340,9 +340,9 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques } // Map response body to schema - err = mapFields(ctx, waitResp, aclList, &model) + err = mapACLField(aclList, &model) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response: %v", err)) + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the ACL: %v", err)) return } @@ -381,12 +381,19 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r } // Map response body to schema - err = mapFields(ctx, instanceResp, aclList, &model) + err = mapFields(ctx, instanceResp, &model) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err)) return } + // Map response body to schema + err = mapACLField(aclList, &model) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the ACL: %v", err)) + return + } + // Set state to fully populated data diags = resp.State.Set(ctx, model) resp.Diagnostics.Append(diags...) @@ -442,7 +449,7 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques return } - err = mapFields(ctx, waitResp, nil, &model) + err = mapFields(ctx, waitResp, &model) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API payload: %v", err)) return @@ -466,9 +473,9 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques } // Map response body to schema - err = mapFields(ctx, waitResp, aclList, &model) + err = mapACLField(aclList, &model) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing ACL API payload: %v", err)) + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API response for the ACL: %v", err)) return } @@ -528,7 +535,7 @@ func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportS tflog.Info(ctx, "Argus instance state imported") } -func mapFields(ctx context.Context, r *argus.GetInstanceResponse, acl *argus.ListACLResponse, model *Model) error { +func mapFields(ctx context.Context, r *argus.GetInstanceResponse, model *Model) error { if r == nil { return fmt.Errorf("response input is nil") } @@ -594,24 +601,18 @@ func mapFields(ctx context.Context, r *argus.GetInstanceResponse, acl *argus.Lis model.ZipkinSpansURL = types.StringPointerValue(i.ZipkinSpansUrl) } - err := mapACLField(acl, model) - if err != nil { - return err - } - return nil } func mapACLField(aclList *argus.ListACLResponse, model *Model) error { if aclList == nil { - if model.ACL.IsNull() || model.ACL.IsUnknown() { - model.ACL = types.SetNull(types.StringType) - } - return nil + return fmt.Errorf("mapping ACL: nil API response") } if aclList.Acl == nil || len(*aclList.Acl) == 0 { - model.ACL = types.SetNull(types.StringType) + if !(model.ACL.IsNull() || model.ACL.IsUnknown() || model.ACL.Equal(types.SetValueMust(types.StringType, []attr.Value{}))) { + model.ACL = types.SetNull(types.StringType) + } return nil } diff --git a/stackit/internal/services/argus/instance/resource_test.go b/stackit/internal/services/argus/instance/resource_test.go index fcce8bfe..2959a83a 100644 --- a/stackit/internal/services/argus/instance/resource_test.go +++ b/stackit/internal/services/argus/instance/resource_test.go @@ -25,7 +25,7 @@ func TestMapFields(t *testing.T) { &argus.GetInstanceResponse{ Id: utils.Ptr("iid"), }, - nil, + &argus.ListACLResponse{}, Model{ Id: types.StringValue("pid,iid"), ProjectId: types.StringValue("pid"), @@ -139,14 +139,17 @@ func TestMapFields(t *testing.T) { t.Run(tt.description, func(t *testing.T) { state := &Model{ ProjectId: tt.expected.ProjectId, + ACL: types.SetNull(types.StringType), } - err := mapFields(context.Background(), tt.instanceResp, tt.listACLResp, state) - if !tt.isValid && err == nil { + err := mapFields(context.Background(), tt.instanceResp, state) + aclErr := mapACLField(tt.listACLResp, state) + if !tt.isValid && err == nil && aclErr == nil { t.Fatalf("Should have failed") } - if tt.isValid && err != nil { + if tt.isValid && (err != nil || aclErr != nil) { t.Fatalf("Should not have failed: %v", err) } + if tt.isValid { diff := cmp.Diff(state, &tt.expected) if diff != "" { diff --git a/stackit/internal/validate/validate.go b/stackit/internal/validate/validate.go index 86e6e9a3..11925a94 100644 --- a/stackit/internal/validate/validate.go +++ b/stackit/internal/validate/validate.go @@ -189,7 +189,7 @@ func CIDR() *Validator { if err != nil { resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( req.Path, - fmt.Sprintf("parsing value in CIDR notation: %s", err.Error()), + "parsing value in CIDR notation: invalid CIDR address", req.ConfigValue.ValueString(), )) }