Skip to content

Commit

Permalink
Terraform ProfitBricks Builder (#7943)
Browse files Browse the repository at this point in the history
* Terraform ProfitBricks Builder

* make fmt

* Merge remote-tracking branch 'upstream/master' into terraform-provider-profitbricks

# Conflicts:
#	command/internal_plugin_list.go

* Addressing PR remarks

* Removed importers
  • Loading branch information
Jasmin Gacic authored and stack72 committed Jan 18, 2017
1 parent 07feb05 commit 7e9c850
Show file tree
Hide file tree
Showing 51 changed files with 5,854 additions and 0 deletions.
12 changes: 12 additions & 0 deletions builtin/bins/provider-profitbricks/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package provider_profitbricks

import (
"github.com/hashicorp/terraform/builtin/providers/profitbricks"
"github.com/hashicorp/terraform/plugin"
)

func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: profitbricks.Provider,
})
}
19 changes: 19 additions & 0 deletions builtin/providers/profitbricks/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package profitbricks

import (
"github.com/profitbricks/profitbricks-sdk-go"
)

type Config struct {
Username string
Password string
Retries int
}

// Client() returns a new client for accessing digital ocean.
func (c *Config) Client() (*Config, error) {
profitbricks.SetAuth(c.Username, c.Password)
profitbricks.SetDepth("5")

return c, nil
}
64 changes: 64 additions & 0 deletions builtin/providers/profitbricks/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package profitbricks

import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)

// Provider returns a schema.Provider for DigitalOcean.
func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"username": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("PROFITBRICKS_USERNAME", nil),
Description: "Profitbricks username for API operations.",
},
"password": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("PROFITBRICKS_PASSWORD", nil),
Description: "Profitbricks password for API operations.",
},
"retries": {
Type: schema.TypeInt,
Optional: true,
Default: 50,
},
},

ResourcesMap: map[string]*schema.Resource{
"profitbricks_datacenter": resourceProfitBricksDatacenter(),
"profitbricks_ipblock": resourceProfitBricksIPBlock(),
"profitbricks_firewall": resourceProfitBricksFirewall(),
"profitbricks_lan": resourceProfitBricksLan(),
"profitbricks_loadbalancer": resourceProfitBricksLoadbalancer(),
"profitbricks_nic": resourceProfitBricksNic(),
"profitbricks_server": resourceProfitBricksServer(),
"profitbricks_volume": resourceProfitBricksVolume(),
},

ConfigureFunc: providerConfigure,
}
}

func providerConfigure(d *schema.ResourceData) (interface{}, error) {

if _, ok := d.GetOk("username"); !ok {
return nil, fmt.Errorf("ProfitBricks username has not been provided.")
}

if _, ok := d.GetOk("password"); !ok {
return nil, fmt.Errorf("ProfitBricks password has not been provided.")
}

config := Config{
Username: d.Get("username").(string),
Password: d.Get("password").(string),
Retries: d.Get("retries").(int),
}

return config.Client()
}
39 changes: 39 additions & 0 deletions builtin/providers/profitbricks/provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package profitbricks

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{
"profitbricks": 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("PROFITBRICKS_USERNAME"); v == "" {
t.Fatal("PROFITBRICKS_USERNAME must be set for acceptance tests")
}

if v := os.Getenv("PROFITBRICKS_PASSWORD"); v == "" {
t.Fatal("PROFITBRICKS_PASSWORD must be set for acceptance tests")
}
}
184 changes: 184 additions & 0 deletions builtin/providers/profitbricks/resource_profitbricks_datacenter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package profitbricks

import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"github.com/profitbricks/profitbricks-sdk-go"
"log"
"runtime"
"strings"
"time"
)

func resourceProfitBricksDatacenter() *schema.Resource {
return &schema.Resource{
Create: resourceProfitBricksDatacenterCreate,
Read: resourceProfitBricksDatacenterRead,
Update: resourceProfitBricksDatacenterUpdate,
Delete: resourceProfitBricksDatacenterDelete,
Schema: map[string]*schema.Schema{

//Datacenter parameters
"name": {
Type: schema.TypeString,
Required: true,
},

"location": {
Type: schema.TypeString,
Required: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
},
}
}

func resourceProfitBricksDatacenterCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
profitbricks.SetAuth(config.Username, config.Password)

datacenter := profitbricks.Datacenter{
Properties: profitbricks.DatacenterProperties{
Name: d.Get("name").(string),
Location: d.Get("location").(string),
},
}

if attr, ok := d.GetOk("description"); ok {
datacenter.Properties.Description = attr.(string)
}
dc := profitbricks.CreateDatacenter(datacenter)

if dc.StatusCode > 299 {
return fmt.Errorf(
"Error creating data center (%s) (%s)", d.Id(), dc.Response)
}
d.SetId(dc.Id)

log.Printf("[INFO] DataCenter Id: %s", d.Id())

err := waitTillProvisioned(meta, dc.Headers.Get("Location"))
if err != nil {
return err
}
return resourceProfitBricksDatacenterRead(d, meta)
}

func resourceProfitBricksDatacenterRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

profitbricks.SetAuth(config.Username, config.Password)
datacenter := profitbricks.GetDatacenter(d.Id())
if datacenter.StatusCode > 299 {
return fmt.Errorf("Error while fetching a data center ID %s %s", d.Id(), datacenter.Response)
}

d.Set("name", datacenter.Properties.Name)
d.Set("location", datacenter.Properties.Location)
d.Set("description", datacenter.Properties.Description)
return nil
}

func resourceProfitBricksDatacenterUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

profitbricks.SetAuth(config.Username, config.Password)

obj := profitbricks.DatacenterProperties{}

if d.HasChange("name") {
_, newName := d.GetChange("name")

obj.Name = newName.(string)
}

if d.HasChange("description") {
_, newDescription := d.GetChange("description")
obj.Description = newDescription.(string)
}

resp := profitbricks.PatchDatacenter(d.Id(), obj)
waitTillProvisioned(meta, resp.Headers.Get("Location"))
return resourceProfitBricksDatacenterRead(d, meta)
}

func resourceProfitBricksDatacenterDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

profitbricks.SetAuth(config.Username, config.Password)
dcid := d.Id()
resp := profitbricks.DeleteDatacenter(dcid)

if resp.StatusCode > 299 {
return fmt.Errorf("An error occured while deleting the data center ID %s %s", d.Id(), string(resp.Body))
}
err := waitTillProvisioned(meta, resp.Headers.Get("Location"))
if err != nil {
return err
}
d.SetId("")
return nil
}

func waitTillProvisioned(meta interface{}, path string) error {
config := meta.(*Config)
profitbricks.SetAuth(config.Username, config.Password)

for i := 0; i < config.Retries; i++ {
request := profitbricks.GetRequestStatus(path)
pc, _, _, ok := runtime.Caller(1)
details := runtime.FuncForPC(pc)
if ok && details != nil {
log.Printf("[DEBUG] Called from %s", details.Name())
}
log.Printf("[DEBUG] Request status: %s", request.Metadata.Status)
log.Printf("[DEBUG] Request status path: %s", path)

if request.Metadata.Status == "DONE" {
return nil
}
if request.Metadata.Status == "FAILED" {

return fmt.Errorf("Request failed with following error: %s", request.Metadata.Message)
}
time.Sleep(10 * time.Second)
i++
}
return fmt.Errorf("Timeout has expired")
}

func getImageId(dcId string, imageName string, imageType string) string {
if imageName == "" {
return ""
}
dc := profitbricks.GetDatacenter(dcId)
if dc.StatusCode > 299 {
log.Print(fmt.Errorf("Error while fetching a data center ID %s %s", dcId, dc.Response))
}

images := profitbricks.ListImages()
if images.StatusCode > 299 {
log.Print(fmt.Errorf("Error while fetching the list of images %s", images.Response))
}

if len(images.Items) > 0 {
for _, i := range images.Items {
imgName := ""
if i.Properties.Name != "" {
imgName = i.Properties.Name
}

if imageType == "SSD" {
imageType = "HDD"
}
if imgName != "" && strings.Contains(strings.ToLower(imgName), strings.ToLower(imageName)) && i.Properties.ImageType == imageType && i.Properties.Location == dc.Properties.Location && i.Properties.Public == true {
return i.Id
}
}
}
return ""
}
Loading

0 comments on commit 7e9c850

Please sign in to comment.