Skip to content

Commit

Permalink
Merge pull request #163 from DrFaust92/workspace-var
Browse files Browse the repository at this point in the history
workspace var
  • Loading branch information
DrFaust92 authored May 13, 2023
2 parents 7fc7df0 + 8da43fb commit 4b2eb09
Show file tree
Hide file tree
Showing 4 changed files with 388 additions and 0 deletions.
1 change: 1 addition & 0 deletions bitbucket/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func Provider() *schema.Provider {
"bitbucket_repository_variable": resourceRepositoryVariable(),
"bitbucket_ssh_key": resourceSshKey(),
"bitbucket_workspace_hook": resourceWorkspaceHook(),
"bitbucket_workspace_variable": resourceWorkspaceVariable(),
},
DataSourcesMap: map[string]*schema.Resource{
"bitbucket_current_user": dataCurrentUser(),
Expand Down
168 changes: 168 additions & 0 deletions bitbucket/resource_workspace_variable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package bitbucket

import (
"context"
"fmt"
"log"
"net/http"
"strings"

"github.com/DrFaust92/bitbucket-go-client"
"github.com/antihax/optional"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceWorkspaceVariable() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceWorkspaceVariableCreate,
UpdateWithoutTimeout: resourceWorkspaceVariableUpdate,
ReadWithoutTimeout: resourceWorkspaceVariableRead,
DeleteWithoutTimeout: resourceWorkspaceVariableDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"uuid": {
Type: schema.TypeString,
Computed: true,
},
"key": {
Type: schema.TypeString,
Required: true,
},
"value": {
Type: schema.TypeString,
Required: true,
Sensitive: true,
},
"secured": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"workspace": {
Type: schema.TypeString,
Required: true,
},
},
}
}

func newWorkspaceVariableFromResource(d *schema.ResourceData) bitbucket.PipelineVariable {
dk := bitbucket.PipelineVariable{
Key: d.Get("key").(string),
Value: d.Get("value").(string),
Secured: d.Get("secured").(bool),
}
return dk
}

func resourceWorkspaceVariableCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(Clients).genClient
pipeApi := c.ApiClient.PipelinesApi
rvcr := newWorkspaceVariableFromResource(d)

workspacePipeBody := &bitbucket.PipelinesApiCreatePipelineVariableForWorkspaceOpts{
Body: optional.NewInterface(rvcr),
}

workspace := d.Get("workspace").(string)

log.Printf("[DEBUG] Workspace Variable Request: %#v", workspacePipeBody)

rvRes, res, err := pipeApi.CreatePipelineVariableForWorkspace(c.AuthContext, workspace, workspacePipeBody)

log.Printf("[DEBUG] Workspace Variable Create Request Res: %#v", res)

if err := handleClientError(err); err != nil {
return diag.FromErr(err)
}

d.SetId(fmt.Sprintf("%s/%s", workspace, rvRes.Uuid))

return resourceWorkspaceVariableRead(ctx, d, m)
}

func resourceWorkspaceVariableRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(Clients).genClient
pipeApi := c.ApiClient.PipelinesApi

workspace, uuid, err := workspaceVarId(d.Id())
if err != nil {
return diag.FromErr(err)
}

rvRes, res, err := pipeApi.GetPipelineVariableForWorkspace(c.AuthContext, workspace, uuid)

log.Printf("[DEBUG] Workspace Variable Get Request Res: %#v", res)

if res.StatusCode == http.StatusNotFound {
log.Printf("[WARN] Workspace Variable (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err := handleClientError(err); err != nil {
return diag.FromErr(err)
}

d.Set("uuid", rvRes.Uuid)
d.Set("workspace", workspace)
d.Set("key", rvRes.Key)
d.Set("secured", rvRes.Secured)

if !rvRes.Secured {
d.Set("value", rvRes.Value)
} else {
d.Set("value", d.Get("value").(string))
}

return nil
}

func resourceWorkspaceVariableUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(Clients).genClient
pipeApi := c.ApiClient.PipelinesApi

workspace, uuid, err := workspaceVarId(d.Id())
if err != nil {
return diag.FromErr(err)
}

rvcr := newWorkspaceVariableFromResource(d)

_, _, err = pipeApi.UpdatePipelineVariableForWorkspace(c.AuthContext, rvcr, workspace, uuid)
if err := handleClientError(err); err != nil {
return diag.FromErr(err)
}

return resourceWorkspaceVariableRead(ctx, d, m)
}

func resourceWorkspaceVariableDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(Clients).genClient
pipeApi := c.ApiClient.PipelinesApi

workspace, uuid, err := workspaceVarId(d.Id())
if err != nil {
return diag.FromErr(err)
}

_, err = pipeApi.DeletePipelineVariableForWorkspace(c.AuthContext, workspace, uuid)
if err := handleClientError(err); err != nil {
return diag.FromErr(err)
}

return nil
}

func workspaceVarId(workspace string) (string, string, error) {
idparts := strings.Split(workspace, "/")
if len(idparts) == 2 {
return idparts[0], idparts[1], nil
} else {
return "", "", fmt.Errorf("incorrect ID format, should match `workspace/uuid`")
}
}
175 changes: 175 additions & 0 deletions bitbucket/resource_workspace_variable_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package bitbucket

import (
"fmt"
"net/http"
"os"
"testing"

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

func TestAccBitbucketWorkspaceVariable_basic(t *testing.T) {
workspace := os.Getenv("BITBUCKET_TEAM")
resourceName := "bitbucket_workspace_variable.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBitbucketWorkspaceVariableDestroy,
Steps: []resource.TestStep{
{
Config: testAccBitbucketWorkspaceVariableConfig(workspace, "test", false),
Check: resource.ComposeTestCheckFunc(
testAccCheckBitbucketWorkspaceVariableExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "workspace", workspace),
resource.TestCheckResourceAttr(resourceName, "key", "test"),
resource.TestCheckResourceAttr(resourceName, "value", "test"),
resource.TestCheckResourceAttr(resourceName, "secured", "false"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccBitbucketWorkspaceVariableConfig(workspace, "test-2", false),
Check: resource.ComposeTestCheckFunc(
testAccCheckBitbucketWorkspaceVariableExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "workspace", workspace),
resource.TestCheckResourceAttr(resourceName, "key", "test"),
resource.TestCheckResourceAttr(resourceName, "value", "test-2"),
resource.TestCheckResourceAttr(resourceName, "secured", "false"),
),
},
},
})
}

func TestAccBitbucketWorkspaceVariable_manyVars(t *testing.T) {
workspace := os.Getenv("BITBUCKET_TEAM")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBitbucketWorkspaceVariableDestroy,
Steps: []resource.TestStep{
{
Config: testAccBitbucketWorkspaceVariableManyConfig(workspace, "test", false),
},
},
})
}

func TestAccBitbucketWorkspaceVariable_secure(t *testing.T) {
workspace := os.Getenv("BITBUCKET_TEAM")
resourceName := "bitbucket_workspace_variable.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBitbucketWorkspaceVariableDestroy,
Steps: []resource.TestStep{
{
Config: testAccBitbucketWorkspaceVariableConfig(workspace, "test", true),
Check: resource.ComposeTestCheckFunc(
testAccCheckBitbucketWorkspaceVariableExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "workspace", workspace),
resource.TestCheckResourceAttr(resourceName, "key", "test"),
resource.TestCheckResourceAttr(resourceName, "value", "test"),
resource.TestCheckResourceAttr(resourceName, "secured", "true"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"value"},
},
{
Config: testAccBitbucketWorkspaceVariableConfig(workspace, "test", false),
Check: resource.ComposeTestCheckFunc(
testAccCheckBitbucketWorkspaceVariableExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "workspace", workspace),
resource.TestCheckResourceAttr(resourceName, "key", "test"),
resource.TestCheckResourceAttr(resourceName, "value", "test"),
resource.TestCheckResourceAttr(resourceName, "secured", "false"),
),
},
{
Config: testAccBitbucketWorkspaceVariableConfig(workspace, "test", true),
Check: resource.ComposeTestCheckFunc(
testAccCheckBitbucketWorkspaceVariableExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "workspace", workspace),
resource.TestCheckResourceAttr(resourceName, "key", "test"),
resource.TestCheckResourceAttr(resourceName, "value", "test"),
resource.TestCheckResourceAttr(resourceName, "secured", "true"),
),
},
},
})
}

func testAccCheckBitbucketWorkspaceVariableDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(Clients).genClient
pipeApi := client.ApiClient.PipelinesApi
for _, rs := range s.RootModule().Resources {
if rs.Type != "bitbucket_workspace_variable" {
continue
}

workspace, uuid, err := workspaceVarId(rs.Primary.ID)
if err != nil {
return err
}

_, res, err := pipeApi.GetPipelineVariableForWorkspace(client.AuthContext, workspace, uuid)

if err == nil {
return fmt.Errorf("The resource was found should have errored")
}

if res.StatusCode != http.StatusNotFound {
return fmt.Errorf("Workspace Variable still exists")
}
}
return nil
}

func testAccCheckBitbucketWorkspaceVariableExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
_, ok := s.RootModule().Resources[n]

if !ok {
return fmt.Errorf("Not found %s", n)
}

return nil
}
}

func testAccBitbucketWorkspaceVariableConfig(workspace, val string, secure bool) string {
return fmt.Sprintf(`
resource "bitbucket_workspace_variable" "test" {
key = "test"
value = %[2]q
workspace = %[1]q
secured = %[3]t
}
`, workspace, val, secure)
}

func testAccBitbucketWorkspaceVariableManyConfig(workspace, val string, secure bool) string {
return fmt.Sprintf(`
resource "bitbucket_workspace_variable" "test" {
count = 50
key = "test${count.index}"
value = %[2]q
workspace = %[1]q
secured = %[3]t
}
`, workspace, val, secure)
}
44 changes: 44 additions & 0 deletions docs/resources/workspace_variable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
layout: "bitbucket"
page_title: "Bitbucket: bitbucket_workspace_variable"
sidebar_current: "docs-bitbucket-resource-workspace-variable"
description: |-
Manage variables for your pipelines workspace environments
---


# bitbucket\_workspace\_variable

This resource allows you to configure workspace variables.

OAuth2 Scopes: `none`

## Example Usage

```hcl
resource "bitbucket_workspace_variable" "country" {
workspace = bitbucket_workspace.test.id
key = "COUNTRY"
value = "Kenya"
secured = false
}
```

## Argument Reference

* `workspace` - (Required) The workspace ID you want to assign this variable to.
* `key` - (Required) The unique name of the variable.
* `value` - (Required) The value of the variable.
* `secured` - (Optional) If true, this variable will be treated as secured. The value will never be exposed in the logs or the REST API.

## Attributes Reference

* `uuid` - (Computed) The UUID identifying the variable.

## Import

Workspace Variables can be imported using their `workspace-id/uuid` ID, e.g.

```sh
terraform import bitbucket_workspace_variable.example workspace-id/uuid
```

0 comments on commit 4b2eb09

Please sign in to comment.