Onboard Argus (ACL): fix edge cases (#314)

* fix empty acl update/create, update CIDR validator

* fix sigsegv in test, acl description

* Split field mapping, better handle edge cases

* Update stackit/internal/services/argus/instance/resource.go

Co-authored-by: Vicente Pinto <vicente.pinto@freiheit.com>

---------

Co-authored-by: Vicente Pinto <vicente.pinto@freiheit.com>
This commit is contained in:
Diogo Ferrão 2024-03-26 13:54:09 +00:00 committed by GitHub
parent 056c000acc
commit 394d5bf8d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 99 additions and 24 deletions

View file

@ -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(`

View file

@ -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() {

View file

@ -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
}

View file

@ -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 != "" {

View file

@ -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(),
))
}