Skip to content

Commit

Permalink
Merge branch '0.4.7' into Orchestrator
Browse files Browse the repository at this point in the history
  • Loading branch information
fdevans committed Oct 23, 2023
2 parents 4d311dc + 086e2fb commit 69bc385
Show file tree
Hide file tree
Showing 11 changed files with 354 additions and 152 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 0.4.7
- Added Password Resource

## 0.4.6
- Added Retry Delay setting to Job definition.
- Added ability to set Secure Options in Job Definition.
Expand Down
1 change: 1 addition & 0 deletions rundeck/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func Provider() terraform.ResourceProvider {
"rundeck_project": resourceRundeckProject(),
"rundeck_job": resourceRundeckJob(),
"rundeck_private_key": resourceRundeckPrivateKey(),
"rundeck_password": resourceRundeckPassword(),
"rundeck_public_key": resourceRundeckPublicKey(),
"rundeck_acl_policy": resourceRundeckAclPolicy(),
},
Expand Down
138 changes: 138 additions & 0 deletions rundeck/resource_base_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package rundeck

import (
"context"
"crypto/sha1"
"encoding/hex"
"fmt"
"io"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"

"github.com/rundeck/go-rundeck/rundeck"
)

type BaseKeyType int32

const (
PRIVATE_KEY BaseKeyType = 0
PASSWORD BaseKeyType = 1
)

func CreateOrUpdateBaseKey(d *schema.ResourceData, meta interface{}, baseKeyType BaseKeyType) error {
client := meta.(*rundeck.BaseClient)

var payload string
path := d.Get("path").(string)

var err error

ctx := context.Background()

var contentType string
switch baseKeyType {
case PRIVATE_KEY:
contentType = "application/octet-stream"
payload = d.Get("key_material").(string)
case PASSWORD:
contentType = "application/x-rundeck-data-password"
payload = d.Get("password").(string)
default:
return fmt.Errorf("Internal error. Unknown key type")
}

payloadReader := io.NopCloser(strings.NewReader(payload))

if d.Id() != "" {
resp, err := client.StorageKeyUpdate(ctx, path, payloadReader, contentType)
if resp.StatusCode == 409 || err != nil {
return fmt.Errorf("Error updating or adding key: Key exists")
}
} else {
resp, err := client.StorageKeyCreate(ctx, path, payloadReader, contentType)
if resp.StatusCode == 409 || err != nil {
return fmt.Errorf("Error updating or adding key: Key exists")
}
}

if err != nil {
return err
}

d.SetId(path)

return ReadBaseKey(d, meta)
}

func DeleteBaseKey(d *schema.ResourceData, meta interface{}) error {
client := meta.(*rundeck.BaseClient)
ctx := context.Background()

path := d.Id()

// The only "delete" call we have is oblivious to key type, but
// that's okay since our Exists implementation makes sure that we
// won't try to delete a key of the wrong type since we'll pretend
// that it's already been deleted.
_, err := client.StorageKeyDelete(ctx, path)
if err != nil {
return err
}

d.SetId("")
return nil
}

func ReadBaseKey(d *schema.ResourceData, meta interface{}) error {
// Nothing to read for a private key: existence is all we need to
// worry about, and PrivateKeyExists took care of that.
return nil
}

func BaseKeyExists(d *schema.ResourceData, meta interface{}, baseKeyType BaseKeyType) (bool, error) {
client := meta.(*rundeck.BaseClient)
ctx := context.Background()

path := d.Id()

resp, err := client.StorageKeyGetMetadata(ctx, path)
if err != nil {
if resp.StatusCode != 404 {
err = nil
}
return false, err
}

// If the resource is not password or private key as far as this resource is
// concerned it doesn't exist. (We'll fail properly when we try to
// create a key where one already exists.)
// Content-type are:
// application/octet-stream specifies a private key
// application/pgp-keys specifies a public key
// application/x-rundeck-data-password specifies a password
switch baseKeyType {
case PRIVATE_KEY:
if *resp.Meta.RundeckContentType != "application/octet-stream" {
return false, nil
}
case PASSWORD:
if *resp.Meta.RundeckContentType != "application/x-rundeck-data-password" {
return false, nil
}
default:
return false, fmt.Errorf("Internal error. Unknown key type")
}

return true, nil
}

func BaseKeyStateFunction(val interface{}) string {
switch v := val.(type) {
case string:
hash := sha1.Sum([]byte(v))
return hex.EncodeToString(hash[:])
default:
return ""
}
}
53 changes: 53 additions & 0 deletions rundeck/resource_base_key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package rundeck

import (
"context"
"fmt"

"github.com/rundeck/go-rundeck/rundeck"

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

func testAccBaseKeyCheckDestroy(key *rundeck.StorageKeyListResponse) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*rundeck.BaseClient)
ctx := context.Background()

resp, err := client.StorageKeyGetMetadata(ctx, *key.Path)

if resp.StatusCode == 200 {
return fmt.Errorf("key still exists")
}
if resp.StatusCode != 404 {
return fmt.Errorf("got something other than NotFoundError (%v) when getting key", err)
}

return nil
}
}

func testAccBaseKeyCheckExists(rn string, key *rundeck.StorageKeyListResponse) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[rn]
if !ok {
return fmt.Errorf("resource not found: %s", rn)
}

if rs.Primary.ID == "" {
return fmt.Errorf("key id not set")
}

client := testAccProvider.Meta().(*rundeck.BaseClient)
ctx := context.Background()
gotKey, err := client.StorageKeyGetMetadata(ctx, rs.Primary.ID)
if gotKey.StatusCode == 404 || err != nil {
return fmt.Errorf("error getting key metadata: %s", err)
}

*key = gotKey

return nil
}
}
37 changes: 37 additions & 0 deletions rundeck/resource_password.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package rundeck

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

func resourceRundeckPassword() *schema.Resource {
return &schema.Resource{
Create: func(rd *schema.ResourceData, i interface{}) error {
return CreateOrUpdateBaseKey(rd, i, PASSWORD)
},
Update: func(rd *schema.ResourceData, i interface{}) error {
return CreateOrUpdateBaseKey(rd, i, PASSWORD)
},
Delete: DeleteBaseKey,
Exists: func(rd *schema.ResourceData, i interface{}) (bool, error) {
return BaseKeyExists(rd, i, PASSWORD)
},
Read: ReadBaseKey,

Schema: map[string]*schema.Schema{
"path": {
Type: schema.TypeString,
Required: true,
Description: "Path to the key within the key store",
ForceNew: true,
},

"password": {
Type: schema.TypeString,
Required: true,
Description: "The password to store",
StateFunc: BaseKeyStateFunction,
},
},
}
}
57 changes: 57 additions & 0 deletions rundeck/resource_password_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package rundeck

import (
"fmt"
"strings"
"testing"

"github.com/rundeck/go-rundeck/rundeck"

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

func TestAccPassword_basic(t *testing.T) {
var key rundeck.StorageKeyListResponse

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccBaseKeyCheckDestroy(&key),
Steps: []resource.TestStep{
{
Config: testAccPasswordConfig_basic,
Check: resource.ComposeTestCheckFunc(
testAccBaseKeyCheckExists("rundeck_password.test", &key),
func(s *terraform.State) error {
if expected := "keys/terraform_acceptance_tests/password"; *key.Path != expected {
return fmt.Errorf("wrong path; expected %v, got %v", expected, *key.Path)
}
if !strings.HasSuffix(*key.URL, "/storage/keys/terraform_acceptance_tests/password") {
return fmt.Errorf("wrong URL; expected to end with the key path")
}
if expected := "file"; *key.Type != expected {
return fmt.Errorf("wrong resource type; expected %v, got %v", expected, *key.Type)
}
if expected := ""; string(key.Meta.RundeckKeyType) != expected {
return fmt.Errorf("wrong key type; expected %v, got %v", expected, key.Meta.RundeckKeyType)
}
if expected := "application/x-rundeck-data-password"; *key.Meta.RundeckContentType != expected {
return fmt.Errorf("wrong RundeckContentType; expected %v, got %v", expected, *key.Meta.RundeckContentType)
}
// Rundeck won't let us re-retrieve a private key payload, so we can't test
// that the key material was submitted and stored correctly.
return nil
},
),
},
},
})
}

const testAccPasswordConfig_basic = `
resource "rundeck_password" "test" {
path = "terraform_acceptance_tests/password"
password = "qwerty"
}
`
Loading

0 comments on commit 69bc385

Please sign in to comment.