Skip to content
This repository was archived by the owner on Dec 8, 2020. It is now read-only.

Add deploy keys and user SSH keys resources #48

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions bitbucket/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ func Provider() terraform.ResourceProvider {
},
ConfigureFunc: providerConfigure,
ResourcesMap: map[string]*schema.Resource{
"bitbucket_hook": resourceHook(),
"bitbucket_default_reviewers": resourceDefaultReviewers(),
"bitbucket_repository": resourceRepository(),
"bitbucket_repository_variable": resourceRepositoryVariable(),
"bitbucket_project": resourceProject(),
"bitbucket_branch_restriction": resourceBranchRestriction(),
"bitbucket_hook": resourceHook(),
"bitbucket_default_reviewers": resourceDefaultReviewers(),
"bitbucket_repository": resourceRepository(),
"bitbucket_repository_variable": resourceRepositoryVariable(),
"bitbucket_project": resourceProject(),
"bitbucket_branch_restriction": resourceBranchRestriction(),
"bitbucket_user_ssh_key": resourceSshKey(),
"bitbucket_repository_deploy_key": resourceRepositoryDeployKey(),
},
DataSourcesMap: map[string]*schema.Resource{
"bitbucket_user": dataUser(),
Expand Down
177 changes: 177 additions & 0 deletions bitbucket/resource_repository_deploy_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package bitbucket

import (
"bytes"
"encoding/json"
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"io/ioutil"
"strings"
)

type DeployKey struct {
Id json.Number `json:"id,omitempty,Number"`
Key string `json:"key,omitempty"`
Comment string `json:"comment,omitempty"`
Label string `json:"label,omitempty"`
Repository string `json:"-"`
Owner string `json:"-"`
}

func (k *DeployKey) getAuthorizedKey() string {
return fmt.Sprintf("%s %s", k.Key, k.Comment)
}

func resourceRepositoryDeployKey() *schema.Resource {
return &schema.Resource{
Create: resourceRepositoryDeployKeyCreate,
Read: resourceRepositoryDeployKeyRead,
Delete: resourceRepositoryDeployKeyDelete,
Update: resourceRepositoryDeployKeyUpdate,

Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateKey,
Description: "The key in OpenSSH authorized_keys format",
},
"owner": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The owner of the repository (user or team)",
},
"repo_slug": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The repository slug",
},
"label": {
Type: schema.TypeString,
Optional: true,
Default: "Managed by Terraform",
Description: "The label for the key to be shown in Bitbucket",
},
},
}
}

func createDeployKey(d *schema.ResourceData) (*DeployKey, error) {
splitKey := strings.Split(d.Get("key").(string), " ")

key := strings.Join(splitKey[:2], " ")
comment := ""
if len(splitKey) > 2 {
comment = splitKey[2]
}

return &DeployKey{
Id: json.Number(d.Id()),
Label: d.Get("label").(string),
Key: key,
Comment: comment,
Owner: d.Get("owner").(string),
Repository: d.Get("repo_slug").(string),
}, nil
}

func resourceRepositoryDeployKeyCreate(d *schema.ResourceData, m interface{}) error {
client := m.(*Client)

key, err := createDeployKey(d)
if err != nil {
return err
}

keyReq, err := client.Post(
fmt.Sprintf("2.0/repositories/%s/%s/deploy-keys", key.Owner, key.Repository),
bytes.NewBufferString(fmt.Sprintf(`{"key":"%s","label":"%s"}`, key.getAuthorizedKey(), key.Label)),
)

if err != nil {
return err
}

body, err := ioutil.ReadAll(keyReq.Body)
if err != nil {
return err
}

err = json.Unmarshal(body, &key)
if err != nil {
return err
}

d.SetId(string(key.Id))

return resourceRepositoryDeployKeyRead(d, m)
}

func resourceRepositoryDeployKeyRead(d *schema.ResourceData, m interface{}) error {
client := m.(*Client)

key, err := createDeployKey(d)
if err != nil {
return err
}

keyReq, err := client.Get(
fmt.Sprintf("2.0/repositories/%s/%s/deploy-keys/%s", key.Owner, key.Repository, d.Id()),
)
if err != nil {
return err
}

body, err := ioutil.ReadAll(keyReq.Body)
if err != nil {
return err
}

err = json.Unmarshal(body, &key)
if err != nil {
return err
}

_ = d.Set("key", key.getAuthorizedKey())
_ = d.Set("label", key.Label)

return nil
}

func resourceRepositoryDeployKeyDelete(d *schema.ResourceData, m interface{}) error {
client := m.(*Client)

key, err := createDeployKey(d)
if err != nil {
return err
}

_, err = client.Delete(
fmt.Sprintf("2.0/repositories/%s/%s/deploy-keys/%s", key.Owner, key.Repository, d.Id()),
)

return err
}

func resourceRepositoryDeployKeyUpdate(d *schema.ResourceData, m interface{}) error {
client := m.(*Client)

key, err := createDeployKey(d)
if err != nil {
return err
}

_, err = client.Put(
fmt.Sprintf("2.0/repositories/%s/%s/deploy-keys/%s", key.Owner, key.Repository, d.Id()),
bytes.NewBufferString(fmt.Sprintf(`{"label":"%s"}`, key.Label)),
)

if err != nil {
return err
}

return resourceRepositoryDeployKeyRead(d, m)
}
100 changes: 100 additions & 0 deletions bitbucket/resource_repository_deploy_key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package bitbucket

import (
"fmt"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"net/url"
"os"
"testing"
)

const deployKeyResource = "bitbucket_repository_deploy_key.test_deploy_key"

func TestAccBitbucketDeployKey_basic(t *testing.T) {
testUser := os.Getenv("BITBUCKET_USERNAME")
template := `
resource bitbucket_repository test_repo {
owner = "%s"
name = "test-repo-for-deployment-key-test"
}
resource bitbucket_repository_deploy_key test_deploy_key {
owner = bitbucket_repository.test_repo.owner
repo_slug = bitbucket_repository.test_repo.slug
key = "%s"
label = "%s"
}
`

key := generateKey(t)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBitbucketDeployKeyDestroy,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(template, testUser, key, "test"),
ExpectNonEmptyPlan: true,
Check: resource.ComposeTestCheckFunc(
testAccCheckBitbucketDeployKeyExists(deployKeyResource),
),
},
{
Config: fmt.Sprintf(template, testUser, key, "test2"),
ExpectNonEmptyPlan: true,
Check: resource.ComposeTestCheckFunc(
testAccCheckBitbucketDeployKeyExists(deployKeyResource),
),
},
{
Config: fmt.Sprintf(template, testUser, generateKey(t), "test2"),
ExpectNonEmptyPlan: true,
Check: resource.ComposeTestCheckFunc(
testAccCheckBitbucketDeployKeyExists(deployKeyResource),
),
},
},
})
}

func testAccCheckBitbucketDeployKeyDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*Client)
rs, ok := s.RootModule().Resources[deployKeyResource]
if !ok {
return fmt.Errorf("not found %s", deployKeyResource)
}

response, err := client.Get(fmt.Sprintf(
"2.0/repositories/%s/%s/deploy-keys/%s",
rs.Primary.Attributes["owner"],
rs.Primary.Attributes["repo_slug"],
url.PathEscape(rs.Primary.Attributes["uuid"])))

if err == nil {
return fmt.Errorf("%v the resource was found, should have errored", response.Status)
}

if response == nil {
return err
}

if response.StatusCode != 404 {
return fmt.Errorf("ssh key still exists")
}

return nil
}

func testAccCheckBitbucketDeployKeyExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("not found %s", name)
}
if rs.Primary.ID == "" {
return fmt.Errorf("no SSH Key ID is set")
}
return nil
}
}
Loading