Skip to content

Commit

Permalink
fix(secure-onboarding) Fix resource update operation (#435)
Browse files Browse the repository at this point in the history
* fix(secure-onboarding) Fix resource update operation

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 ""

* Fix lint issue

* Update docs with the missing attributes
  • Loading branch information
ravinadhruve10 authored Oct 27, 2023
1 parent fc6bad7 commit 59e69cb
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 29 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"
)
75 changes: 61 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,10 @@ func resourceSysdigSecureCloudauthAccountCreate(ctx context.Context, data *schem
}

data.SetId(cloudauthAccount.Id)
err = data.Set(SchemaOrganizationIDKey, cloudauthAccount.OrganizationId)
if err != nil {
return diag.FromErr(err)
}

return nil
}
Expand Down Expand Up @@ -206,8 +215,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 +260,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 +476,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 +536,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 +575,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_key_bytes, _ := json.Marshal(test_service_account_key)
test_service_account_key_encoded := b64.StdEncoding.EncodeToString(test_service_account_key_bytes)
return test_service_account_key_encoded
}
6 changes: 5 additions & 1 deletion website/docs/r/secure_cloud_auth_account.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,8 @@ resource "sysdig_secure_cloud_auth_account" "sample" {

## Attributes Reference

No additional attributes are exported.
In addition to all arguments above, the following attributes are exported:

* `id` - (Computed) The ID of the cloud account.

* `organization_id` - (Computed) The ID of the organization, if the cloud account is part of any organization.

0 comments on commit 59e69cb

Please sign in to comment.