Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(secure-onboarding): Adding AccountFeature Resource #508

Merged
merged 4 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sysdig/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,5 @@ const (
SchemaCloudProviderTenantId = "provider_tenant_id"
SchemaCloudProviderAlias = "provider_alias"
SchemaAccountId = "account_id"
SchemaFeatureFlags = "flags"
)
1 change: 0 additions & 1 deletion sysdig/internal/client/v2/cloudauth_account_component.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
const (
cloudauthAccountComponentsPath = "%s/api/cloudauth/v1/accounts/%s/components" // POST
cloudauthAccountComponentPath = "%s/api/cloudauth/v1/accounts/%s/components/%s/%s" // GET, PUT, DEL
// getCloudauthAccountPath = "%s/api/cloudauth/v1/accounts/%s?decrypt=%s" // does GET require decryption?
)

type CloudauthAccountComponentSecureInterface interface {
Expand Down
82 changes: 82 additions & 0 deletions sysdig/internal/client/v2/cloudauth_account_feature.go
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)
}
4 changes: 4 additions & 0 deletions sysdig/internal/client/v2/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,10 @@ type CloudauthAccountComponentSecure struct {
cloudauth.AccountComponent
}

type CloudauthAccountFeatureSecure struct {
cloudauth.AccountFeature
}

type ScanningPolicy struct {
ID string `json:"id,omitempty"`
Version string `json:"version,omitempty"`
Expand Down
1 change: 1 addition & 0 deletions sysdig/internal/client/v2/sysdig.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type SysdigSecure interface {
CloudauthAccountSecureInterface
OrganizationSecureInterface
CloudauthAccountComponentSecureInterface
CloudauthAccountFeatureSecureInterface
}

func (sr *SysdigRequest) Request(ctx context.Context, method string, url string, payload io.Reader) (*http.Response, error) {
Expand Down
1 change: 1 addition & 0 deletions sysdig/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func (p *SysdigProvider) Provider() *schema.Provider {
"sysdig_secure_scanning_policy_assignment": resourceSysdigSecureScanningPolicyAssignment(),
"sysdig_secure_cloud_auth_account": resourceSysdigSecureCloudauthAccount(),
"sysdig_secure_cloud_auth_account_component": resourceSysdigSecureCloudauthAccountComponent(),
"sysdig_secure_cloud_auth_account_feature": resourceSysdigSecureCloudauthAccountFeature(),

"sysdig_monitor_silence_rule": resourceSysdigMonitorSilenceRule(),
"sysdig_monitor_alert_downtime": resourceSysdigMonitorAlertDowntime(),
Expand Down
4 changes: 2 additions & 2 deletions sysdig/resource_sysdig_secure_cloud_auth_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
declare common schemas used across resources here
*/
var (
accountComponents = &schema.Resource{
accountComponent = &schema.Resource{
Schema: map[string]*schema.Schema{
SchemaType: {
Type: schema.TypeString,
Expand Down Expand Up @@ -166,7 +166,7 @@ func resourceSysdigSecureCloudauthAccount() *schema.Resource {
SchemaComponent: {
Type: schema.TypeSet,
Optional: true,
Elem: accountComponents,
Elem: accountComponent,
},
SchemaOrganizationIDKey: {
Type: schema.TypeString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func getAccountComponentSchema() map[string]*schema.Schema {
},
}

for field, schema := range accountComponents.Schema {
for field, schema := range accountComponent.Schema {
componentSchema[field] = schema
}
return componentSchema
Expand Down
248 changes: 248 additions & 0 deletions sysdig/resource_sysdig_secure_cloud_auth_account_feature.go
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
}
Loading
Loading