diff --git a/builtin/providers/digitalocean/config.go b/builtin/providers/digitalocean/config.go index b32be5938..9c9e4cd20 100644 --- a/builtin/providers/digitalocean/config.go +++ b/builtin/providers/digitalocean/config.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "log" "net/http" "net/http/httputil" @@ -55,7 +56,7 @@ func waitForAction(client *godo.Client, action *godo.Action) error { pending = "in-progress" target = "completed" refreshfn = func() (result interface{}, state string, err error) { - a, _, err := client.Actions.Get(action.ID) + a, _, err := client.Actions.Get(context.Background(), action.ID) if err != nil { return nil, "", err } diff --git a/builtin/providers/digitalocean/datasource_digitalocean_image.go b/builtin/providers/digitalocean/datasource_digitalocean_image.go index d4023daf8..e47e422bf 100644 --- a/builtin/providers/digitalocean/datasource_digitalocean_image.go +++ b/builtin/providers/digitalocean/datasource_digitalocean_image.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "fmt" "strconv" @@ -54,7 +55,7 @@ func dataSourceDigitalOceanImageRead(d *schema.ResourceData, meta interface{}) e opts := &godo.ListOptions{} - images, _, err := client.Images.ListUser(opts) + images, _, err := client.Images.ListUser(context.Background(), opts) if err != nil { d.SetId("") return err diff --git a/builtin/providers/digitalocean/datasource_digitalocean_image_test.go b/builtin/providers/digitalocean/datasource_digitalocean_image_test.go index ab77c75ae..18a1e0865 100644 --- a/builtin/providers/digitalocean/datasource_digitalocean_image_test.go +++ b/builtin/providers/digitalocean/datasource_digitalocean_image_test.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "fmt" "log" "regexp" @@ -70,7 +71,7 @@ func takeSnapshotsOfDroplet(rInt int, droplet *godo.Droplet, snapshotsId *[]int) return err } } - retrieveDroplet, _, err := client.Droplets.Get((*droplet).ID) + retrieveDroplet, _, err := client.Droplets.Get(context.Background(), (*droplet).ID) if err != nil { return err } @@ -81,7 +82,7 @@ func takeSnapshotsOfDroplet(rInt int, droplet *godo.Droplet, snapshotsId *[]int) func takeSnapshotOfDroplet(rInt, sInt int, droplet *godo.Droplet) error { client := testAccProvider.Meta().(*godo.Client) - action, _, err := client.DropletActions.Snapshot((*droplet).ID, fmt.Sprintf("snap-%d-%d", rInt, sInt)) + action, _, err := client.DropletActions.Snapshot(context.Background(), (*droplet).ID, fmt.Sprintf("snap-%d-%d", rInt, sInt)) if err != nil { return err } @@ -96,7 +97,7 @@ func deleteSnapshots(snapshotsId *[]int) resource.TestCheckFunc { snapshots := *snapshotsId for _, value := range snapshots { log.Printf("XXX Deleting %d", value) - _, err := client.Images.Delete(value) + _, err := client.Images.Delete(context.Background(), value) if err != nil { return err } diff --git a/builtin/providers/digitalocean/loadbalancer.go b/builtin/providers/digitalocean/loadbalancer.go index abf868055..efacf7ae5 100644 --- a/builtin/providers/digitalocean/loadbalancer.go +++ b/builtin/providers/digitalocean/loadbalancer.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "fmt" "github.com/digitalocean/godo" @@ -9,7 +10,7 @@ import ( func loadbalancerStateRefreshFunc(client *godo.Client, loadbalancerId string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - lb, _, err := client.LoadBalancers.Get(loadbalancerId) + lb, _, err := client.LoadBalancers.Get(context.Background(), loadbalancerId) if err != nil { return nil, "", fmt.Errorf("Error issuing read request in LoadbalancerStateRefreshFunc to DigitalOcean for Load Balancer '%s': %s", loadbalancerId, err) } diff --git a/builtin/providers/digitalocean/provider.go b/builtin/providers/digitalocean/provider.go index e885e0823..9d0b9af1b 100644 --- a/builtin/providers/digitalocean/provider.go +++ b/builtin/providers/digitalocean/provider.go @@ -22,6 +22,7 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ + "digitalocean_certificate": resourceDigitalOceanCertificate(), "digitalocean_domain": resourceDigitalOceanDomain(), "digitalocean_droplet": resourceDigitalOceanDroplet(), "digitalocean_floating_ip": resourceDigitalOceanFloatingIp(), diff --git a/builtin/providers/digitalocean/resource_digitalocean_certificate.go b/builtin/providers/digitalocean/resource_digitalocean_certificate.go new file mode 100644 index 000000000..264ab5297 --- /dev/null +++ b/builtin/providers/digitalocean/resource_digitalocean_certificate.go @@ -0,0 +1,116 @@ +package digitalocean + +import ( + "context" + "fmt" + "log" + + "github.com/digitalocean/godo" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceDigitalOceanCertificate() *schema.Resource { + return &schema.Resource{ + Create: resourceDigitalOceanCertificateCreate, + Read: resourceDigitalOceanCertificateRead, + Delete: resourceDigitalOceanCertificateDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "private_key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "leaf_certificate": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "certificate_chain": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "not_after": { + Type: schema.TypeString, + Computed: true, + }, + + "sha1_fingerprint": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func buildCertificateRequest(d *schema.ResourceData) (*godo.CertificateRequest, error) { + req := &godo.CertificateRequest{ + Name: d.Get("name").(string), + PrivateKey: d.Get("private_key").(string), + LeafCertificate: d.Get("leaf_certificate").(string), + CertificateChain: d.Get("certificate_chain").(string), + } + + return req, nil +} + +func resourceDigitalOceanCertificateCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*godo.Client) + + log.Printf("[INFO] Create a Certificate Request") + + certReq, err := buildCertificateRequest(d) + if err != nil { + return err + } + + log.Printf("[DEBUG] Certificate Create: %#v", certReq) + cert, _, err := client.Certificates.Create(context.Background(), certReq) + if err != nil { + return fmt.Errorf("Error creating Certificate: %s", err) + } + + d.SetId(cert.ID) + + return resourceDigitalOceanCertificateRead(d, meta) +} + +func resourceDigitalOceanCertificateRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*godo.Client) + + log.Printf("[INFO] Reading the details of the Certificate %s", d.Id()) + cert, _, err := client.Certificates.Get(context.Background(), d.Id()) + if err != nil { + return fmt.Errorf("Error retrieving Certificate: %s", err) + } + + d.Set("name", cert.Name) + d.Set("not_after", cert.NotAfter) + d.Set("sha1_fingerprint", cert.SHA1Fingerprint) + + return nil + +} + +func resourceDigitalOceanCertificateDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*godo.Client) + + log.Printf("[INFO] Deleting Certificate: %s", d.Id()) + _, err := client.Certificates.Delete(context.Background(), d.Id()) + if err != nil { + return fmt.Errorf("Error deleting Certificate: %s", err) + } + + return nil + +} diff --git a/builtin/providers/digitalocean/resource_digitalocean_certificate_test.go b/builtin/providers/digitalocean/resource_digitalocean_certificate_test.go new file mode 100644 index 000000000..270d01a24 --- /dev/null +++ b/builtin/providers/digitalocean/resource_digitalocean_certificate_test.go @@ -0,0 +1,114 @@ +package digitalocean + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/digitalocean/godo" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDigitalOceanCertificate_Basic(t *testing.T) { + var cert godo.Certificate + rInt := acctest.RandInt() + leafCertMaterial, privateKeyMaterial, err := acctest.RandTLSCert("Acme Co") + if err != nil { + t.Fatalf("Cannot generate test TLS certificate: %s", err) + } + rootCertMaterial, _, err := acctest.RandTLSCert("Acme Go") + if err != nil { + t.Fatalf("Cannot generate test TLS certificate: %s", err) + } + certChainMaterial := fmt.Sprintf("%s\n%s", strings.TrimSpace(rootCertMaterial), leafCertMaterial) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDigitalOceanCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckDigitalOceanCertificateConfig_basic(rInt, privateKeyMaterial, leafCertMaterial, certChainMaterial), + Check: resource.ComposeTestCheckFunc( + testAccCheckDigitalOceanCertificateExists("digitalocean_certificate.foobar", &cert), + resource.TestCheckResourceAttr( + "digitalocean_certificate.foobar", "name", fmt.Sprintf("certificate-%d", rInt)), + resource.TestCheckResourceAttr( + "digitalocean_certificate.foobar", "private_key", fmt.Sprintf("%s\n", privateKeyMaterial)), + resource.TestCheckResourceAttr( + "digitalocean_certificate.foobar", "leaf_certificate", fmt.Sprintf("%s\n", leafCertMaterial)), + resource.TestCheckResourceAttr( + "digitalocean_certificate.foobar", "certificate_chain", fmt.Sprintf("%s\n", certChainMaterial)), + ), + }, + }, + }) +} + +func testAccCheckDigitalOceanCertificateDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*godo.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "digitalocean_certificate" { + continue + } + + _, _, err := client.Certificates.Get(context.Background(), rs.Primary.ID) + + if err != nil && !strings.Contains(err.Error(), "404") { + return fmt.Errorf( + "Error waiting for certificate (%s) to be destroyed: %s", + rs.Primary.ID, err) + } + } + + return nil +} + +func testAccCheckDigitalOceanCertificateExists(n string, cert *godo.Certificate) 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 Certificate ID is set") + } + + client := testAccProvider.Meta().(*godo.Client) + + c, _, err := client.Certificates.Get(context.Background(), rs.Primary.ID) + + if err != nil { + return err + } + + if c.ID != rs.Primary.ID { + return fmt.Errorf("Certificate not found") + } + + *cert = *c + + return nil + } +} + +func testAccCheckDigitalOceanCertificateConfig_basic(rInt int, privateKeyMaterial, leafCert, certChain string) string { + return fmt.Sprintf(` +resource "digitalocean_certificate" "foobar" { + name = "certificate-%d" + private_key = <> Resources