Skip to content

Commit

Permalink
GH-60 Add pipeline variable resource & data source (#61)
Browse files Browse the repository at this point in the history
Along with docs.
  • Loading branch information
zahiar authored Dec 18, 2021
1 parent c8c5d0e commit 5b5ce63
Show file tree
Hide file tree
Showing 7 changed files with 524 additions and 0 deletions.
44 changes: 44 additions & 0 deletions bitbucket/data_source_bitbucket_pipeline_variable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package bitbucket

import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceBitbucketPipelineVariable() *schema.Resource {
return &schema.Resource{
ReadContext: resourceBitbucketPipelineVariableRead,
Schema: map[string]*schema.Schema{
"id": {
Description: "The ID of the pipeline variable.",
Type: schema.TypeString,
Required: true,
},
"workspace": {
Description: "The slug or UUID (including the enclosing `{}`) of the workspace.",
Type: schema.TypeString,
Required: true,
},
"repository": {
Description: "The name of the repository (must consist of only lowercase ASCII letters, numbers, underscores and hyphens).",
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: validateRepositoryName,
},
"key": {
Description: "The name of the variable.",
Type: schema.TypeString,
Computed: true,
},
"value": {
Description: "The value of the variable (note: if this variable is marked 'secured', this attribute will be blank).",
Type: schema.TypeString,
Computed: true,
},
"secured": {
Description: "Whether this variable is considered secure/sensitive. If true, then it's value will not be exposed in any logs or API requests.",
Type: schema.TypeBool,
Computed: true,
},
},
}
}
109 changes: 109 additions & 0 deletions bitbucket/data_source_bitbucket_pipeline_variable_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package bitbucket

import (
"fmt"
"os"
"strings"
"testing"

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

func TestAccBitbucketPipelineVariableDataSource_basic(t *testing.T) {
workspaceSlug := os.Getenv("BITBUCKET_USERNAME")
projectName := "tf-acc-test-" + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
projectKey := strings.ToUpper(acctest.RandStringFromCharSet(3, acctest.CharSetAlpha))
repoName := "tf-acc-test-" + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
pipelineVariableName := "tf_acc_test" + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
pipelineVariableValue := "tf-acc-test" + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviders,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
data "bitbucket_workspace" "testacc" {
id = "%s"
}
resource "bitbucket_project" "testacc" {
workspace = data.bitbucket_workspace.testacc.id
name = "%s"
key = "%s"
}
resource "bitbucket_repository" "testacc" {
workspace = data.bitbucket_workspace.testacc.id
project_key = bitbucket_project.testacc.key
name = "%s"
enable_pipelines = true
}
resource "bitbucket_pipeline_variable" "testacc" {
workspace = data.bitbucket_workspace.testacc.id
repository = bitbucket_repository.testacc.name
key = "%s"
value = "%s"
secured = true
}
data "bitbucket_pipeline_variable" "testacc" {
id = bitbucket_pipeline_variable.testacc.id
workspace = data.bitbucket_workspace.testacc.id
repository = bitbucket_repository.testacc.name
}`, workspaceSlug, projectName, projectKey, repoName, pipelineVariableName, pipelineVariableValue),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.bitbucket_pipeline_variable.testacc", "workspace", workspaceSlug),
resource.TestCheckResourceAttr("data.bitbucket_pipeline_variable.testacc", "repository", repoName),
resource.TestCheckResourceAttr("data.bitbucket_pipeline_variable.testacc", "key", pipelineVariableName),
resource.TestCheckResourceAttr("data.bitbucket_pipeline_variable.testacc", "value", ""),
resource.TestCheckResourceAttr("data.bitbucket_pipeline_variable.testacc", "secured", "true"),
resource.TestCheckResourceAttrSet("data.bitbucket_pipeline_variable.testacc", "id"),
),
},
{
Config: fmt.Sprintf(`
data "bitbucket_workspace" "testacc" {
id = "%s"
}
resource "bitbucket_project" "testacc" {
workspace = data.bitbucket_workspace.testacc.id
name = "%s"
key = "%s"
}
resource "bitbucket_repository" "testacc" {
workspace = data.bitbucket_workspace.testacc.id
project_key = bitbucket_project.testacc.key
name = "%s"
enable_pipelines = true
}
resource "bitbucket_pipeline_variable" "testacc" {
workspace = data.bitbucket_workspace.testacc.id
repository = bitbucket_repository.testacc.name
key = "%s"
value = "%s"
secured = false
}
data "bitbucket_pipeline_variable" "testacc" {
id = bitbucket_pipeline_variable.testacc.id
workspace = data.bitbucket_workspace.testacc.id
repository = bitbucket_repository.testacc.name
}`, workspaceSlug, projectName, projectKey, repoName, pipelineVariableName, pipelineVariableValue),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.bitbucket_pipeline_variable.testacc", "workspace", workspaceSlug),
resource.TestCheckResourceAttr("data.bitbucket_pipeline_variable.testacc", "repository", repoName),
resource.TestCheckResourceAttr("data.bitbucket_pipeline_variable.testacc", "key", pipelineVariableName),
resource.TestCheckResourceAttr("data.bitbucket_pipeline_variable.testacc", "value", pipelineVariableValue),
resource.TestCheckResourceAttr("data.bitbucket_pipeline_variable.testacc", "secured", "false"),
resource.TestCheckResourceAttrSet("data.bitbucket_pipeline_variable.testacc", "id"),
),
},
},
})
}
2 changes: 2 additions & 0 deletions bitbucket/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func Provider() *schema.Provider {
"bitbucket_deploy_key": dataSourceBitbucketDeployKey(),
"bitbucket_group": dataSourceBitbucketGroup(),
"bitbucket_group_permission": dataSourceBitbucketGroupPermission(),
"bitbucket_pipeline_variable": dataSourceBitbucketPipelineVariable(),
"bitbucket_project": dataSourceBitbucketProject(),
"bitbucket_repository": dataSourceBitbucketRepository(),
"bitbucket_user": dataSourceBitbucketUser(),
Expand All @@ -47,6 +48,7 @@ func Provider() *schema.Provider {
"bitbucket_group": resourceBitbucketGroup(),
"bitbucket_group_member": resourceBitbucketGroupMember(),
"bitbucket_group_permission": resourceBitbucketGroupPermission(),
"bitbucket_pipeline_variable": resourceBitbucketPipelineVariable(),
"bitbucket_project": resourceBitbucketProject(),
"bitbucket_repository": resourceBitbucketRepository(),
"bitbucket_webhook": resourceBitbucketWebhook(),
Expand Down
175 changes: 175 additions & 0 deletions bitbucket/resource_bitbucket_pipeline_variable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package bitbucket

import (
"context"
"fmt"
"regexp"
"strings"

"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
gobb "github.com/ktrysmt/go-bitbucket"
)

func resourceBitbucketPipelineVariable() *schema.Resource {
return &schema.Resource{
CreateContext: resourceBitbucketPipelineVariableCreate,
ReadContext: resourceBitbucketPipelineVariableRead,
UpdateContext: resourceBitbucketPipelineVariableUpdate,
DeleteContext: resourceBitbucketPipelineVariableDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceBitbucketPipelineVariableImport,
},
Schema: map[string]*schema.Schema{
"id": {
Description: "The ID of the pipeline variable.",
Type: schema.TypeString,
Computed: true,
},
"workspace": {
Description: "The slug or UUID (including the enclosing `{}`) of the workspace.",
Type: schema.TypeString,
Required: true,
},
"repository": {
Description: "The name of the repository (must consist of only lowercase ASCII letters, numbers, underscores and hyphens).",
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: validateRepositoryName,
},
"key": {
Description: "The name of the variable (must consist of only ASCII letters, numbers, underscores & not begin with a number).",
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: validateRepositoryVariableName,
},
"value": {
Description: "The value of the variable.",
Type: schema.TypeString,
Required: true,
},
"secured": {
Description: "Whether this variable is considered secure/sensitive. If true, then it's value will not be exposed in any logs or API requests.",
Type: schema.TypeBool,
Default: false,
Optional: true,
},
},
}
}

func resourceBitbucketPipelineVariableCreate(ctx context.Context, resourceData *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*Clients).V2

pipelineVariable, err := client.Repositories.Repository.AddPipelineVariable(
&gobb.RepositoryPipelineVariableOptions{
Owner: resourceData.Get("workspace").(string),
RepoSlug: resourceData.Get("repository").(string),
Key: resourceData.Get("key").(string),
Value: resourceData.Get("value").(string),
Secured: resourceData.Get("secured").(bool),
},
)
if err != nil {
return diag.FromErr(fmt.Errorf("unable to create pipeline variable with error: %s", err))
}

resourceData.SetId(pipelineVariable.Uuid)

return resourceBitbucketPipelineVariableRead(ctx, resourceData, meta)
}

func resourceBitbucketPipelineVariableRead(ctx context.Context, resourceData *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*Clients).V2

pipelineVariable, err := client.Repositories.Repository.GetPipelineVariable(
&gobb.RepositoryPipelineVariableOptions{
Owner: resourceData.Get("workspace").(string),
RepoSlug: resourceData.Get("repository").(string),
Uuid: resourceData.Get("id").(string),
},
)
if err != nil {
return diag.FromErr(fmt.Errorf("unable to get repository variable with error: %s", err))
}

_ = resourceData.Set("key", pipelineVariable.Key)

if !pipelineVariable.Secured {
_ = resourceData.Set("value", pipelineVariable.Value)
} else {
_ = resourceData.Set("value", resourceData.Get("value").(string))
}

_ = resourceData.Set("secured", pipelineVariable.Secured)

resourceData.SetId(pipelineVariable.Uuid)

return nil
}

func resourceBitbucketPipelineVariableUpdate(ctx context.Context, resourceData *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*Clients).V2

_, err := client.Repositories.Repository.UpdatePipelineVariable(
&gobb.RepositoryPipelineVariableOptions{
Owner: resourceData.Get("workspace").(string),
RepoSlug: resourceData.Get("repository").(string),
Uuid: resourceData.Get("id").(string),
Key: resourceData.Get("key").(string),
Value: resourceData.Get("value").(string),
Secured: resourceData.Get("secured").(bool),
},
)
if err != nil {
return diag.FromErr(fmt.Errorf("unable to update repository variable with error: %s", err))
}

return resourceBitbucketPipelineVariableRead(ctx, resourceData, meta)
}

func resourceBitbucketPipelineVariableDelete(ctx context.Context, resourceData *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*Clients).V2

_, err := client.Repositories.Repository.DeletePipelineVariable(
&gobb.RepositoryPipelineVariableDeleteOptions{
Owner: resourceData.Get("workspace").(string),
RepoSlug: resourceData.Get("repository").(string),
Uuid: resourceData.Id(),
},
)
if err != nil {
return diag.FromErr(fmt.Errorf("unable to delete repository variable with error: %s", err))
}

resourceData.SetId("")

return nil
}

func resourceBitbucketPipelineVariableImport(ctx context.Context, resourceData *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
ret := []*schema.ResourceData{resourceData}

splitID := strings.Split(resourceData.Id(), "/")
if len(splitID) < 3 {
return ret, fmt.Errorf("invalid import ID. It must to be in this format \"<workspace-slug|workspace-uuid>/<repository-slug|repository-uuid>/<pipeline-variable-uuid>\"")
}

_ = resourceData.Set("workspace", splitID[0])
_ = resourceData.Set("repository", splitID[1])
_ = resourceData.Set("id", splitID[2])

_ = resourceBitbucketPipelineVariableRead(ctx, resourceData, meta)

return ret, nil
}

func validateRepositoryVariableName(val interface{}, path cty.Path) diag.Diagnostics {
match, _ := regexp.MatchString("^([a-zA-Z])[a-zA-Z0-9_]+$", val.(string))
if !match {
return diag.FromErr(fmt.Errorf("repository variable name must consist of only ASCII letters, numbers, underscores & not begin with a number (a-z, 0-9, _)"))
}

return diag.Diagnostics{}
}
Loading

0 comments on commit 5b5ce63

Please sign in to comment.