Skip to content

Commit

Permalink
Feat: Add project_policy resource and data source (#151)
Browse files Browse the repository at this point in the history
* feat: add policy resource

* feat: create resource schema

* feat: stub resource handlers

* feat: resource (wip)

* feat: policy resource update

* feat: policy data source

* fix: policy data source

* feat: policy provider (wip)

* fix: policy data source (wip)

* test: data policy (broken)

* fix: id required

* test: fix broken data source test

* test: integration (wip)

* test: number of environments

* test: num environments

* fix: add project_id to resource

* fix: minor

* fix: complete models

* test: scaffold unit test for policy resource

* test: failing unit test for policy resource

* fix: policy resource

* fix: handle zeroes for number of environments

* test: fix case on error

* fix: support null number of environments

* refactor: remove unreachable error

* refactor: remove redundant code

* refactor: clean up errors

* refactor: remove unnecessary validation

* test: all policy attributes

* refactor: policy > project_policy

* test: fix expected error

* test: fix resource name in integration test

* fix: review comments

* test: fix resource spec test example

* fix: disallow zero for number of environments

* fix: handling of creating policy

* fix: validate project id is not empty
  • Loading branch information
bernot-dev authored Nov 11, 2021
1 parent 81f8649 commit 75ab01d
Show file tree
Hide file tree
Showing 11 changed files with 596 additions and 9 deletions.
54 changes: 45 additions & 9 deletions client/model.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package client

import "encoding/json"

type Organization struct {
Id string `json:"id"`
Name string `json:"name"`
Expand Down Expand Up @@ -241,22 +243,56 @@ type TeamProjectAssignment struct {
}

type Policy struct {
Id string `json:"id"`
ProjectId string `json:"projectId"`
NumberOfEnvironments int `json:"numberOfEnvironments"`
NumberOfEnvironmentsPerProject int `json:"numberOfEnvironmentsPerProject"`
RequiresApprovalDefault bool `json:"requiresApprovalDefault"`
IncludeCostEstimation bool `json:"includeCostEstimation"`
SkipApplyWhenPlanIsEmpty bool `json:"skipApplyWhenPlanIsEmpty"`
DisableDestroyEnvironments bool `json:"disableDestroyEnvironments"`
UpdatedBy string `json:"updatedBy"`
Id string `json:"id"`
ProjectId string `json:"projectId"`
NumberOfEnvironments int `json:"numberOfEnvironments"`
NumberOfEnvironmentsTotal int `json:"numberOfEnvironmentsTotal"`
RequiresApprovalDefault bool `json:"requiresApprovalDefault"`
IncludeCostEstimation bool `json:"includeCostEstimation"`
SkipApplyWhenPlanIsEmpty bool `json:"skipApplyWhenPlanIsEmpty"`
DisableDestroyEnvironments bool `json:"disableDestroyEnvironments"`
SkipRedundantDepolyments bool `json:"skipRedundantDeployments"`
UpdatedBy string `json:"updatedBy"`
}

type PolicyUpdatePayload struct {
ProjectId string `json:"projectId"`
NumberOfEnvironments int `json:"numberOfEnvironments"`
NumberOfEnvironmentsTotal int `json:"numberOfEnvironmentsTotal"`
RequiresApprovalDefault bool `json:"requiresApprovalDefault"`
IncludeCostEstimation bool `json:"includeCostEstimation"`
SkipApplyWhenPlanIsEmpty bool `json:"skipApplyWhenPlanIsEmpty"`
DisableDestroyEnvironments bool `json:"disableDestroyEnvironments"`
SkipRedundantDepolyments bool `json:"skipRedundantDeployments"`
}

func (p PolicyUpdatePayload) MarshalJSON() ([]byte, error) {
type serial struct {
ProjectId string `json:"projectId"`
NumberOfEnvironments *int `json:"numberOfEnvironments"`
NumberOfEnvironmentsTotal *int `json:"numberOfEnvironmentsTotal"`
RequiresApprovalDefault bool `json:"requiresApprovalDefault"`
IncludeCostEstimation bool `json:"includeCostEstimation"`
SkipApplyWhenPlanIsEmpty bool `json:"skipApplyWhenPlanIsEmpty"`
DisableDestroyEnvironments bool `json:"disableDestroyEnvironments"`
SkipRedundantDepolyments bool `json:"skipRedundantDeployments"`
}

s := serial{
ProjectId: p.ProjectId,
RequiresApprovalDefault: p.RequiresApprovalDefault,
IncludeCostEstimation: p.IncludeCostEstimation,
SkipApplyWhenPlanIsEmpty: p.SkipApplyWhenPlanIsEmpty,
DisableDestroyEnvironments: p.DisableDestroyEnvironments,
SkipRedundantDepolyments: p.SkipRedundantDepolyments,
}

if p.NumberOfEnvironments != 0 {
s.NumberOfEnvironments = &p.NumberOfEnvironments
}
if p.NumberOfEnvironmentsTotal != 0 {
s.NumberOfEnvironmentsTotal = &p.NumberOfEnvironmentsTotal
}

return json.Marshal(s)
}
File renamed without changes.
File renamed without changes.
93 changes: 93 additions & 0 deletions env0/data_project_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package env0

import (
"context"

"github.com/env0/terraform-provider-env0/client"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataPolicy() *schema.Resource {
return &schema.Resource{
ReadContext: dataPolicyRead,

Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Description: "id of the policy",
Computed: true,
},
"project_id": {
Type: schema.TypeString,
Description: "id of the project",
Required: true,
},
"number_of_environments": {
Type: schema.TypeInt,
Description: "number of environments per project",
Computed: true,
},
"number_of_environments_total": {
Type: schema.TypeInt,
Description: "number of environments total",
Computed: true,
},
"requires_approval_default": {
Type: schema.TypeBool,
Description: "requires approval",
Computed: true,
},
"include_cost_estimation": {
Type: schema.TypeBool,
Description: "include cost estimation",
Computed: true,
},
"skip_apply_when_plan_is_empty": {
Type: schema.TypeBool,
Description: "skip apply when plan is empty",
Computed: true,
},
"disable_destroy_environments": {
Type: schema.TypeBool,
Description: "disable destroy environments",
Computed: true,
},
"skip_redundant_deployments": {
Type: schema.TypeBool,
Description: "skip redundant deployments",
Computed: true,
},
"updated_by": {
Type: schema.TypeString,
Description: "updated by",
Computed: true,
},
},
}
}

func dataPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var err diag.Diagnostics
var policy client.Policy

projectId, ok := d.GetOk("project_id")
if ok {
policy, err = getPolicyByProjectId(projectId.(string), meta)
if err != nil {
return err
}
}
d.SetId(policy.Id)
setPolicySchema(d, policy)
return nil
}

func getPolicyByProjectId(projectId string, meta interface{}) (client.Policy, diag.Diagnostics) {
apiClient := meta.(client.ApiClientInterface)
policy, err := apiClient.Policy(projectId)
if err != nil {
return client.Policy{}, diag.Errorf("Could not query policy: %v", err)
}
return policy, nil
}
54 changes: 54 additions & 0 deletions env0/data_project_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package env0

import (
"strconv"
"testing"

"github.com/env0/terraform-provider-env0/client"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestPolicyDataSource(t *testing.T) {
policy := client.Policy{
Id: "id0",
ProjectId: "project0",
NumberOfEnvironments: 1,
NumberOfEnvironmentsTotal: 2,
RequiresApprovalDefault: true,
IncludeCostEstimation: true,
SkipApplyWhenPlanIsEmpty: true,
DisableDestroyEnvironments: true,
}

resourceType := "env0_project_policy"
resourceName := "test_policy"
accessor := dataSourceAccessor(resourceType, resourceName)

getValidTestCase := func(input map[string]interface{}) resource.TestCase {
return resource.TestCase{
Steps: []resource.TestStep{
{
Config: dataSourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"project_id": policy.ProjectId,
}),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "id", policy.Id),
resource.TestCheckResourceAttr(accessor, "project_id", policy.ProjectId),
resource.TestCheckResourceAttr(accessor, "number_of_environments", strconv.Itoa(policy.NumberOfEnvironments)),
resource.TestCheckResourceAttr(accessor, "number_of_environments_total", strconv.Itoa(policy.NumberOfEnvironmentsTotal)),
resource.TestCheckResourceAttr(accessor, "requires_approval_default", strconv.FormatBool(policy.RequiresApprovalDefault)),
resource.TestCheckResourceAttr(accessor, "include_cost_estimation", strconv.FormatBool(policy.IncludeCostEstimation)),
resource.TestCheckResourceAttr(accessor, "skip_apply_when_plan_is_empty", strconv.FormatBool(policy.SkipApplyWhenPlanIsEmpty)),
resource.TestCheckResourceAttr(accessor, "disable_destroy_environments", strconv.FormatBool(policy.DisableDestroyEnvironments)),
),
},
},
}
}

t.Run("valid", func(t *testing.T) {
runUnitTest(t, getValidTestCase(map[string]interface{}{}), func(mock *client.MockApiClientInterface) {
mock.EXPECT().Policy(policy.ProjectId).AnyTimes().Return(policy, nil)
})
})
}
3 changes: 3 additions & 0 deletions env0/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package env0

import (
"context"

"github.com/env0/terraform-provider-env0/client"
"github.com/env0/terraform-provider-env0/client/http"
"github.com/go-resty/resty/v2"
Expand Down Expand Up @@ -38,6 +39,7 @@ func Provider(version string) plugin.ProviderFunc {
DataSourcesMap: map[string]*schema.Resource{
"env0_organization": dataOrganization(),
"env0_project": dataProject(),
"env0_project_policy": dataPolicy(),
"env0_configuration_variable": dataConfigurationVariable(),
"env0_template": dataTemplate(),
"env0_ssh_key": dataSshKey(),
Expand All @@ -46,6 +48,7 @@ func Provider(version string) plugin.ProviderFunc {
},
ResourcesMap: map[string]*schema.Resource{
"env0_project": resourceProject(),
"env0_project_policy": resourcePolicy(),
"env0_configuration_variable": resourceConfigurationVariable(),
"env0_template": resourceTemplate(),
"env0_ssh_key": resourceSshKey(),
Expand Down
Loading

0 comments on commit 75ab01d

Please sign in to comment.