Skip to content

Commit

Permalink
fix(secure-onboarding) Fix resource update operation
Browse files Browse the repository at this point in the history
The TF provider’s knowledge of cloud account object is older and
different from the actual BE cloud account. This is because BE has
modified the account object with fields like organization_id during
org onboarding. Hence, during intended PUTs, TF overwrites these
fields resulting in unexpected state.

Fix summary:
------------
1. Add and expose the organization_id in provider's account resource schema
   (as a computed field only). This is to let the customers know if their
   onboarded account is part of any org when they fetch the real existing
   infrastructure objects using GET calls.
2. During resource updates (PUT API calls) restrict and reject with error
   when the customer tries to update any non-updatable resource fields.
3. Minor refactoring for cleaner code.

Testing done:
---------------
Validated the scenarios :-
- During tf apply again --> it returns and updates organization_id on the account.
- No unnecessary PUTs
- On triggering an intended PUT :-
  - restricts any non-updatable fields
  - PUT works as expected, doesn't oevrwrite organization_id to ""
  • Loading branch information
ravinadhruve10 committed Oct 25, 2023
1 parent eebe2b6 commit 3ed0614
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 28 deletions.
1 change: 1 addition & 0 deletions sysdig/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@ const (
SchemaCloudProviderType = "provider_type"
SchemaFeature = "feature"
SchemaManagementAccountId = "management_account_id"
SchemaOrganizationIDKey = "organization_id"
)
72 changes: 58 additions & 14 deletions sysdig/resource_sysdig_secure_cloud_auth_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
b64 "encoding/base64"
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"
Expand Down Expand Up @@ -153,6 +154,10 @@ func resourceSysdigSecureCloudauthAccount() *schema.Resource {
Optional: true,
Elem: accountComponents,
},
SchemaOrganizationIDKey: {
Type: schema.TypeString,
Computed: true,
},
},
}
}
Expand All @@ -173,6 +178,7 @@ func resourceSysdigSecureCloudauthAccountCreate(ctx context.Context, data *schem
}

data.SetId(cloudauthAccount.Id)
data.Set(SchemaOrganizationIDKey, cloudauthAccount.OrganizationId)

Check failure on line 181 in sysdig/resource_sysdig_secure_cloud_auth_account.go

View workflow job for this annotation

GitHub Actions / Test / Lint

Error return value of `data.Set` is not checked (errcheck)

return nil
}
Expand Down Expand Up @@ -206,8 +212,23 @@ func resourceSysdigSecureCloudauthAccountUpdate(ctx context.Context, data *schem
return diag.FromErr(err)
}

_, errStatus, err := client.UpdateCloudauthAccountSecure(ctx, data.Id(), cloudauthAccountFromResourceData(data))
existingCloudAccount, errStatus, err := client.GetCloudauthAccountSecure(ctx, data.Id())
if err != nil {
if strings.Contains(errStatus, "404") {
return nil
}
return diag.FromErr(err)
}

newCloudAccount := cloudauthAccountFromResourceData(data)

// validate and reject non-updatable resource schema fields upfront
err = validateCloudauthAccountUpdate(existingCloudAccount, newCloudAccount)
if err != nil {
return diag.FromErr(err)
}

_, errStatus, err = client.UpdateCloudauthAccountSecure(ctx, data.Id(), newCloudAccount)
if err != nil {
if strings.Contains(errStatus, "404") {
return nil
Expand Down Expand Up @@ -236,6 +257,19 @@ func resourceSysdigSecureCloudauthAccountDelete(ctx context.Context, data *schem
return nil
}

/*
This function validates and restricts any fields not allowed to be updated during resource updates.
*/
func validateCloudauthAccountUpdate(existingCloudAccount *v2.CloudauthAccountSecure, newCloudAccount *v2.CloudauthAccountSecure) error {
if existingCloudAccount.Enabled != newCloudAccount.Enabled || existingCloudAccount.Provider != newCloudAccount.Provider ||
existingCloudAccount.ProviderId != newCloudAccount.ProviderId || existingCloudAccount.OrganizationId != newCloudAccount.OrganizationId {
errorInvalidResourceUpdate := fmt.Sprintf("Bad Request. Updating restricted fields not allowed: %s", []string{"enabled", "provider_type", "provider_id", "organization_id"})
return errors.New(errorInvalidResourceUpdate)
}

return nil
}

/*
This function converts a schema set to map for iteration.
*/
Expand Down Expand Up @@ -439,11 +473,12 @@ func cloudauthAccountFromResourceData(data *schema.ResourceData) *v2.CloudauthAc

return &v2.CloudauthAccountSecure{
CloudAccount: cloudauth.CloudAccount{
Enabled: data.Get(SchemaEnabled).(bool),
ProviderId: data.Get(SchemaCloudProviderId).(string),
Provider: cloudauth.Provider(cloudauth.Provider_value[data.Get(SchemaCloudProviderType).(string)]),
Components: accountComponents,
Feature: accountFeatures,
Enabled: data.Get(SchemaEnabled).(bool),
OrganizationId: data.Get(SchemaOrganizationIDKey).(string),
ProviderId: data.Get(SchemaCloudProviderId).(string),
Provider: cloudauth.Provider(cloudauth.Provider_value[data.Get(SchemaCloudProviderType).(string)]),
Components: accountComponents,
Feature: accountFeatures,
},
}
}
Expand Down Expand Up @@ -498,6 +533,19 @@ func featureToResourceData(features *cloudauth.AccountFeatures) []interface{} {
return nil
}

func componentsToResourceData(components []*cloudauth.AccountComponent) []map[string]interface{} {
componentsList := []map[string]interface{}{}

for _, comp := range components {
componentsList = append(componentsList, map[string]interface{}{
SchemaType: comp.Type.String(),
SchemaInstance: comp.Instance,
})
}

return componentsList
}

func cloudauthAccountToResourceData(data *schema.ResourceData, cloudAccount *v2.CloudauthAccountSecure) error {
err := data.Set(SchemaIDKey, cloudAccount.Id)
if err != nil {
Expand All @@ -524,16 +572,12 @@ func cloudauthAccountToResourceData(data *schema.ResourceData, cloudAccount *v2.
return err
}

components := []map[string]interface{}{}

for _, comp := range cloudAccount.Components {
components = append(components, map[string]interface{}{
SchemaType: comp.Type.String(),
SchemaInstance: comp.Instance,
})
err = data.Set(SchemaComponent, componentsToResourceData(cloudAccount.Components))
if err != nil {
return err
}

err = data.Set(SchemaComponent, components)
err = data.Set(SchemaOrganizationIDKey, cloudAccount.OrganizationId)
if err != nil {
return err
}
Expand Down
33 changes: 19 additions & 14 deletions sysdig/resource_sysdig_secure_cloud_auth_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,6 @@ func TestAccSecureCloudAuthAccountFC(t *testing.T) {
}

func secureCloudAuthAccountWithFC(accountID string) string {
type sample_service_account_key struct {
ProjectId string `json:"project_id"`
PrivateKeyId string `json:"private_key_id"`
PrivateKey string `json:"private_key"`
}
test_service_account_key := &sample_service_account_key{
ProjectId: fmt.Sprintf("sample-1-%s", accountID),
PrivateKeyId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
PrivateKey: "-----BEGIN PRIVATE KEY-----\nxxxxxxxxxxxxxxxxxxxxxxxxxxx\n-----END PRIVATE KEY-----\n",
}
test_service_account_keyJSON, _ := json.Marshal(test_service_account_key)
test_service_account_key_encoded := b64.StdEncoding.EncodeToString([]byte(string(test_service_account_keyJSON)))

return fmt.Sprintf(`
resource "sysdig_secure_cloud_auth_account" "sample-1" {
provider_id = "sample-1-%s"
Expand Down Expand Up @@ -121,5 +108,23 @@ resource "sysdig_secure_cloud_auth_account" "sample-1" {
ignore_changes = [component]
}
}
`, accountID, test_service_account_key_encoded)
`, accountID, getEncodedServiceAccountKey("sample-1", accountID))
}

func getEncodedServiceAccountKey(resourceName string, accountID string) string {
type sample_service_account_key struct {
Type string `json:"type"`
ProjectId string `json:"project_id"`
PrivateKeyId string `json:"private_key_id"`
PrivateKey string `json:"private_key"`
}
test_service_account_key := &sample_service_account_key{
Type: "service_account",
ProjectId: fmt.Sprintf("%s-%s", resourceName, accountID),
PrivateKeyId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
PrivateKey: "-----BEGIN PRIVATE KEY-----\nxxxxxxxxxxxxxxxxxxxxxxxxxxx\n-----END PRIVATE KEY-----\n",
}
test_service_account_keyJSON, _ := json.Marshal(test_service_account_key)
test_service_account_key_encoded := b64.StdEncoding.EncodeToString([]byte(string(test_service_account_keyJSON)))
return test_service_account_key_encoded
}

0 comments on commit 3ed0614

Please sign in to comment.