Skip to content
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
1 change: 1 addition & 0 deletions nomad/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ func Provider() *schema.Provider {
ResourcesMap: map[string]*schema.Resource{
"nomad_acl_auth_method": resourceACLAuthMethod(),
"nomad_acl_binding_rule": resourceACLBindingRule(),
"nomad_acl_bootstrap": resourceACLBootstrap(),
"nomad_acl_policy": resourceACLPolicy(),
"nomad_acl_role": resourceACLRole(),
"nomad_acl_token": resourceACLToken(),
Expand Down
101 changes: 101 additions & 0 deletions nomad/resource_acl_bootstrap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package nomad

import (
"fmt"
"log"
"strings"

"github.com/hashicorp/nomad/api"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceACLBootstrap() *schema.Resource {
return &schema.Resource{
Create: resourceACLBootstrapCreate,
Delete: resourceACLBootstrapDelete,
Read: resourceACLBootstrapRead,
Exists: resourceACLBootstrapExists,

Schema: map[string]*schema.Schema{
"accessor_id": {
Description: "Nomad-generated ID for this token.",
Computed: true,
Type: schema.TypeString,
},
"bootstrap_token": {
Description: "The value that grants access to Nomad.",
Computed: true,
Optional: true,
Sensitive: true,
Type: schema.TypeString,
},
},
}
}

func resourceACLBootstrapCreate(d *schema.ResourceData, meta interface{}) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only support recent versions of Go, so any place we have interface{} we can use any now:

Suggested change
func resourceACLBootstrapCreate(d *schema.ResourceData, meta interface{}) error {
func resourceACLBootstrapCreate(d *schema.ResourceData, meta any) error {

providerConfig := meta.(ProviderConfig)
client := providerConfig.client

token := api.BootstrapRequest{
BootstrapSecret: d.Get("bootstrap_token").(string),
}
// create our token
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can leave comments like this out, especially given the log following right behind it.

Suggested change
// create our token

log.Println("[DEBUG] Creating ACL Bootstrap token")
resp, _, err := client.ACLTokens().BootstrapOpts(token.BootstrapSecret, nil)
if err != nil {
return fmt.Errorf("error bootstrapping the cluster: %w", err)
}
log.Printf("[DEBUG] Created ACL token AccessorID %q", resp.AccessorID)
d.SetId(resp.AccessorID)

return resourceACLBootstrapRead(d, meta)
}

// not implemented as a cluster bootstrap can't be reverted
func resourceACLBootstrapDelete(d *schema.ResourceData, meta interface{}) error {
return nil
}

func resourceACLBootstrapRead(d *schema.ResourceData, meta interface{}) error {
providerConfig := meta.(ProviderConfig)
client := providerConfig.client
accessor := d.Id()

// retrieve the token
log.Printf("[DEBUG] Reading ACL bootstrap token %q", accessor)
token, _, err := client.ACLTokens().Info(accessor, nil)
if err != nil {
// we have Exists, so no need to handle 404
return fmt.Errorf("error reading ACL token %q: %s", accessor, err.Error())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("error reading ACL token %q: %s", accessor, err.Error())
return fmt.Errorf("error reading ACL token %q: %w", accessor, err)

}
log.Printf("[DEBUG] Read ACL bootstrap token %q", accessor)

d.Set("accessor_id", token.AccessorID)
d.Set("bootstrap_token", token.SecretID)

return nil
}

func resourceACLBootstrapExists(d *schema.ResourceData, meta interface{}) (bool, error) {
providerConfig := meta.(ProviderConfig)
client := providerConfig.client

accessor := d.Id()
log.Printf("[DEBUG] Checking if ACL token %q exists", accessor)
_, _, err := client.ACLTokens().Info(accessor, nil)
if err != nil {
// As of Nomad 0.4.1, the API client returns an error for 404
// rather than a nil result, so we must check this way.
Comment on lines +91 to +92
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably leave this comment out, or at least remove the "as of Nomad 0.4.1" bit (I realize this is probably just copy-pasted from the other ACL resources but no need to keep adding it).

if strings.Contains(err.Error(), "404") {
return false, nil
}

return true, fmt.Errorf("error checking for ACL token %q: %#v", accessor, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return true, fmt.Errorf("error checking for ACL token %q: %#v", accessor, err)
return true, fmt.Errorf("error checking for ACL token %q: %w", accessor, err)

}

return true, nil
}
64 changes: 64 additions & 0 deletions nomad/resource_acl_bootstrap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package nomad

import (
"fmt"
"testing"

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

func TestResourceACLBootstrap_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testAccPreCheck(t) },
Steps: []resource.TestStep{
{
Config: testResourceACLBootstrap_initialConfig(),
Check: testResourceACLBootstrap_initialCheck(),
},
},
// Note: We don't include CheckDestroy because bootstrap cannot be reverted
})
}

func testResourceACLBootstrap_initialConfig() string {
return `
resource "nomad_acl_bootstrap" "test" {
}
`
}

func testResourceACLBootstrap_initialCheck() resource.TestCheckFunc {
return resource.ComposeTestCheckFunc(
testResourceACLBootstrapExists("nomad_acl_bootstrap.test"),
resource.TestCheckResourceAttrSet("nomad_acl_bootstrap.test", "accessor_id"),
resource.TestCheckResourceAttrSet("nomad_acl_bootstrap.test", "bootstrap_token"),
)
}

func testResourceACLBootstrapExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}

providerConfig := testProvider.Meta().(ProviderConfig)
client := providerConfig.client

_, _, err := client.ACLTokens().Info(rs.Primary.ID, nil)
if err != nil {
return fmt.Errorf("ACL bootstrap token doesn't exist: %s", err)
}

return nil
}
}
Loading