-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(secure-onboarding): Adding AccountFeature Resource (#508)
* SSPROD-38949: Adding support for AccountFeature resource * updating method type * feat(secure-onboarding): Adding AccountFeature Resource Change summary: ---------------- - Adding new Account Feature resource with schema and CRUD operations in parity with Cloudauth Account and AccountComponent resource. - Adding the respective client and support for new resource type. - Added TF ACC tests for the new resource type. - Added docs md for the new resource. * Remove redundant client implementation between create & update --------- Co-authored-by: Haresh Suresh <[email protected]>
- Loading branch information
1 parent
cbc8239
commit f681575
Showing
11 changed files
with
594 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package v2 | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
) | ||
|
||
const ( | ||
cloudauthAccountFeaturePath = "%s/api/cloudauth/v1/accounts/%s/feature/%s" // GET, PUT, DEL | ||
) | ||
|
||
type CloudauthAccountFeatureSecureInterface interface { | ||
Base | ||
CreateOrUpdateCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string, cloudAccountFeature *CloudauthAccountFeatureSecure) (*CloudauthAccountFeatureSecure, string, error) | ||
GetCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string) (*CloudauthAccountFeatureSecure, string, error) | ||
DeleteCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string) (string, error) | ||
} | ||
|
||
// both create and update makes a PUT call to backend | ||
func (client *Client) CreateOrUpdateCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string, cloudAccountFeature *CloudauthAccountFeatureSecure) ( | ||
*CloudauthAccountFeatureSecure, string, error) { | ||
payload, err := client.marshalCloudauthProto(cloudAccountFeature) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
|
||
response, err := client.requester.Request(ctx, http.MethodPut, client.cloudauthAccountFeatureURL(accountID, featureType), payload) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
defer response.Body.Close() | ||
|
||
if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusCreated { | ||
errStatus, err := client.ErrorAndStatusFromResponse(response) | ||
return nil, errStatus, err | ||
} | ||
|
||
cloudauthAccountFeature := &CloudauthAccountFeatureSecure{} | ||
err = client.unmarshalCloudauthProto(response.Body, cloudauthAccountFeature) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
return cloudauthAccountFeature, "", nil | ||
} | ||
|
||
func (client *Client) GetCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string) (*CloudauthAccountFeatureSecure, string, error) { | ||
response, err := client.requester.Request(ctx, http.MethodGet, client.cloudauthAccountFeatureURL(accountID, featureType), nil) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
defer response.Body.Close() | ||
|
||
if response.StatusCode != http.StatusOK { | ||
errStatus, err := client.ErrorAndStatusFromResponse(response) | ||
return nil, errStatus, err | ||
} | ||
|
||
cloudauthAccountFeature := &CloudauthAccountFeatureSecure{} | ||
err = client.unmarshalCloudauthProto(response.Body, cloudauthAccountFeature) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
return cloudauthAccountFeature, "", nil | ||
} | ||
|
||
func (client *Client) DeleteCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string) (string, error) { | ||
response, err := client.requester.Request(ctx, http.MethodDelete, client.cloudauthAccountFeatureURL(accountID, featureType), nil) | ||
if err != nil { | ||
return "", err | ||
} | ||
defer response.Body.Close() | ||
|
||
if response.StatusCode != http.StatusNoContent && response.StatusCode != http.StatusOK { | ||
return client.ErrorAndStatusFromResponse(response) | ||
} | ||
return "", nil | ||
} | ||
|
||
func (client *Client) cloudauthAccountFeatureURL(accountID string, featureType string) string { | ||
return fmt.Sprintf(cloudauthAccountFeaturePath, client.config.url, accountID, featureType) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
248 changes: 248 additions & 0 deletions
248
sysdig/resource_sysdig_secure_cloud_auth_account_feature.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
package sysdig | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"strings" | ||
"time" | ||
|
||
v2 "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2" | ||
cloudauth "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2/cloudauth/go" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
func resourceSysdigSecureCloudauthAccountFeature() *schema.Resource { | ||
timeout := 5 * time.Minute | ||
|
||
return &schema.Resource{ | ||
CreateContext: resourceSysdigSecureCloudauthAccountFeatureCreate, | ||
UpdateContext: resourceSysdigSecureCloudauthAccountFeatureUpdate, | ||
ReadContext: resourceSysdigSecureCloudauthAccountFeatureRead, | ||
DeleteContext: resourceSysdigSecureCloudauthAccountFeatureDelete, | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: schema.ImportStatePassthroughContext, | ||
}, | ||
Timeouts: &schema.ResourceTimeout{ | ||
Create: schema.DefaultTimeout(timeout), | ||
Update: schema.DefaultTimeout(timeout), | ||
Read: schema.DefaultTimeout(timeout), | ||
Delete: schema.DefaultTimeout(timeout), | ||
}, | ||
Schema: getAccountFeatureSchema(), | ||
} | ||
} | ||
|
||
func getAccountFeatureSchema() map[string]*schema.Schema { | ||
// though the schema fields are already defined in cloud_auth_account resource, for AccountFeature | ||
// calls they are required fields. Also, account_id & flags are needed additionally. | ||
featureSchema := map[string]*schema.Schema{ | ||
SchemaAccountId: { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
SchemaType: { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
SchemaEnabled: { | ||
Type: schema.TypeBool, | ||
Required: true, | ||
}, | ||
SchemaComponents: { | ||
Type: schema.TypeList, | ||
Required: true, | ||
Elem: &schema.Schema{ | ||
Type: schema.TypeString, | ||
}, | ||
}, | ||
SchemaFeatureFlags: { | ||
Type: schema.TypeMap, | ||
Optional: true, | ||
}, | ||
} | ||
|
||
return featureSchema | ||
} | ||
|
||
func getSecureCloudauthAccountFeatureClient(client SysdigClients) (v2.CloudauthAccountFeatureSecureInterface, error) { | ||
return client.sysdigSecureClientV2() | ||
} | ||
|
||
func resourceSysdigSecureCloudauthAccountFeatureCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client, err := getSecureCloudauthAccountFeatureClient((meta.(SysdigClients))) | ||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
accountId := data.Get(SchemaAccountId).(string) | ||
cloudauthAccountFeature, errStatus, err := client.CreateOrUpdateCloudauthAccountFeatureSecure( | ||
ctx, accountId, data.Get(SchemaType).(string), cloudauthAccountFeatureFromResourceData(data)) | ||
if err != nil { | ||
return diag.Errorf("Error creating resource: %s %s", errStatus, err) | ||
} | ||
|
||
// using tuple 'accountId/featureType' as TF resource identifier | ||
data.SetId(accountId + "/" + cloudauthAccountFeature.GetType().String()) | ||
err = data.Set(SchemaAccountId, accountId) | ||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceSysdigSecureCloudauthAccountFeatureRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client, err := getSecureCloudauthAccountFeatureClient((meta.(SysdigClients))) | ||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
cloudauthAccountFeature, errStatus, err := client.GetCloudauthAccountFeatureSecure( | ||
ctx, data.Get(SchemaAccountId).(string), data.Get(SchemaType).(string)) | ||
|
||
if err != nil { | ||
if strings.Contains(errStatus, "404") { | ||
return nil | ||
} | ||
return diag.Errorf("Error reading resource: %s %s", errStatus, err) | ||
} | ||
|
||
err = cloudauthAccountFeatureToResourceData(data, cloudauthAccountFeature) | ||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceSysdigSecureCloudauthAccountFeatureUpdate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client, err := getSecureCloudauthAccountFeatureClient(meta.(SysdigClients)) | ||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
accountId := data.Get(SchemaAccountId).(string) | ||
existingCloudAccountFeature, errStatus, err := client.GetCloudauthAccountFeatureSecure( | ||
ctx, accountId, data.Get(SchemaType).(string)) | ||
if err != nil { | ||
if strings.Contains(errStatus, "404") { | ||
return nil | ||
} | ||
return diag.Errorf("Error reading resource: %s %s", errStatus, err) | ||
} | ||
|
||
newCloudAccountFeature := cloudauthAccountFeatureFromResourceData(data) | ||
|
||
// validate and reject non-updatable resource schema fields upfront | ||
err = validateCloudauthAccountFeatureUpdate(existingCloudAccountFeature, newCloudAccountFeature) | ||
if err != nil { | ||
return diag.Errorf("Error updating resource: %s", err) | ||
} | ||
|
||
_, errStatus, err = client.CreateOrUpdateCloudauthAccountFeatureSecure( | ||
ctx, accountId, data.Get(SchemaType).(string), newCloudAccountFeature) | ||
if err != nil { | ||
if strings.Contains(errStatus, "404") { | ||
return nil | ||
} | ||
return diag.Errorf("Error updating resource: %s %s", errStatus, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceSysdigSecureCloudauthAccountFeatureDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client, err := getSecureCloudauthAccountFeatureClient(meta.(SysdigClients)) | ||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
errStatus, err := client.DeleteCloudauthAccountFeatureSecure( | ||
ctx, data.Get(SchemaAccountId).(string), data.Get(SchemaType).(string)) | ||
if err != nil { | ||
if strings.Contains(errStatus, "404") { | ||
return nil | ||
} | ||
return diag.Errorf("Error deleting resource: %s %s", errStatus, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
/* | ||
This function validates and restricts any fields not allowed to be updated during resource updates. | ||
*/ | ||
func validateCloudauthAccountFeatureUpdate(existingFeature *v2.CloudauthAccountFeatureSecure, newFeature *v2.CloudauthAccountFeatureSecure) error { | ||
if existingFeature.Type != newFeature.Type { | ||
errorInvalidResourceUpdate := fmt.Sprintf("Bad Request. Updating restricted fields not allowed: %s", []string{"type"}) | ||
return errors.New(errorInvalidResourceUpdate) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func getFeatureComponentsList(data *schema.ResourceData) []string { | ||
componentsList := []string{} | ||
componentsResourceList := data.Get(SchemaComponents).([]interface{}) | ||
for _, componentID := range componentsResourceList { | ||
componentsList = append(componentsList, componentID.(string)) | ||
} | ||
return componentsList | ||
} | ||
|
||
func getFeatureFlags(data *schema.ResourceData) map[string]string { | ||
featureFlags := map[string]string{} | ||
flagsResource := data.Get(SchemaFeatureFlags).(map[string]interface{}) | ||
for name, value := range flagsResource { | ||
featureFlags[name] = value.(string) | ||
} | ||
return featureFlags | ||
} | ||
|
||
func cloudauthAccountFeatureFromResourceData(data *schema.ResourceData) *v2.CloudauthAccountFeatureSecure { | ||
cloudAccountFeature := &v2.CloudauthAccountFeatureSecure{ | ||
AccountFeature: cloudauth.AccountFeature{ | ||
Type: cloudauth.Feature(cloudauth.Feature_value[data.Get(SchemaType).(string)]), | ||
Enabled: data.Get(SchemaEnabled).(bool), | ||
Components: getFeatureComponentsList(data), | ||
Flags: getFeatureFlags(data), | ||
}, | ||
} | ||
|
||
return cloudAccountFeature | ||
} | ||
|
||
func cloudauthAccountFeatureToResourceData(data *schema.ResourceData, cloudAccountFeature *v2.CloudauthAccountFeatureSecure) error { | ||
|
||
accountId := data.Get(SchemaAccountId).(string) | ||
data.SetId(accountId + "/" + cloudAccountFeature.GetType().String()) | ||
|
||
err := data.Set(SchemaAccountId, accountId) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = data.Set(SchemaType, cloudAccountFeature.GetType().String()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = data.Set(SchemaEnabled, cloudAccountFeature.GetEnabled()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = data.Set(SchemaComponents, cloudAccountFeature.GetComponents()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = data.Set(SchemaFeatureFlags, cloudAccountFeature.GetFlags()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.