Packet bare metal cloud hosting platform provider
This commit is contained in:
parent
5f129f1b9d
commit
07ad320960
|
@ -0,0 +1,12 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/builtin/providers/packet"
|
||||||
|
"github.com/hashicorp/terraform/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.Serve(&plugin.ServeOpts{
|
||||||
|
ProviderFunc: packet.Provider,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package packet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/packethost/packngo"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
consumerToken = "aZ9GmqHTPtxevvFq9SK3Pi2yr9YCbRzduCSXF2SNem5sjB91mDq7Th3ZwTtRqMWZ"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
AuthToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client() returns a new client for accessing packet.
|
||||||
|
func (c *Config) Client() *packngo.Client {
|
||||||
|
return packngo.NewClient(consumerToken, c.AuthToken)
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package packet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provider returns a schema.Provider for Packet.
|
||||||
|
func Provider() terraform.ResourceProvider {
|
||||||
|
return &schema.Provider{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"auth_token": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("PACKET_AUTH_TOKEN", nil),
|
||||||
|
Description: "The API auth key for API operations.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
ResourcesMap: map[string]*schema.Resource{
|
||||||
|
"packet_device": resourcePacketDevice(),
|
||||||
|
"packet_ssh_key": resourcePacketSSHKey(),
|
||||||
|
"packet_project": resourcePacketProject(),
|
||||||
|
},
|
||||||
|
|
||||||
|
ConfigureFunc: providerConfigure,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||||
|
config := Config{
|
||||||
|
AuthToken: d.Get("auth_token").(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
return config.Client(), nil
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package packet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testAccProviders map[string]terraform.ResourceProvider
|
||||||
|
var testAccProvider *schema.Provider
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testAccProvider = Provider().(*schema.Provider)
|
||||||
|
testAccProviders = map[string]terraform.ResourceProvider{
|
||||||
|
"packet": testAccProvider,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProvider(t *testing.T) {
|
||||||
|
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProvider_impl(t *testing.T) {
|
||||||
|
var _ terraform.ResourceProvider = Provider()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccPreCheck(t *testing.T) {
|
||||||
|
if v := os.Getenv("PACKET_AUTH_TOKEN"); v == "" {
|
||||||
|
t.Fatal("PACKET_AUTH_TOKEN must be set for acceptance tests")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,302 @@
|
||||||
|
package packet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"github.com/packethost/packngo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourcePacketDevice() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourcePacketDeviceCreate,
|
||||||
|
Read: resourcePacketDeviceRead,
|
||||||
|
Update: resourcePacketDeviceUpdate,
|
||||||
|
Delete: resourcePacketDeviceDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"project_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"hostname": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"operating_system": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"facility": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"plan": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"billing_cycle": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"state": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"locked": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"network": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"address": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"gateway": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"family": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"cidr": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"public": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"created": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"updated": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"user_data": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"tags": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketDeviceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
createRequest := &packngo.DeviceCreateRequest{
|
||||||
|
HostName: d.Get("hostname").(string),
|
||||||
|
Plan: d.Get("plan").(string),
|
||||||
|
Facility: d.Get("facility").(string),
|
||||||
|
OS: d.Get("operating_system").(string),
|
||||||
|
BillingCycle: d.Get("billing_cycle").(string),
|
||||||
|
ProjectID: d.Get("project_id").(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr, ok := d.GetOk("user_data"); ok {
|
||||||
|
createRequest.UserData = attr.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
tags := d.Get("tags.#").(int)
|
||||||
|
if tags > 0 {
|
||||||
|
createRequest.Tags = make([]string, 0, tags)
|
||||||
|
for i := 0; i < tags; i++ {
|
||||||
|
key := fmt.Sprintf("tags.%d", i)
|
||||||
|
createRequest.Tags = append(createRequest.Tags, d.Get(key).(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Device create configuration: %#v", createRequest)
|
||||||
|
|
||||||
|
newDevice, _, err := client.Devices.Create(createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating device: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the device id
|
||||||
|
d.SetId(newDevice.ID)
|
||||||
|
|
||||||
|
log.Printf("[INFO] Device ID: %s", d.Id())
|
||||||
|
|
||||||
|
_, err = WaitForDeviceAttribute(d, "active", []string{"provisioning"}, "state", meta)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error waiting for device (%s) to become ready: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourcePacketDeviceRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketDeviceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
// Retrieve the device properties for updating the state
|
||||||
|
device, _, err := client.Devices.Get(d.Id())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error retrieving device: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("name", device.Hostname)
|
||||||
|
d.Set("plan", device.Plan.Slug)
|
||||||
|
d.Set("facility", device.Facility.Code)
|
||||||
|
d.Set("operating_system", device.OS.Slug)
|
||||||
|
d.Set("state", device.State)
|
||||||
|
d.Set("billing_cycle", device.BillingCycle)
|
||||||
|
d.Set("locked", device.Locked)
|
||||||
|
d.Set("created", device.Created)
|
||||||
|
d.Set("udpated", device.Updated)
|
||||||
|
|
||||||
|
tags := make([]string, 0)
|
||||||
|
for _, tag := range device.Tags {
|
||||||
|
tags = append(tags, tag)
|
||||||
|
}
|
||||||
|
d.Set("tags", tags)
|
||||||
|
|
||||||
|
networks := make([]map[string]interface{}, 0, 1)
|
||||||
|
for _, ip := range device.Network {
|
||||||
|
network := make(map[string]interface{})
|
||||||
|
network["address"] = ip.Address
|
||||||
|
network["gateway"] = ip.Gateway
|
||||||
|
network["family"] = ip.Family
|
||||||
|
network["cidr"] = ip.Cidr
|
||||||
|
network["public"] = ip.Public
|
||||||
|
networks = append(networks, network)
|
||||||
|
}
|
||||||
|
d.Set("network", networks)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketDeviceUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
if d.HasChange("locked") && d.Get("locked").(bool) {
|
||||||
|
_, err := client.Devices.Lock(d.Id())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error locking device (%s): %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
} else if d.HasChange("locked") {
|
||||||
|
_, err := client.Devices.Unlock(d.Id())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error unlocking device (%s): %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourcePacketDeviceRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketDeviceDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
log.Printf("[INFO] Deleting device: %s", d.Id())
|
||||||
|
if _, err := client.Devices.Delete(d.Id()); err != nil {
|
||||||
|
return fmt.Errorf("Error deleting device: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WaitForDeviceAttribute(
|
||||||
|
d *schema.ResourceData, target string, pending []string, attribute string, meta interface{}) (interface{}, error) {
|
||||||
|
// Wait for the device so we can get the networking attributes
|
||||||
|
// that show up after a while
|
||||||
|
log.Printf(
|
||||||
|
"[INFO] Waiting for device (%s) to have %s of %s",
|
||||||
|
d.Id(), attribute, target)
|
||||||
|
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: pending,
|
||||||
|
Target: target,
|
||||||
|
Refresh: newDeviceStateRefreshFunc(d, attribute, meta),
|
||||||
|
Timeout: 60 * time.Minute,
|
||||||
|
Delay: 10 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateConf.WaitForState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDeviceStateRefreshFunc(
|
||||||
|
d *schema.ResourceData, attribute string, meta interface{}) resource.StateRefreshFunc {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
return func() (interface{}, string, error) {
|
||||||
|
err := resourcePacketDeviceRead(d, meta)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if we can access our attribute
|
||||||
|
if attr, ok := d.GetOk(attribute); ok {
|
||||||
|
// Retrieve the device properties
|
||||||
|
device, _, err := client.Devices.Get(d.Id())
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("Error retrieving device: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &device, attr.(string), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Powers on the device and waits for it to be active
|
||||||
|
func powerOnAndWait(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
_, err := client.Devices.PowerOn(d.Id())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for power on
|
||||||
|
_, err = WaitForDeviceAttribute(d, "active", []string{"off"}, "state", client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package packet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"github.com/packethost/packngo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourcePacketProject() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourcePacketProjectCreate,
|
||||||
|
Read: resourcePacketProjectRead,
|
||||||
|
Update: resourcePacketProjectUpdate,
|
||||||
|
Delete: resourcePacketProjectDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"payment_method": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"created": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"updated": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketProjectCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
createRequest := &packngo.ProjectCreateRequest{
|
||||||
|
Name: d.Get("name").(string),
|
||||||
|
PaymentMethod: d.Get("payment_method").(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Project create configuration: %#v", createRequest)
|
||||||
|
project, _, err := client.Projects.Create(createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating Project: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(project.ID)
|
||||||
|
log.Printf("[INFO] Project created: %s", project.ID)
|
||||||
|
|
||||||
|
return resourcePacketProjectRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketProjectRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
key, _, err := client.Projects.Get(d.Id())
|
||||||
|
if err != nil {
|
||||||
|
// If the project somehow already destroyed, mark as
|
||||||
|
// succesfully gone
|
||||||
|
if strings.Contains(err.Error(), "404") {
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Error retrieving Project: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("id", key.ID)
|
||||||
|
d.Set("name", key.Name)
|
||||||
|
d.Set("created", key.Created)
|
||||||
|
d.Set("updated", key.Updated)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketProjectUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
updateRequest := &packngo.ProjectUpdateRequest{
|
||||||
|
ID: d.Get("id").(string),
|
||||||
|
Name: d.Get("name").(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr, ok := d.GetOk("payment_method"); ok {
|
||||||
|
updateRequest.PaymentMethod = attr.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Project update: %#v", d.Get("id"))
|
||||||
|
_, _, err := client.Projects.Update(updateRequest)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to update Project: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourcePacketProjectRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketProjectDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
log.Printf("[INFO] Deleting Project: %s", d.Id())
|
||||||
|
_, err := client.Projects.Delete(d.Id())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error deleting SSH key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package packet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/packethost/packngo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccPacketProject_Basic(t *testing.T) {
|
||||||
|
var project packngo.Project
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckPacketProjectDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccCheckPacketProjectConfig_basic,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckPacketProjectExists("packet_project.foobar", &project),
|
||||||
|
testAccCheckPacketProjectAttributes(&project),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"packet_project.foobar", "name", "foobar"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckPacketProjectDestroy(s *terraform.State) error {
|
||||||
|
client := testAccProvider.Meta().(*packngo.Client)
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "packet_project" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := client.Projects.Get(rs.Primary.ID)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
fmt.Errorf("Project cstill exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckPacketProjectAttributes(project *packngo.Project) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
|
||||||
|
if project.Name != "foobar" {
|
||||||
|
return fmt.Errorf("Bad name: %s", project.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckPacketProjectExists(n string, project *packngo.Project) 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 Record ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := testAccProvider.Meta().(*packngo.Client)
|
||||||
|
|
||||||
|
foundProject, _, err := client.Projects.Get(rs.Primary.ID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundProject.ID != rs.Primary.ID {
|
||||||
|
return fmt.Errorf("Record not found: %v - %v", rs.Primary.ID, foundProject)
|
||||||
|
}
|
||||||
|
|
||||||
|
*project = *foundProject
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAccCheckPacketProjectConfig_basic = fmt.Sprintf(`
|
||||||
|
resource "packet_project" "foobar" {
|
||||||
|
name = "foobar"
|
||||||
|
}`)
|
|
@ -0,0 +1,128 @@
|
||||||
|
package packet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"github.com/packethost/packngo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourcePacketSSHKey() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourcePacketSSHKeyCreate,
|
||||||
|
Read: resourcePacketSSHKeyRead,
|
||||||
|
Update: resourcePacketSSHKeyUpdate,
|
||||||
|
Delete: resourcePacketSSHKeyDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"public_key": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"fingerprint": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"created": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"updated": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketSSHKeyCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
createRequest := &packngo.SSHKeyCreateRequest{
|
||||||
|
Label: d.Get("name").(string),
|
||||||
|
Key: d.Get("public_key").(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] SSH Key create configuration: %#v", createRequest)
|
||||||
|
key, _, err := client.SSHKeys.Create(createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating SSH Key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(key.ID)
|
||||||
|
log.Printf("[INFO] SSH Key: %s", key.ID)
|
||||||
|
|
||||||
|
return resourcePacketSSHKeyRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketSSHKeyRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
key, _, err := client.SSHKeys.Get(d.Id())
|
||||||
|
if err != nil {
|
||||||
|
// If the key is somehow already destroyed, mark as
|
||||||
|
// succesfully gone
|
||||||
|
if strings.Contains(err.Error(), "404") {
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Error retrieving SSH key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("id", key.ID)
|
||||||
|
d.Set("name", key.Label)
|
||||||
|
d.Set("public_key", key.Key)
|
||||||
|
d.Set("fingerprint", key.FingerPrint)
|
||||||
|
d.Set("created", key.Created)
|
||||||
|
d.Set("updated", key.Updated)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
updateRequest := &packngo.SSHKeyUpdateRequest{
|
||||||
|
ID: d.Get("id").(string),
|
||||||
|
Label: d.Get("name").(string),
|
||||||
|
Key: d.Get("public_key").(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] SSH key update: %#v", d.Get("id"))
|
||||||
|
_, _, err := client.SSHKeys.Update(updateRequest)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to update SSH key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourcePacketSSHKeyRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePacketSSHKeyDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*packngo.Client)
|
||||||
|
|
||||||
|
log.Printf("[INFO] Deleting SSH key: %s", d.Id())
|
||||||
|
_, err := client.SSHKeys.Delete(d.Id())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error deleting SSH key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package packet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/packethost/packngo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccPacketSSHKey_Basic(t *testing.T) {
|
||||||
|
var key packngo.SSHKey
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckPacketSSHKeyDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccCheckPacketSSHKeyConfig_basic,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckPacketSSHKeyExists("packet_ssh_key.foobar", &key),
|
||||||
|
testAccCheckPacketSSHKeyAttributes(&key),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"packet_ssh_key.foobar", "name", "foobar"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"packet_ssh_key.foobar", "public_key", testAccValidPublicKey),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckPacketSSHKeyDestroy(s *terraform.State) error {
|
||||||
|
client := testAccProvider.Meta().(*packngo.Client)
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "packet_ssh_key" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := client.SSHKeys.Get(rs.Primary.ID)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
fmt.Errorf("SSH key still exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckPacketSSHKeyAttributes(key *packngo.SSHKey) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
|
||||||
|
if key.Label != "foobar" {
|
||||||
|
return fmt.Errorf("Bad name: %s", key.Label)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckPacketSSHKeyExists(n string, key *packngo.SSHKey) 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 Record ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := testAccProvider.Meta().(*packngo.Client)
|
||||||
|
|
||||||
|
foundKey, _, err := client.SSHKeys.Get(rs.Primary.ID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundKey.ID != rs.Primary.ID {
|
||||||
|
return fmt.Errorf("SSh Key not found: %v - %v", rs.Primary.ID, foundKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
*key = *foundKey
|
||||||
|
|
||||||
|
fmt.Printf("key: %v", key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAccCheckPacketSSHKeyConfig_basic = fmt.Sprintf(`
|
||||||
|
resource "packet_ssh_key" "foobar" {
|
||||||
|
name = "foobar"
|
||||||
|
public_key = "%s"
|
||||||
|
}`, testAccValidPublicKey)
|
||||||
|
|
||||||
|
var testAccValidPublicKey = strings.TrimSpace(`
|
||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR
|
||||||
|
`)
|
|
@ -0,0 +1,47 @@
|
||||||
|
---
|
||||||
|
layout: "packet"
|
||||||
|
page_title: "Provider: Packet"
|
||||||
|
sidebar_current: "docs-packet-index"
|
||||||
|
description: |-
|
||||||
|
The Packet provider is used to interact with the resources supported by Packet. The provider needs to be configured with the proper credentials before it can be used.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Packet Provider
|
||||||
|
|
||||||
|
The Packet provider is used to interact with the resources supported by Packet.
|
||||||
|
The provider needs to be configured with the proper credentials before it can be used.
|
||||||
|
|
||||||
|
Use the navigation to the left to read about the available resources.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
# Configure the Packet Provider
|
||||||
|
provider "packet" {
|
||||||
|
auth_token = "${var.auth_token}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a project
|
||||||
|
resource "packet_project" "tf_project_1" {
|
||||||
|
name = "My First Terraform Project"
|
||||||
|
payment_method = "PAYMENT_METHOD_ID"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a device and add it to tf_project_1
|
||||||
|
resource "packet_device" "web1" {
|
||||||
|
hostname = "tf.coreos2"
|
||||||
|
plan = "baremetal_1"
|
||||||
|
facility = "ewr1"
|
||||||
|
operating_system = "coreos_stable"
|
||||||
|
billing_cycle = "hourly"
|
||||||
|
project_id = "${packet_project.tf_project_1.id}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `auth_token` - (Required) This is your Packet API Auth token. This can also be specified
|
||||||
|
with the `PACKET_AUTH_TOKEN` shell environment variable.
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
---
|
||||||
|
layout: "packet"
|
||||||
|
page_title: "Packet: packet_device"
|
||||||
|
sidebar_current: "docs-packet-resource-device"
|
||||||
|
description: |-
|
||||||
|
Provides a Packet device resource. This can be used to create, modify, and delete devices.
|
||||||
|
---
|
||||||
|
|
||||||
|
# packet\_device
|
||||||
|
|
||||||
|
Provides a Packet device resource. This can be used to create,
|
||||||
|
modify, and delete devices.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
# Create a device and add it to tf_project_1
|
||||||
|
resource "packet_device" "web1" {
|
||||||
|
hostname = "tf.coreos2"
|
||||||
|
plan = "baremetal_1"
|
||||||
|
facility = "ewr1"
|
||||||
|
operating_system = "coreos_stable"
|
||||||
|
billing_cycle = "hourly"
|
||||||
|
project_id = "${packet_project.tf_project_1.id}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `hostname` - (Required) The device name
|
||||||
|
* `project_id` - (Required) The id of the project in which to create the device
|
||||||
|
* `operating_system` - (Required) The operating system slug
|
||||||
|
* `facility` - (Required) The facility in which to create the device
|
||||||
|
* `plan` - (Required) The config type slug
|
||||||
|
* `billing_cycle` - (Required) monthly or hourly
|
||||||
|
* `user_data` (Optional) - A string of the desired User Data for the device.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `id` - The ID of the device
|
||||||
|
* `hostname`- The hostname of the device
|
||||||
|
* `project_id`- The Id of the project the device belonds to
|
||||||
|
* `facility` - The facility the device is in
|
||||||
|
* `plan` - The config type of the device
|
||||||
|
* `network` - The private and public v4 and v6 IPs assigned to the device
|
||||||
|
* `locked` - Is the device locked
|
||||||
|
* `billing_cycle` - The billing cycle of the device (monthly or hourly)
|
||||||
|
* `operating_system` - The operating system running on the device
|
||||||
|
* `status` - The status of the device
|
||||||
|
* `created` - The timestamp for when the device was created
|
||||||
|
* `updated` - The timestamp for the last time the device was udpated
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
layout: "packet"
|
||||||
|
page_title: "Packet: packet_ssh_key"
|
||||||
|
sidebar_current: "docs-packet-resource-project"
|
||||||
|
description: |-
|
||||||
|
Provides a Packet Project resource.
|
||||||
|
---
|
||||||
|
|
||||||
|
# packet\_project
|
||||||
|
|
||||||
|
Provides a Packet Project resource to allow you manage devices
|
||||||
|
in your projects.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
# Create a new Project
|
||||||
|
resource "packet_project" "tf_project_1" {
|
||||||
|
name = "Terraform Fun"
|
||||||
|
payment_method = "payment-method-id"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `name` - (Required) The name of the SSH key for identification
|
||||||
|
* `payment_method` - (Required) The id of the payment method on file to use for services created
|
||||||
|
on this project.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `id` - The unique ID of the key
|
||||||
|
* `payment_method` - The id of the payment method on file to use for services created
|
||||||
|
on this project.
|
||||||
|
* `created` - The timestamp for when the SSH key was created
|
||||||
|
* `updated` - The timestamp for the last time the SSH key was udpated
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
layout: "packet"
|
||||||
|
page_title: "Packet: packet_ssh_key"
|
||||||
|
sidebar_current: "docs-packet-resource-ssh-key"
|
||||||
|
description: |-
|
||||||
|
Provides a Packet SSH key resource.
|
||||||
|
---
|
||||||
|
|
||||||
|
# packet\_ssh_key
|
||||||
|
|
||||||
|
Provides a Packet SSH key resource to allow you manage SSH
|
||||||
|
keys on your account. All ssh keys on your account are loaded on
|
||||||
|
all new devices, they do not have to be explicitly declared on
|
||||||
|
device creation.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
# Create a new SSH key
|
||||||
|
resource "packet_ssh_key" "key1" {
|
||||||
|
name = "terraform-1"
|
||||||
|
public_key = "${file("/home/terraform/.ssh/id_rsa.pub")}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `name` - (Required) The name of the SSH key for identification
|
||||||
|
* `public_key` - (Required) The public key. If this is a file, it
|
||||||
|
can be read using the file interpolation function
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `id` - The unique ID of the key
|
||||||
|
* `name` - The name of the SSH key
|
||||||
|
* `public_key` - The text of the public key
|
||||||
|
* `fingerprint` - The fingerprint of the SSH key
|
||||||
|
* `created` - The timestamp for when the SSH key was created
|
||||||
|
* `updated` - The timestamp for the last time the SSH key was udpated
|
Loading…
Reference in New Issue