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

Add OPA support for policy sets #691

Merged
merged 10 commits into from Nov 28, 2022
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,8 @@
FEATURES:
* r/tfe_workspace: Add preemptive check for resources under management when `force_delete` attribute is false ([#699](https://github.com/hashicorp/terraform-provider-tfe/pull/699))
* r/tfe_policy: Add OPA support for policies. `tfe_policy` is a new resource that supports both Sentinel as well as OPA policies. `tfe_sentinel_policy` now includes a deprecation warning. ([#690](https://github.com/hashicorp/terraform-provider-tfe/pull/690))
* r/tfe_policy_set: Add OPA support for policy sets. ([#691](https://github.com/hashicorp/terraform-provider-tfe/pull/691))
* d/tfe_policy_set: Add optional `kind` and `overridable` fields for OPA policy sets ([#691](https://github.com/hashicorp/terraform-provider-tfe/pull/691))

## v0.39.0 (November 18, 2022)

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -12,7 +12,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
github.com/hashicorp/go-slug v0.10.1
github.com/hashicorp/go-tfe v1.13.0
github.com/hashicorp/go-tfe v1.14.0
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce
github.com/hashicorp/hcl/v2 v2.15.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -163,8 +163,8 @@ github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-slug v0.10.1 h1:05SCRWCBpCxOeP7stQHvMgOz0raCBCekaytu8Rg/RZ4=
github.com/hashicorp/go-slug v0.10.1/go.mod h1:Ib+IWBYfEfJGI1ZyXMGNbu2BU+aa3Dzu41RKLH301v4=
github.com/hashicorp/go-tfe v1.13.0 h1:Z8pJrmN9BV5EncnFRmQmLjhP7pHMNXkC2VkCQXWxKjM=
github.com/hashicorp/go-tfe v1.13.0/go.mod h1:thYtIxtgBpDDNdf/2yYPdBJ94Fz5yT5XCNZvGtTGHAU=
github.com/hashicorp/go-tfe v1.14.0 h1:FZKKkwlyTxw8/OE3e7NiFQLcgGXTHra9ogGhMTotxh8=
github.com/hashicorp/go-tfe v1.14.0/go.mod h1:77snluBqtTTvMrY0w/mxQA5jlHQ8NT44AqQ8UdrPf0o=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
Expand Down
21 changes: 21 additions & 0 deletions tfe/data_source_policy_set.go
Expand Up @@ -33,6 +33,18 @@ func dataSourceTFEPolicySet() *schema.Resource {
Computed: true,
},

"kind": {
mrinalirao marked this conversation as resolved.
Show resolved Hide resolved
Description: "The policy-as-code framework for the policy. Valid values are sentinel and opa",
Type: schema.TypeString,
Optional: true,
},

"overridable": {
Description: "Whether users can override this policy when it fails during a run. Only valid for OPA policies",
Type: schema.TypeBool,
Optional: true,
},

"policies_path": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -100,6 +112,7 @@ func dataSourceTFEPolicySetRead(d *schema.ResourceData, meta interface{}) error
}

for _, policySet := range policySetList.Items {
// nolint: nestif
if policySet.Name == name {
d.Set("name", policySet.Name)
d.Set("description", policySet.Description)
Expand All @@ -110,6 +123,14 @@ func dataSourceTFEPolicySetRead(d *schema.ResourceData, meta interface{}) error
d.Set("organization", policySet.Organization.Name)
}

if policySet.Kind != "" {
d.Set("kind", policySet.Kind)
}

if policySet.Overridable != nil {
d.Set("overridable", policySet.Overridable)
}

var vcsRepo []interface{}
if policySet.VCSRepo != nil {
vcsRepo = append(vcsRepo, map[string]interface{}{
Expand Down
72 changes: 72 additions & 0 deletions tfe/data_source_policy_set_test.go
Expand Up @@ -50,6 +50,49 @@ func TestAccTFEPolicySetDataSource_basic(t *testing.T) {
)
}

func TestAccTFEPolicySetDataSourceOPA_basic(t *testing.T) {
skipUnlessBeta(t)
tfeClient, err := getClientUsingEnv()
if err != nil {
t.Fatal(err)
}

org, orgCleanup := createBusinessOrganization(t, tfeClient)
t.Cleanup(orgCleanup)

rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccTFEPolicySetDataSourceConfigOPA_basic(org.Name, rInt),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("data.tfe_policy_set.bar", "id"),
resource.TestCheckResourceAttr(
"data.tfe_policy_set.bar", "name", fmt.Sprintf("tst-policy-set-%d", rInt)),
resource.TestCheckResourceAttr(
"data.tfe_policy_set.bar", "description", "Policy Set"),
resource.TestCheckResourceAttr(
"data.tfe_policy_set.bar", "global", "false"),
resource.TestCheckResourceAttr(
"data.tfe_policy_set.bar", "organization", org.Name),
resource.TestCheckResourceAttr(
"data.tfe_policy_set.bar", "kind", "opa"),
resource.TestCheckResourceAttr(
"data.tfe_policy_set.bar", "overridable", "true"),
resource.TestCheckResourceAttr(
"data.tfe_policy_set.bar", "workspace_ids.#", "1"),
resource.TestCheckResourceAttr(
"data.tfe_policy_set.bar", "vcs_repo.#", "0"),
),
},
},
},
)
}

func TestAccTFEPolicySetDataSource_vcs(t *testing.T) {
tfeClient, err := getClientUsingEnv()
if err != nil {
Expand Down Expand Up @@ -90,6 +133,8 @@ func TestAccTFEPolicySetDataSource_vcs(t *testing.T) {
"data.tfe_policy_set.bar", "description", "Policy Set"),
resource.TestCheckResourceAttr(
"data.tfe_policy_set.bar", "global", "false"),
resource.TestCheckResourceAttr(
"data.tfe_policy_set.bar", "kind", "sentinel"),
resource.TestCheckResourceAttr(
"data.tfe_policy_set.bar", "organization", org.Name),
resource.TestCheckResourceAttr(
Expand Down Expand Up @@ -152,6 +197,33 @@ data "tfe_policy_set" "bar" {
}`, organization, rInt, rInt)
}

func testAccTFEPolicySetDataSourceConfigOPA_basic(organization string, rInt int) string {
return fmt.Sprintf(`
locals {
organization_name = "%s"
}

resource "tfe_workspace" "foobar" {
name = "workspace-foo-%d"
organization = local.organization_name
}

resource "tfe_policy_set" "foobar" {
name = "tst-policy-set-%d"
description = "Policy Set"
organization = local.organization_name
kind = "opa"
overridable = true
workspace_ids = [tfe_workspace.foobar.id]
}

data "tfe_policy_set" "bar" {
name = tfe_policy_set.foobar.name
organization = local.organization_name
kind = "opa"
}`, organization, rInt, rInt)
}

func testAccTFEPolicySetDataSourceConfig_vcs(organization string, rInt int) string {
return fmt.Sprintf(`
locals {
Expand Down
42 changes: 41 additions & 1 deletion tfe/resource_tfe_policy_set.go
Expand Up @@ -46,6 +46,24 @@ func resourceTFEPolicySet() *schema.Resource {
ConflictsWith: []string{"workspace_ids"},
},

"kind": {
Type: schema.TypeString,
Optional: true,
Default: string(tfe.Sentinel),
ForceNew: true,
ValidateFunc: validation.StringInSlice(
[]string{
string(tfe.OPA),
string(tfe.Sentinel),
}, false),
},

"overridable": {
Type: schema.TypeBool,
Optional: true,
Default: false,
mrinalirao marked this conversation as resolved.
Show resolved Hide resolved
},

"policies_path": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -123,6 +141,14 @@ func resourceTFEPolicySetCreate(d *schema.ResourceData, meta interface{}) error
}

// Process all configured options.
if vKind, ok := d.GetOk("kind"); ok {
options.Kind = tfe.PolicyKind(vKind.(string))
}

if vOverridable, ok := d.GetOk("overridable"); ok {
options.Overridable = tfe.Bool(vOverridable.(bool))
}

if desc, ok := d.GetOk("description"); ok {
options.Description = tfe.String(desc.(string))
}
Expand Down Expand Up @@ -199,6 +225,15 @@ func resourceTFEPolicySetRead(d *schema.ResourceData, meta interface{}) error {
d.Set("organization", policySet.Organization.Name)
}

// Note: Old API endpoints return an empty string, so use the default in the schema
if policySet.Kind != "" {
d.Set("kind", policySet.Kind)
}

if policySet.Overridable != nil {
d.Set("overridable", policySet.Overridable)
}

// Set VCS policy set options.
var vcsRepo []interface{}
if policySet.VCSRepo != nil {
Expand Down Expand Up @@ -271,7 +306,7 @@ func resourceTFEPolicySetUpdate(d *schema.ResourceData, meta interface{}) error
}

// Don't bother updating the policy set's attributes if they haven't changed
if d.HasChange("name") || d.HasChange("description") || d.HasChange("global") || d.HasChange("vcs_repo") {
if d.HasChange("name") || d.HasChange("description") || d.HasChange("global") || d.HasChange("vcs_repo") || d.HasChange("overridable") {
// Create a new options struct.
options := tfe.PolicySetUpdateOptions{
Name: tfe.String(name),
Expand All @@ -282,6 +317,11 @@ func resourceTFEPolicySetUpdate(d *schema.ResourceData, meta interface{}) error
options.Description = tfe.String(desc.(string))
}

if d.HasChange("overridable") {
o := d.Get("overridable").(bool)
options.Overridable = tfe.Bool(o)
}

if v, ok := d.GetOk("vcs_repo"); ok {
vcsRepo := v.([]interface{})[0].(map[string]interface{})

Expand Down