From 375715e0dad59aca1ed49f6a1c893b69b0b1cb59 Mon Sep 17 00:00:00 2001 From: Deepak Gupta Date: Mon, 9 Dec 2024 16:18:27 -0500 Subject: [PATCH] use gRPC to implement policy sets --- cyral/internal/policy/v2/model.go | 7 - cyral/internal/policyset/datasource.go | 22 +-- cyral/internal/policyset/model.go | 199 ++++++++++++++----------- cyral/internal/policyset/resource.go | 39 ++--- 4 files changed, 126 insertions(+), 141 deletions(-) diff --git a/cyral/internal/policy/v2/model.go b/cyral/internal/policy/v2/model.go index 01d07330..3ad70f19 100644 --- a/cyral/internal/policy/v2/model.go +++ b/cyral/internal/policy/v2/model.go @@ -13,13 +13,6 @@ import ( "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) -// ChangeInfo represents information about changes to the policy -type ChangeInfo struct { - Actor string `json:"actor,omitempty"` - ActorType string `json:"actorType,omitempty"` - Timestamp string `json:"timestamp,omitempty"` -} - // changeInfoToMap converts ChangeInfo to a map func changeInfoToMap(c *msg.ChangeInfo) map[string]interface{} { return map[string]interface{}{ diff --git a/cyral/internal/policyset/datasource.go b/cyral/internal/policyset/datasource.go index b0c2f2ae..844c5077 100644 --- a/cyral/internal/policyset/datasource.go +++ b/cyral/internal/policyset/datasource.go @@ -1,34 +1,22 @@ package policyset import ( - "fmt" - "net/url" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/cyralinc/terraform-provider-cyral/cyral/client" "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/resourcetype" ) -var dsContextHandler = core.HTTPContextHandler{ - ResourceName: dataSourceName, - ResourceType: resourcetype.DataSource, - SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &PolicySet{} }, - ReadUpdateDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { - baseURL := &url.URL{ - Scheme: "https", - Host: c.ControlPlane, - Path: fmt.Sprintf("%s/%s", apiPathPolicySet, d.Get("id").(string)), - } - return baseURL.String() - }, +var dsContextHandler = core.ContextHandler{ + ResourceName: dataSourceName, + ResourceType: resourcetype.DataSource, + Read: readPolicySet, } func dataSourceSchema() *schema.Resource { return &schema.Resource{ Description: "This data source provides information about a policy set.", - ReadContext: dsContextHandler.ReadContext(), + ReadContext: dsContextHandler.ReadContext, Schema: map[string]*schema.Schema{ "id": { Description: "Identifier for the policy set.", diff --git a/cyral/internal/policyset/model.go b/cyral/internal/policyset/model.go index cdfc4dcc..fc1941f8 100644 --- a/cyral/internal/policyset/model.go +++ b/cyral/internal/policyset/model.go @@ -1,147 +1,168 @@ package policyset import ( + "context" "fmt" + "time" + methods "buf.build/gen/go/cyral/policy/grpc/go/policy/v1/policyv1grpc" + msg "buf.build/gen/go/cyral/policy/protocolbuffers/go/policy/v1" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/cyralinc/terraform-provider-cyral/cyral/client" "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) -// ChangeInfo represents information about changes to the policy set -type ChangeInfo struct { - Actor string `json:"actor,omitempty"` - ActorType string `json:"actorType,omitempty"` - Timestamp string `json:"timestamp,omitempty"` -} - -// ToMap converts ChangeInfo to a map -func (c ChangeInfo) ToMap() map[string]interface{} { +// ToMap converts PolicySetPolicy to a map +func policySetPolicyToMap(p *msg.PolicySetPolicy) map[string]interface{} { return map[string]interface{}{ - "actor": c.Actor, - "actor_type": c.ActorType, - "timestamp": c.Timestamp, + "type": p.GetType().String(), + "id": p.GetId(), } } -// PolicySetPolicy represents a policy in the policy set -type PolicySetPolicy struct { - Type string `json:"type,omitempty"` - ID string `json:"id,omitempty"` +func policiesToMaps(policies []*msg.PolicySetPolicy) []map[string]interface{} { + var result []map[string]interface{} + for _, policy := range policies { + result = append(result, policySetPolicyToMap(policy)) + } + return result } -// ToMap converts PolicySetPolicy to a map -func (p PolicySetPolicy) ToMap() map[string]interface{} { +// changeInfoToMap converts ChangeInfo to a map +func changeInfoToMap(c *msg.ChangeInfo) map[string]interface{} { return map[string]interface{}{ - "type": p.Type, - "id": p.ID, + "actor": c.GetActor(), + "actor_type": c.GetActorType().String(), + "timestamp": c.GetTimestamp().AsTime().Format(time.RFC3339), } } -// Scope represents the scope of the policy set -type Scope struct { - RepoIds []string `json:"repoIds,omitempty"` -} - -// ToMap converts Scope to a list of maps -func (s *Scope) ToMap() []map[string]interface{} { +// scopeToMap converts Scope to a list of maps +func scopeToMap(s *msg.Scope) []map[string]interface{} { return []map[string]interface{}{ { - "repo_ids": s.RepoIds, + "repo_ids": s.GetRepoIds(), }, } } -// PolicySet represents the policy set details -type PolicySet struct { - ID string `json:"id,omitempty"` - WizardID string `json:"wizardId,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Tags []string `json:"tags,omitempty"` - Scope *Scope `json:"scope,omitempty"` - WizardParameters string `json:"wizardParameters,omitempty"` - Enabled bool `json:"enabled,omitempty"` - Policies []PolicySetPolicy `json:"policies,omitempty"` - LastUpdated ChangeInfo `json:"lastUpdated,omitempty"` - Created ChangeInfo `json:"created,omitempty"` -} - -// WriteToSchema writes the policy set data to the schema -func (r *PolicySet) WriteToSchema(d *schema.ResourceData) error { - if err := d.Set("id", r.ID); err != nil { +// updateSchema writes the policy set data to the schema +func updateSchema(ps *msg.PolicySet, d *schema.ResourceData) error { + if err := d.Set("id", ps.GetId()); err != nil { return fmt.Errorf("error setting 'id' field: %w", err) } - if err := d.Set("wizard_id", r.WizardID); err != nil { - return fmt.Errorf("error setting 'wizard_id' field: %w", err) + if err := d.Set("wizard_id", ps.GetWizardId()); err != nil { + return fmt.Errorf("error setting 'id' field: %w", err) } - if err := d.Set("name", r.Name); err != nil { + if err := d.Set("name", ps.GetName()); err != nil { return fmt.Errorf("error setting 'name' field: %w", err) } - if err := d.Set("description", r.Description); err != nil { + if err := d.Set("description", ps.GetDescription()); err != nil { return fmt.Errorf("error setting 'description' field: %w", err) } - if err := d.Set("tags", r.Tags); err != nil { - return fmt.Errorf("error setting 'tags' field: %w", err) + if err := d.Set("enabled", ps.GetEnabled()); err != nil { + return fmt.Errorf("error setting 'enabled' field: %w", err) } - if err := d.Set("wizard_parameters", r.WizardParameters); err != nil { - return fmt.Errorf("error setting 'wizard_parameters' field: %w", err) + if err := d.Set("tags", ps.GetTags()); err != nil { + return fmt.Errorf("error setting 'tags' field: %w", err) } - if err := d.Set("enabled", r.Enabled); err != nil { - return fmt.Errorf("error setting 'enabled' field: %w", err) + if err := d.Set("wizard_parameters", ps.GetWizardParameters()); err != nil { + return fmt.Errorf("error setting 'document' field: %w", err) } - if err := d.Set("policies", policiesToMaps(r.Policies)); err != nil { + + if err := d.Set("policies", policiesToMaps(ps.GetPolicies())); err != nil { return fmt.Errorf("error setting 'policies' field: %w", err) } - if err := d.Set("last_updated", r.LastUpdated.ToMap()); err != nil { + if ps.GetScope() != nil { + if err := d.Set("scope", scopeToMap(ps.GetScope())); err != nil { + return fmt.Errorf("error setting 'scope' field: %w", err) + } + } + // Use the changeInfoToMap method to set the last_updated and created fields + if err := d.Set("last_updated", changeInfoToMap(ps.GetLastUpdated())); err != nil { return fmt.Errorf("error setting 'last_updated' field: %w", err) } - if err := d.Set("created", r.Created.ToMap()); err != nil { + if err := d.Set("created", changeInfoToMap(ps.GetCreated())); err != nil { return fmt.Errorf("error setting 'created' field: %w", err) } - if r.Scope != nil { - if err := d.Set("scope", r.Scope.ToMap()); err != nil { - return fmt.Errorf("error setting 'scope' field: %w", err) - } - } - d.SetId(r.ID) + d.SetId(ps.GetId()) return nil } -func policiesToMaps(policies []PolicySetPolicy) []map[string]interface{} { - var result []map[string]interface{} - for _, policy := range policies { - result = append(result, policy.ToMap()) +func policySetFromSchema(d *schema.ResourceData) *msg.PolicySet { + p := &msg.PolicySet{ + Id: d.Get("id").(string), + Name: d.Get("name").(string), + Description: d.Get("description").(string), + Enabled: d.Get("enabled").(bool), + Tags: utils.ConvertFromInterfaceList[string](d.Get("tags").([]interface{})), + WizardId: d.Get("wizard_id").(string), + WizardParameters: d.Get("wizard_parameters").(string), } - return result -} -// ReadFromSchema reads the policy set data from the schema -func (r *PolicySet) ReadFromSchema(d *schema.ResourceData) error { - r.ID = d.Get("id").(string) - r.WizardID = d.Get("wizard_id").(string) - r.Name = d.Get("name").(string) - r.Description = d.Get("description").(string) - r.Tags = utils.ConvertFromInterfaceList[string](d.Get("tags").([]interface{})) - r.WizardParameters = d.Get("wizard_parameters").(string) - r.Enabled = d.Get("enabled").(bool) if v, ok := d.GetOk("scope"); ok { - r.Scope = scopeFromInterface(v.([]interface{})) + p.Scope = scopeFromInterface(v.([]interface{})) } - return nil + return p } // scopeFromInterface converts the map to a Scope struct -func scopeFromInterface(s []interface{}) *Scope { +func scopeFromInterface(s []interface{}) *msg.Scope { if len(s) == 0 || s[0] == nil { - // return an empty scope (ie a scope with a repo ids array of length 0) - return &Scope{ - RepoIds: []string{}, - } + return nil } m := s[0].(map[string]interface{}) - scope := Scope{ + return &msg.Scope{ RepoIds: utils.ConvertFromInterfaceList[string](m["repo_ids"].([]interface{})), } - return &scope +} + +func createPolicySet(ctx context.Context, cl *client.Client, rd *schema.ResourceData) error { + ps := policySetFromSchema(rd) + req := &msg.CreatePolicySetRequest{ + PolicySet: ps, + } + grpcClient := methods.NewPolicyWizardServiceClient(cl.GRPCClient()) + resp, err := grpcClient.CreatePolicySet(ctx, req) + if err != nil { + return err + } + rd.SetId(resp.GetPolicySet().GetId()) + return nil +} + +func readPolicySet(ctx context.Context, cl *client.Client, rd *schema.ResourceData) error { + req := &msg.ReadPolicySetRequest{ + Id: rd.Get("id").(string), + } + grpcClient := methods.NewPolicyWizardServiceClient(cl.GRPCClient()) + resp, err := grpcClient.ReadPolicySet(ctx, req) + if err != nil { + return err + } + return updateSchema(resp.GetPolicySet(), rd) +} + +func updatePolicySet(ctx context.Context, cl *client.Client, rd *schema.ResourceData) error { + ps := policySetFromSchema(rd) + req := &msg.UpdatePolicySetRequest{ + Id: ps.GetId(), + PolicySet: ps, + } + grpcClient := methods.NewPolicyWizardServiceClient(cl.GRPCClient()) + resp, err := grpcClient.UpdatePolicySet(ctx, req) + if err != nil { + return err + } + return updateSchema(resp.GetPolicySet(), rd) +} + +func deletePolicySet(ctx context.Context, cl *client.Client, rd *schema.ResourceData) error { + req := &msg.DeletePolicySetRequest{ + Id: rd.Get("id").(string), + } + grpcClient := methods.NewPolicyWizardServiceClient(cl.GRPCClient()) + _, err := grpcClient.DeletePolicySet(ctx, req) + return err } diff --git a/cyral/internal/policyset/resource.go b/cyral/internal/policyset/resource.go index 0031a663..459d0a1c 100644 --- a/cyral/internal/policyset/resource.go +++ b/cyral/internal/policyset/resource.go @@ -2,46 +2,29 @@ package policyset import ( "context" - "fmt" - "net/url" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/cyralinc/terraform-provider-cyral/cyral/client" "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/resourcetype" ) -var resourceContextHandler = core.HTTPContextHandler{ - ResourceName: resourceName, - ResourceType: resourcetype.Resource, - SchemaReaderFactory: func() core.SchemaReader { return &PolicySet{} }, - SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &PolicySet{} }, - BaseURLFactory: func(d *schema.ResourceData, c *client.Client) string { - baseURL := &url.URL{ - Scheme: "https", - Host: c.ControlPlane, - Path: apiPathPolicySet, - } - return baseURL.String() - }, - ReadUpdateDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { - baseURL := &url.URL{ - Scheme: "https", - Host: c.ControlPlane, - Path: fmt.Sprintf("%s/%s", apiPathPolicySet, d.Id()), - } - return baseURL.String() - }, +var resourceContextHandler = core.ContextHandler{ + ResourceName: resourceName, + ResourceType: resourcetype.Resource, + Create: createPolicySet, + Read: readPolicySet, + Update: updatePolicySet, + Delete: deletePolicySet, } func resourceSchema() *schema.Resource { return &schema.Resource{ Description: "This resource allows management of policy sets in the Cyral platform.", - CreateContext: resourceContextHandler.CreateContext(), - ReadContext: resourceContextHandler.ReadContext(), - UpdateContext: resourceContextHandler.UpdateContext(), - DeleteContext: resourceContextHandler.DeleteContext(), + CreateContext: resourceContextHandler.CreateContext, + ReadContext: resourceContextHandler.ReadContext, + UpdateContext: resourceContextHandler.UpdateContext, + DeleteContext: resourceContextHandler.DeleteContext, Importer: &schema.ResourceImporter{ StateContext: importPolicySetStateContext, },