Skip to content

Commit f681575

Browse files
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 <haresh.suresh@sysdig.com>
1 parent cbc8239 commit f681575

11 files changed

+594
-4
lines changed

sysdig/common.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,5 @@ const (
6464
SchemaCloudProviderTenantId = "provider_tenant_id"
6565
SchemaCloudProviderAlias = "provider_alias"
6666
SchemaAccountId = "account_id"
67+
SchemaFeatureFlags = "flags"
6768
)

sysdig/internal/client/v2/cloudauth_account_component.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
const (
1010
cloudauthAccountComponentsPath = "%s/api/cloudauth/v1/accounts/%s/components" // POST
1111
cloudauthAccountComponentPath = "%s/api/cloudauth/v1/accounts/%s/components/%s/%s" // GET, PUT, DEL
12-
// getCloudauthAccountPath = "%s/api/cloudauth/v1/accounts/%s?decrypt=%s" // does GET require decryption?
1312
)
1413

1514
type CloudauthAccountComponentSecureInterface interface {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package v2
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
)
8+
9+
const (
10+
cloudauthAccountFeaturePath = "%s/api/cloudauth/v1/accounts/%s/feature/%s" // GET, PUT, DEL
11+
)
12+
13+
type CloudauthAccountFeatureSecureInterface interface {
14+
Base
15+
CreateOrUpdateCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string, cloudAccountFeature *CloudauthAccountFeatureSecure) (*CloudauthAccountFeatureSecure, string, error)
16+
GetCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string) (*CloudauthAccountFeatureSecure, string, error)
17+
DeleteCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string) (string, error)
18+
}
19+
20+
// both create and update makes a PUT call to backend
21+
func (client *Client) CreateOrUpdateCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string, cloudAccountFeature *CloudauthAccountFeatureSecure) (
22+
*CloudauthAccountFeatureSecure, string, error) {
23+
payload, err := client.marshalCloudauthProto(cloudAccountFeature)
24+
if err != nil {
25+
return nil, "", err
26+
}
27+
28+
response, err := client.requester.Request(ctx, http.MethodPut, client.cloudauthAccountFeatureURL(accountID, featureType), payload)
29+
if err != nil {
30+
return nil, "", err
31+
}
32+
defer response.Body.Close()
33+
34+
if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusCreated {
35+
errStatus, err := client.ErrorAndStatusFromResponse(response)
36+
return nil, errStatus, err
37+
}
38+
39+
cloudauthAccountFeature := &CloudauthAccountFeatureSecure{}
40+
err = client.unmarshalCloudauthProto(response.Body, cloudauthAccountFeature)
41+
if err != nil {
42+
return nil, "", err
43+
}
44+
return cloudauthAccountFeature, "", nil
45+
}
46+
47+
func (client *Client) GetCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string) (*CloudauthAccountFeatureSecure, string, error) {
48+
response, err := client.requester.Request(ctx, http.MethodGet, client.cloudauthAccountFeatureURL(accountID, featureType), nil)
49+
if err != nil {
50+
return nil, "", err
51+
}
52+
defer response.Body.Close()
53+
54+
if response.StatusCode != http.StatusOK {
55+
errStatus, err := client.ErrorAndStatusFromResponse(response)
56+
return nil, errStatus, err
57+
}
58+
59+
cloudauthAccountFeature := &CloudauthAccountFeatureSecure{}
60+
err = client.unmarshalCloudauthProto(response.Body, cloudauthAccountFeature)
61+
if err != nil {
62+
return nil, "", err
63+
}
64+
return cloudauthAccountFeature, "", nil
65+
}
66+
67+
func (client *Client) DeleteCloudauthAccountFeatureSecure(ctx context.Context, accountID, featureType string) (string, error) {
68+
response, err := client.requester.Request(ctx, http.MethodDelete, client.cloudauthAccountFeatureURL(accountID, featureType), nil)
69+
if err != nil {
70+
return "", err
71+
}
72+
defer response.Body.Close()
73+
74+
if response.StatusCode != http.StatusNoContent && response.StatusCode != http.StatusOK {
75+
return client.ErrorAndStatusFromResponse(response)
76+
}
77+
return "", nil
78+
}
79+
80+
func (client *Client) cloudauthAccountFeatureURL(accountID string, featureType string) string {
81+
return fmt.Sprintf(cloudauthAccountFeaturePath, client.config.url, accountID, featureType)
82+
}

sysdig/internal/client/v2/model.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,10 @@ type CloudauthAccountComponentSecure struct {
609609
cloudauth.AccountComponent
610610
}
611611

612+
type CloudauthAccountFeatureSecure struct {
613+
cloudauth.AccountFeature
614+
}
615+
612616
type ScanningPolicy struct {
613617
ID string `json:"id,omitempty"`
614618
Version string `json:"version,omitempty"`

sysdig/internal/client/v2/sysdig.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type SysdigSecure interface {
4747
CloudauthAccountSecureInterface
4848
OrganizationSecureInterface
4949
CloudauthAccountComponentSecureInterface
50+
CloudauthAccountFeatureSecureInterface
5051
}
5152

5253
func (sr *SysdigRequest) Request(ctx context.Context, method string, url string, payload io.Reader) (*http.Response, error) {

sysdig/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ func (p *SysdigProvider) Provider() *schema.Provider {
156156
"sysdig_secure_scanning_policy_assignment": resourceSysdigSecureScanningPolicyAssignment(),
157157
"sysdig_secure_cloud_auth_account": resourceSysdigSecureCloudauthAccount(),
158158
"sysdig_secure_cloud_auth_account_component": resourceSysdigSecureCloudauthAccountComponent(),
159+
"sysdig_secure_cloud_auth_account_feature": resourceSysdigSecureCloudauthAccountFeature(),
159160

160161
"sysdig_monitor_silence_rule": resourceSysdigMonitorSilenceRule(),
161162
"sysdig_monitor_alert_downtime": resourceSysdigMonitorAlertDowntime(),

sysdig/resource_sysdig_secure_cloud_auth_account.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
declare common schemas used across resources here
2424
*/
2525
var (
26-
accountComponents = &schema.Resource{
26+
accountComponent = &schema.Resource{
2727
Schema: map[string]*schema.Schema{
2828
SchemaType: {
2929
Type: schema.TypeString,
@@ -166,7 +166,7 @@ func resourceSysdigSecureCloudauthAccount() *schema.Resource {
166166
SchemaComponent: {
167167
Type: schema.TypeSet,
168168
Optional: true,
169-
Elem: accountComponents,
169+
Elem: accountComponent,
170170
},
171171
SchemaOrganizationIDKey: {
172172
Type: schema.TypeString,

sysdig/resource_sysdig_secure_cloud_auth_account_component.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func getAccountComponentSchema() map[string]*schema.Schema {
4848
},
4949
}
5050

51-
for field, schema := range accountComponents.Schema {
51+
for field, schema := range accountComponent.Schema {
5252
componentSchema[field] = schema
5353
}
5454
return componentSchema
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
package sysdig
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"strings"
8+
"time"
9+
10+
v2 "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2"
11+
cloudauth "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2/cloudauth/go"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
14+
)
15+
16+
func resourceSysdigSecureCloudauthAccountFeature() *schema.Resource {
17+
timeout := 5 * time.Minute
18+
19+
return &schema.Resource{
20+
CreateContext: resourceSysdigSecureCloudauthAccountFeatureCreate,
21+
UpdateContext: resourceSysdigSecureCloudauthAccountFeatureUpdate,
22+
ReadContext: resourceSysdigSecureCloudauthAccountFeatureRead,
23+
DeleteContext: resourceSysdigSecureCloudauthAccountFeatureDelete,
24+
Importer: &schema.ResourceImporter{
25+
StateContext: schema.ImportStatePassthroughContext,
26+
},
27+
Timeouts: &schema.ResourceTimeout{
28+
Create: schema.DefaultTimeout(timeout),
29+
Update: schema.DefaultTimeout(timeout),
30+
Read: schema.DefaultTimeout(timeout),
31+
Delete: schema.DefaultTimeout(timeout),
32+
},
33+
Schema: getAccountFeatureSchema(),
34+
}
35+
}
36+
37+
func getAccountFeatureSchema() map[string]*schema.Schema {
38+
// though the schema fields are already defined in cloud_auth_account resource, for AccountFeature
39+
// calls they are required fields. Also, account_id & flags are needed additionally.
40+
featureSchema := map[string]*schema.Schema{
41+
SchemaAccountId: {
42+
Type: schema.TypeString,
43+
Required: true,
44+
},
45+
SchemaType: {
46+
Type: schema.TypeString,
47+
Required: true,
48+
},
49+
SchemaEnabled: {
50+
Type: schema.TypeBool,
51+
Required: true,
52+
},
53+
SchemaComponents: {
54+
Type: schema.TypeList,
55+
Required: true,
56+
Elem: &schema.Schema{
57+
Type: schema.TypeString,
58+
},
59+
},
60+
SchemaFeatureFlags: {
61+
Type: schema.TypeMap,
62+
Optional: true,
63+
},
64+
}
65+
66+
return featureSchema
67+
}
68+
69+
func getSecureCloudauthAccountFeatureClient(client SysdigClients) (v2.CloudauthAccountFeatureSecureInterface, error) {
70+
return client.sysdigSecureClientV2()
71+
}
72+
73+
func resourceSysdigSecureCloudauthAccountFeatureCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
74+
client, err := getSecureCloudauthAccountFeatureClient((meta.(SysdigClients)))
75+
if err != nil {
76+
return diag.FromErr(err)
77+
}
78+
79+
accountId := data.Get(SchemaAccountId).(string)
80+
cloudauthAccountFeature, errStatus, err := client.CreateOrUpdateCloudauthAccountFeatureSecure(
81+
ctx, accountId, data.Get(SchemaType).(string), cloudauthAccountFeatureFromResourceData(data))
82+
if err != nil {
83+
return diag.Errorf("Error creating resource: %s %s", errStatus, err)
84+
}
85+
86+
// using tuple 'accountId/featureType' as TF resource identifier
87+
data.SetId(accountId + "/" + cloudauthAccountFeature.GetType().String())
88+
err = data.Set(SchemaAccountId, accountId)
89+
if err != nil {
90+
return diag.FromErr(err)
91+
}
92+
93+
return nil
94+
}
95+
96+
func resourceSysdigSecureCloudauthAccountFeatureRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
97+
client, err := getSecureCloudauthAccountFeatureClient((meta.(SysdigClients)))
98+
if err != nil {
99+
return diag.FromErr(err)
100+
}
101+
102+
cloudauthAccountFeature, errStatus, err := client.GetCloudauthAccountFeatureSecure(
103+
ctx, data.Get(SchemaAccountId).(string), data.Get(SchemaType).(string))
104+
105+
if err != nil {
106+
if strings.Contains(errStatus, "404") {
107+
return nil
108+
}
109+
return diag.Errorf("Error reading resource: %s %s", errStatus, err)
110+
}
111+
112+
err = cloudauthAccountFeatureToResourceData(data, cloudauthAccountFeature)
113+
if err != nil {
114+
return diag.FromErr(err)
115+
}
116+
117+
return nil
118+
}
119+
120+
func resourceSysdigSecureCloudauthAccountFeatureUpdate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
121+
client, err := getSecureCloudauthAccountFeatureClient(meta.(SysdigClients))
122+
if err != nil {
123+
return diag.FromErr(err)
124+
}
125+
126+
accountId := data.Get(SchemaAccountId).(string)
127+
existingCloudAccountFeature, errStatus, err := client.GetCloudauthAccountFeatureSecure(
128+
ctx, accountId, data.Get(SchemaType).(string))
129+
if err != nil {
130+
if strings.Contains(errStatus, "404") {
131+
return nil
132+
}
133+
return diag.Errorf("Error reading resource: %s %s", errStatus, err)
134+
}
135+
136+
newCloudAccountFeature := cloudauthAccountFeatureFromResourceData(data)
137+
138+
// validate and reject non-updatable resource schema fields upfront
139+
err = validateCloudauthAccountFeatureUpdate(existingCloudAccountFeature, newCloudAccountFeature)
140+
if err != nil {
141+
return diag.Errorf("Error updating resource: %s", err)
142+
}
143+
144+
_, errStatus, err = client.CreateOrUpdateCloudauthAccountFeatureSecure(
145+
ctx, accountId, data.Get(SchemaType).(string), newCloudAccountFeature)
146+
if err != nil {
147+
if strings.Contains(errStatus, "404") {
148+
return nil
149+
}
150+
return diag.Errorf("Error updating resource: %s %s", errStatus, err)
151+
}
152+
153+
return nil
154+
}
155+
156+
func resourceSysdigSecureCloudauthAccountFeatureDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
157+
client, err := getSecureCloudauthAccountFeatureClient(meta.(SysdigClients))
158+
if err != nil {
159+
return diag.FromErr(err)
160+
}
161+
162+
errStatus, err := client.DeleteCloudauthAccountFeatureSecure(
163+
ctx, data.Get(SchemaAccountId).(string), data.Get(SchemaType).(string))
164+
if err != nil {
165+
if strings.Contains(errStatus, "404") {
166+
return nil
167+
}
168+
return diag.Errorf("Error deleting resource: %s %s", errStatus, err)
169+
}
170+
171+
return nil
172+
}
173+
174+
/*
175+
This function validates and restricts any fields not allowed to be updated during resource updates.
176+
*/
177+
func validateCloudauthAccountFeatureUpdate(existingFeature *v2.CloudauthAccountFeatureSecure, newFeature *v2.CloudauthAccountFeatureSecure) error {
178+
if existingFeature.Type != newFeature.Type {
179+
errorInvalidResourceUpdate := fmt.Sprintf("Bad Request. Updating restricted fields not allowed: %s", []string{"type"})
180+
return errors.New(errorInvalidResourceUpdate)
181+
}
182+
183+
return nil
184+
}
185+
186+
func getFeatureComponentsList(data *schema.ResourceData) []string {
187+
componentsList := []string{}
188+
componentsResourceList := data.Get(SchemaComponents).([]interface{})
189+
for _, componentID := range componentsResourceList {
190+
componentsList = append(componentsList, componentID.(string))
191+
}
192+
return componentsList
193+
}
194+
195+
func getFeatureFlags(data *schema.ResourceData) map[string]string {
196+
featureFlags := map[string]string{}
197+
flagsResource := data.Get(SchemaFeatureFlags).(map[string]interface{})
198+
for name, value := range flagsResource {
199+
featureFlags[name] = value.(string)
200+
}
201+
return featureFlags
202+
}
203+
204+
func cloudauthAccountFeatureFromResourceData(data *schema.ResourceData) *v2.CloudauthAccountFeatureSecure {
205+
cloudAccountFeature := &v2.CloudauthAccountFeatureSecure{
206+
AccountFeature: cloudauth.AccountFeature{
207+
Type: cloudauth.Feature(cloudauth.Feature_value[data.Get(SchemaType).(string)]),
208+
Enabled: data.Get(SchemaEnabled).(bool),
209+
Components: getFeatureComponentsList(data),
210+
Flags: getFeatureFlags(data),
211+
},
212+
}
213+
214+
return cloudAccountFeature
215+
}
216+
217+
func cloudauthAccountFeatureToResourceData(data *schema.ResourceData, cloudAccountFeature *v2.CloudauthAccountFeatureSecure) error {
218+
219+
accountId := data.Get(SchemaAccountId).(string)
220+
data.SetId(accountId + "/" + cloudAccountFeature.GetType().String())
221+
222+
err := data.Set(SchemaAccountId, accountId)
223+
if err != nil {
224+
return err
225+
}
226+
227+
err = data.Set(SchemaType, cloudAccountFeature.GetType().String())
228+
if err != nil {
229+
return err
230+
}
231+
232+
err = data.Set(SchemaEnabled, cloudAccountFeature.GetEnabled())
233+
if err != nil {
234+
return err
235+
}
236+
237+
err = data.Set(SchemaComponents, cloudAccountFeature.GetComponents())
238+
if err != nil {
239+
return err
240+
}
241+
242+
err = data.Set(SchemaFeatureFlags, cloudAccountFeature.GetFlags())
243+
if err != nil {
244+
return err
245+
}
246+
247+
return nil
248+
}

0 commit comments

Comments
 (0)