From b862cd2ccb4593a7dc618e2a2bc53d6289661963 Mon Sep 17 00:00:00 2001 From: Jasmin Gacic Date: Thu, 2 Feb 2017 14:26:14 +0100 Subject: [PATCH] Terraform provider ProfitBricks - Data Sources (#11520) * 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 * Added ProfitBricks Data Sources * Added documentation * Updated to REST v3: - nat parameter for Nics - availabilityZone for Volumes Minor code clean up * Minor code clean up * Fixed typo in volume documentation * make fmt * Addressing requested changes * Added a step in load balancer tests in CheckDestroy where we are making sure that the test doesn't leave dangling resources in ProfitBricks * Changed expected image name * Fixed data center test Code clean up --- builtin/providers/profitbricks/config.go | 5 +- .../profitbricks/data_source_datacenter.go | 69 ++++++++++ .../data_source_datacenter_test.go | 48 +++++++ .../profitbricks/data_source_image.go | 102 ++++++++++++++ .../profitbricks/data_source_image_test.go | 36 +++++ .../profitbricks/data_source_location.go | 73 ++++++++++ .../profitbricks/data_source_location_test.go | 32 +++++ builtin/providers/profitbricks/provider.go | 14 +- .../resource_profitbricks_datacenter.go | 26 ++-- .../resource_profitbricks_firewall.go | 12 -- .../resource_profitbricks_ipblock.go | 9 -- .../profitbricks/resource_profitbricks_lan.go | 21 ++- .../resource_profitbricks_loadbalancer.go | 9 -- ...resource_profitbricks_loadbalancer_test.go | 6 +- .../profitbricks/resource_profitbricks_nic.go | 45 +++--- .../resource_profitbricks_server.go | 128 +++++++++++------- .../resource_profitbricks_server_test.go | 8 +- .../resource_profitbricks_volume.go | 65 +++++---- .../resource_profitbricks_volume_test.go | 8 +- .../profitbricks-sdk-go/config.go | 8 +- .../profitbricks-sdk-go/location.go | 3 +- .../profitbricks/profitbricks-sdk-go/nic.go | 1 + .../profitbricks/profitbricks-sdk-go/paths.go | 26 ---- .../profitbricks/profitbricks-sdk-go/req.go | 12 +- .../profitbricks/profitbricks-sdk-go/resp.go | 31 +---- .../profitbricks-sdk-go/test_helpers.go | 37 ++++- .../profitbricks-sdk-go/volume.go | 1 + vendor/vendor.json | 6 +- .../d/profitbricks_datacenter.html.markdown | 29 ++++ .../d/profitbricks_image.html.markdown | 35 +++++ .../d/profitbricks_location.html.markdown | 29 ++++ .../r/profitbricks_nic.html.markdown | 2 +- .../r/profitbricks_volume.html.markdown | 6 +- 33 files changed, 716 insertions(+), 226 deletions(-) create mode 100644 builtin/providers/profitbricks/data_source_datacenter.go create mode 100644 builtin/providers/profitbricks/data_source_datacenter_test.go create mode 100644 builtin/providers/profitbricks/data_source_image.go create mode 100644 builtin/providers/profitbricks/data_source_image_test.go create mode 100644 builtin/providers/profitbricks/data_source_location.go create mode 100644 builtin/providers/profitbricks/data_source_location_test.go create mode 100644 website/source/docs/providers/profitbricks/d/profitbricks_datacenter.html.markdown create mode 100644 website/source/docs/providers/profitbricks/d/profitbricks_image.html.markdown create mode 100644 website/source/docs/providers/profitbricks/d/profitbricks_location.html.markdown diff --git a/builtin/providers/profitbricks/config.go b/builtin/providers/profitbricks/config.go index 957bc137c..d3b74f2fe 100644 --- a/builtin/providers/profitbricks/config.go +++ b/builtin/providers/profitbricks/config.go @@ -7,6 +7,7 @@ import ( type Config struct { Username string Password string + Endpoint string Retries int } @@ -14,6 +15,8 @@ type Config struct { func (c *Config) Client() (*Config, error) { profitbricks.SetAuth(c.Username, c.Password) profitbricks.SetDepth("5") - + if len(c.Endpoint) > 0 { + profitbricks.SetEndpoint(c.Endpoint) + } return c, nil } diff --git a/builtin/providers/profitbricks/data_source_datacenter.go b/builtin/providers/profitbricks/data_source_datacenter.go new file mode 100644 index 000000000..4e3cf00d4 --- /dev/null +++ b/builtin/providers/profitbricks/data_source_datacenter.go @@ -0,0 +1,69 @@ +package profitbricks + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/profitbricks/profitbricks-sdk-go" + "log" + "strings" +) + +func dataSourceDataCenter() *schema.Resource { + return &schema.Resource{ + Read: dataSourceDataCenterRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "location": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func dataSourceDataCenterRead(d *schema.ResourceData, meta interface{}) error { + datacenters := profitbricks.ListDatacenters() + + if datacenters.StatusCode > 299 { + return fmt.Errorf("An error occured while fetching datacenters %s", datacenters.Response) + } + + name := d.Get("name").(string) + location, locationOk := d.GetOk("location") + + results := []profitbricks.Datacenter{} + + for _, dc := range datacenters.Items { + if dc.Properties.Name == name || strings.Contains(dc.Properties.Name, name) { + results = append(results, dc) + } + } + + if locationOk { + log.Printf("[INFO] searching dcs by location***********") + locationResults := []profitbricks.Datacenter{} + for _, dc := range results { + if dc.Properties.Location == location.(string) { + locationResults = append(locationResults, dc) + } + } + results = locationResults + } + log.Printf("[INFO] Results length %d *************", len(results)) + + if len(results) > 1 { + log.Printf("[INFO] Results length greater than 1") + return fmt.Errorf("There is more than one datacenters that match the search criteria") + } + + if len(results) == 0 { + return fmt.Errorf("There are no datacenters that match the search criteria") + } + + d.SetId(results[0].Id) + + return nil +} diff --git a/builtin/providers/profitbricks/data_source_datacenter_test.go b/builtin/providers/profitbricks/data_source_datacenter_test.go new file mode 100644 index 000000000..7295313cc --- /dev/null +++ b/builtin/providers/profitbricks/data_source_datacenter_test.go @@ -0,0 +1,48 @@ +package profitbricks + +import ( + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccDataSourceDatacenter_matching(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + + Config: testAccDataSourceProfitBricksDataCenter_matching, + }, + { + + Config: testAccDataSourceProfitBricksDataCenter_matchingWithDataSource, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.profitbricks_datacenter.foobar", "name", "test_name"), + resource.TestCheckResourceAttr("data.profitbricks_datacenter.foobar", "location", "us/las"), + ), + }, + }, + }) + +} + +const testAccDataSourceProfitBricksDataCenter_matching = ` +resource "profitbricks_datacenter" "foobar" { + name = "test_name" + location = "us/las" +} +` + +const testAccDataSourceProfitBricksDataCenter_matchingWithDataSource = ` +resource "profitbricks_datacenter" "foobar" { + name = "test_name" + location = "us/las" +} + +data "profitbricks_datacenter" "foobar" { + name = "${profitbricks_datacenter.foobar.name}" + location = "us/las" +}` diff --git a/builtin/providers/profitbricks/data_source_image.go b/builtin/providers/profitbricks/data_source_image.go new file mode 100644 index 000000000..61d542b5b --- /dev/null +++ b/builtin/providers/profitbricks/data_source_image.go @@ -0,0 +1,102 @@ +package profitbricks + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/profitbricks/profitbricks-sdk-go" + "strings" +) + +func dataSourceImage() *schema.Resource { + return &schema.Resource{ + Read: dataSourceImageRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + }, + "type": { + Type: schema.TypeString, + Optional: true, + }, + "location": { + Type: schema.TypeString, + Optional: true, + }, + "version": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func dataSourceImageRead(d *schema.ResourceData, meta interface{}) error { + profitbricks.SetDepth("5") + + images := profitbricks.ListImages() + + if images.StatusCode > 299 { + return fmt.Errorf("An error occured while fetching ProfitBricks locations %s", images.Response) + } + + name := d.Get("name").(string) + imageType, imageTypeOk := d.GetOk("type") + location, locationOk := d.GetOk("location") + version, versionOk := d.GetOk("version") + + results := []profitbricks.Image{} + + // if version value is present then concatenate name - version + // otherwise search by name or part of the name + if versionOk { + name_ver := fmt.Sprintf("%s-%s", name, version.(string)) + for _, img := range images.Items { + if strings.Contains(strings.ToLower(img.Properties.Name), strings.ToLower(name_ver)) { + results = append(results, img) + } + } + } else { + for _, img := range images.Items { + if strings.Contains(strings.ToLower(img.Properties.Name), strings.ToLower(name)) { + results = append(results, img) + } + } + } + + if imageTypeOk { + imageTypeResults := []profitbricks.Image{} + for _, img := range results { + if img.Properties.ImageType == imageType.(string) { + imageTypeResults = append(imageTypeResults, img) + } + + } + results = imageTypeResults + } + + if locationOk { + locationResults := []profitbricks.Image{} + for _, img := range results { + if img.Properties.Location == location.(string) { + locationResults = append(locationResults, img) + } + + } + results = locationResults + } + + if len(results) > 1 { + return fmt.Errorf("There is more than one image that match the search criteria") + } + + if len(results) == 0 { + return fmt.Errorf("There are no images that match the search criteria") + } + + d.Set("name", results[0].Properties.Name) + + d.SetId(results[0].Id) + + return nil +} diff --git a/builtin/providers/profitbricks/data_source_image_test.go b/builtin/providers/profitbricks/data_source_image_test.go new file mode 100644 index 000000000..3efe6d325 --- /dev/null +++ b/builtin/providers/profitbricks/data_source_image_test.go @@ -0,0 +1,36 @@ +package profitbricks + +import ( + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccDataSourceImage_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + + Config: testAccDataSourceProfitBricksImage_basic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.profitbricks_image.img", "location", "us/las"), + resource.TestCheckResourceAttr("data.profitbricks_image.img", "name", "Ubuntu-16.04-LTS-server-2017-02-01"), + resource.TestCheckResourceAttr("data.profitbricks_image.img", "type", "HDD"), + ), + }, + }, + }) + +} + +const testAccDataSourceProfitBricksImage_basic = ` + data "profitbricks_image" "img" { + name = "Ubuntu" + type = "HDD" + version = "16" + location = "us/las" + } + ` diff --git a/builtin/providers/profitbricks/data_source_location.go b/builtin/providers/profitbricks/data_source_location.go new file mode 100644 index 000000000..f55d60872 --- /dev/null +++ b/builtin/providers/profitbricks/data_source_location.go @@ -0,0 +1,73 @@ +package profitbricks + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/profitbricks/profitbricks-sdk-go" + "log" + "strings" +) + +func dataSourceLocation() *schema.Resource { + return &schema.Resource{ + Read: dataSourceLocationRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + }, + "feature": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func dataSourceLocationRead(d *schema.ResourceData, meta interface{}) error { + locations := profitbricks.ListLocations() + + if locations.StatusCode > 299 { + return fmt.Errorf("An error occured while fetching ProfitBricks locations %s", locations.Response) + } + + name, nameOk := d.GetOk("name") + feature, featureOk := d.GetOk("features") + + if !nameOk && !featureOk { + return fmt.Errorf("Either 'name' or 'feature' must be provided.") + } + results := []profitbricks.Location{} + + for _, loc := range locations.Items { + if loc.Properties.Name == name.(string) || strings.Contains(loc.Properties.Name, name.(string)) { + results = append(results, loc) + } + } + + if featureOk { + locationResults := []profitbricks.Location{} + for _, loc := range results { + for _, f := range loc.Properties.Features { + if f == feature.(string) { + locationResults = append(locationResults, loc) + } + } + } + results = locationResults + } + log.Printf("[INFO] Results length %d *************", len(results)) + + if len(results) > 1 { + log.Printf("[INFO] Results length greater than 1") + return fmt.Errorf("There is more than one location that match the search criteria") + } + + if len(results) == 0 { + return fmt.Errorf("There are no locations that match the search criteria") + } + + d.SetId(results[0].Id) + + return nil +} diff --git a/builtin/providers/profitbricks/data_source_location_test.go b/builtin/providers/profitbricks/data_source_location_test.go new file mode 100644 index 000000000..f1411f354 --- /dev/null +++ b/builtin/providers/profitbricks/data_source_location_test.go @@ -0,0 +1,32 @@ +package profitbricks + +import ( + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccDataSourceLocation_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + + Config: testAccDataSourceProfitBricksLocation_basic, + Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("data.profitbricks_location.loc", "id", "de/fkb"), + resource.TestCheckResourceAttr("data.profitbricks_location.loc", "name", "karlsruhe"), + ), + }, + }, + }) + +} + +const testAccDataSourceProfitBricksLocation_basic = ` + data "profitbricks_location" "loc" { + name = "karlsruhe" + feature = "SSD" + } + ` diff --git a/builtin/providers/profitbricks/provider.go b/builtin/providers/profitbricks/provider.go index e53b79521..dc0c782a0 100644 --- a/builtin/providers/profitbricks/provider.go +++ b/builtin/providers/profitbricks/provider.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" + "github.com/profitbricks/profitbricks-sdk-go" ) // Provider returns a schema.Provider for DigitalOcean. @@ -22,6 +23,12 @@ func Provider() terraform.ResourceProvider { DefaultFunc: schema.EnvDefaultFunc("PROFITBRICKS_PASSWORD", nil), Description: "Profitbricks password for API operations.", }, + "endpoint": { + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("PROFITBRICKS_API_URL", profitbricks.Endpoint), + Description: "Profitbricks REST API URL.", + }, "retries": { Type: schema.TypeInt, Optional: true, @@ -39,7 +46,11 @@ func Provider() terraform.ResourceProvider { "profitbricks_server": resourceProfitBricksServer(), "profitbricks_volume": resourceProfitBricksVolume(), }, - + DataSourcesMap: map[string]*schema.Resource{ + "profitbricks_datacenter": dataSourceDataCenter(), + "profitbricks_location": dataSourceLocation(), + "profitbricks_image": dataSourceImage(), + }, ConfigureFunc: providerConfigure, } } @@ -57,6 +68,7 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { config := Config{ Username: d.Get("username").(string), Password: d.Get("password").(string), + Endpoint: d.Get("endpoint").(string), Retries: d.Get("retries").(int), } diff --git a/builtin/providers/profitbricks/resource_profitbricks_datacenter.go b/builtin/providers/profitbricks/resource_profitbricks_datacenter.go index c5a42ec40..d402b57dc 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_datacenter.go +++ b/builtin/providers/profitbricks/resource_profitbricks_datacenter.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/profitbricks/profitbricks-sdk-go" "log" + "regexp" "runtime" "strings" "time" @@ -38,9 +39,6 @@ func resourceProfitBricksDatacenter() *schema.Resource { } 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), @@ -69,9 +67,6 @@ func resourceProfitBricksDatacenterCreate(d *schema.ResourceData, meta interface } 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) @@ -84,10 +79,6 @@ func resourceProfitBricksDatacenterRead(d *schema.ResourceData, meta interface{} } func resourceProfitBricksDatacenterUpdate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - profitbricks.SetAuth(config.Username, config.Password) - obj := profitbricks.DatacenterProperties{} if d.HasChange("name") { @@ -107,9 +98,6 @@ func resourceProfitBricksDatacenterUpdate(d *schema.ResourceData, meta interface } func resourceProfitBricksDatacenterDelete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - profitbricks.SetAuth(config.Username, config.Password) dcid := d.Id() resp := profitbricks.DeleteDatacenter(dcid) @@ -126,9 +114,12 @@ func resourceProfitBricksDatacenterDelete(d *schema.ResourceData, meta interface func waitTillProvisioned(meta interface{}, path string) error { config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) + waitCount := 50 - for i := 0; i < config.Retries; i++ { + if config.Retries != 0 { + waitCount = config.Retries + } + for i := 0; i < waitCount; i++ { request := profitbricks.GetRequestStatus(path) pc, _, _, ok := runtime.Caller(1) details := runtime.FuncForPC(pc) @@ -182,3 +173,8 @@ func getImageId(dcId string, imageName string, imageType string) string { } return "" } + +func IsValidUUID(uuid string) bool { + r := regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$") + return r.MatchString(uuid) +} diff --git a/builtin/providers/profitbricks/resource_profitbricks_firewall.go b/builtin/providers/profitbricks/resource_profitbricks_firewall.go index e4fcd24bf..4559a0428 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_firewall.go +++ b/builtin/providers/profitbricks/resource_profitbricks_firewall.go @@ -81,9 +81,6 @@ func resourceProfitBricksFirewall() *schema.Resource { } func resourceProfitBricksFirewallCreate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - fw := profitbricks.FirewallRule{ Properties: profitbricks.FirewallruleProperties{ Protocol: d.Get("protocol").(string), @@ -131,9 +128,6 @@ func resourceProfitBricksFirewallCreate(d *schema.ResourceData, meta interface{} } func resourceProfitBricksFirewallRead(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - fw := profitbricks.GetFirewallRule(d.Get("datacenter_id").(string), d.Get("server_id").(string), d.Get("nic_id").(string), d.Id()) if fw.StatusCode > 299 { @@ -155,9 +149,6 @@ func resourceProfitBricksFirewallRead(d *schema.ResourceData, meta interface{}) } func resourceProfitBricksFirewallUpdate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - properties := profitbricks.FirewallruleProperties{} if d.HasChange("name") { @@ -215,9 +206,6 @@ func resourceProfitBricksFirewallUpdate(d *schema.ResourceData, meta interface{} } func resourceProfitBricksFirewallDelete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - resp := profitbricks.DeleteFirewallRule(d.Get("datacenter_id").(string), d.Get("server_id").(string), d.Get("nic_id").(string), d.Id()) if resp.StatusCode > 299 { diff --git a/builtin/providers/profitbricks/resource_profitbricks_ipblock.go b/builtin/providers/profitbricks/resource_profitbricks_ipblock.go index 008a12050..7ba2fdab7 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_ipblock.go +++ b/builtin/providers/profitbricks/resource_profitbricks_ipblock.go @@ -34,9 +34,6 @@ func resourceProfitBricksIPBlock() *schema.Resource { } func resourceProfitBricksIPBlockCreate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - ipblock := profitbricks.IpBlock{ Properties: profitbricks.IpBlockProperties{ Size: d.Get("size").(int), @@ -59,9 +56,6 @@ func resourceProfitBricksIPBlockCreate(d *schema.ResourceData, meta interface{}) } func resourceProfitBricksIPBlockRead(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - ipblock := profitbricks.GetIpBlock(d.Id()) if ipblock.StatusCode > 299 { @@ -78,9 +72,6 @@ func resourceProfitBricksIPBlockRead(d *schema.ResourceData, meta interface{}) e } func resourceProfitBricksIPBlockDelete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - resp := profitbricks.ReleaseIpBlock(d.Id()) if resp.StatusCode > 299 { return fmt.Errorf("An error occured while releasing an ipblock ID: %s %s", d.Id(), string(resp.Body)) diff --git a/builtin/providers/profitbricks/resource_profitbricks_lan.go b/builtin/providers/profitbricks/resource_profitbricks_lan.go index 28849928d..3a3725bd0 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_lan.go +++ b/builtin/providers/profitbricks/resource_profitbricks_lan.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/profitbricks/profitbricks-sdk-go" "log" + "time" ) func resourceProfitBricksLan() *schema.Resource { @@ -32,9 +33,6 @@ func resourceProfitBricksLan() *schema.Resource { } func resourceProfitBricksLanCreate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - profitbricks.SetDepth("5") request := profitbricks.Lan{ Properties: profitbricks.LanProperties{ Public: d.Get("public").(bool), @@ -64,9 +62,6 @@ func resourceProfitBricksLanCreate(d *schema.ResourceData, meta interface{}) err } func resourceProfitBricksLanRead(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - lan := profitbricks.GetLan(d.Get("datacenter_id").(string), d.Id()) if lan.StatusCode > 299 { @@ -80,9 +75,6 @@ func resourceProfitBricksLanRead(d *schema.ResourceData, meta interface{}) error } func resourceProfitBricksLanUpdate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - properties := &profitbricks.LanProperties{} if d.HasChange("public") { _, newValue := d.GetChange("public") @@ -107,10 +99,15 @@ func resourceProfitBricksLanUpdate(d *schema.ResourceData, meta interface{}) err } func resourceProfitBricksLanDelete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - resp := profitbricks.DeleteLan(d.Get("datacenter_id").(string), d.Id()) + if resp.StatusCode > 299 { + //try again in 20 seconds + time.Sleep(60 * time.Second) + resp = profitbricks.DeleteLan(d.Get("datacenter_id").(string), d.Id()) + if resp.StatusCode > 299 && resp.StatusCode != 404 { + return fmt.Errorf("An error occured while deleting a lan dcId %s ID %s %s", d.Get("datacenter_id").(string), d.Id(), string(resp.Body)) + } + } if resp.Headers.Get("Location") != "" { err := waitTillProvisioned(meta, resp.Headers.Get("Location")) diff --git a/builtin/providers/profitbricks/resource_profitbricks_loadbalancer.go b/builtin/providers/profitbricks/resource_profitbricks_loadbalancer.go index c58af3e33..a905831c4 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_loadbalancer.go +++ b/builtin/providers/profitbricks/resource_profitbricks_loadbalancer.go @@ -40,9 +40,6 @@ func resourceProfitBricksLoadbalancer() *schema.Resource { } func resourceProfitBricksLoadbalancerCreate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - lb := profitbricks.Loadbalancer{ Properties: profitbricks.LoadbalancerProperties{ Name: d.Get("name").(string), @@ -86,9 +83,6 @@ func resourceProfitBricksLoadbalancerRead(d *schema.ResourceData, meta interface } func resourceProfitBricksLoadbalancerUpdate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - properties := profitbricks.LoadbalancerProperties{} if d.HasChange("name") { _, new := d.GetChange("name") @@ -129,9 +123,6 @@ func resourceProfitBricksLoadbalancerUpdate(d *schema.ResourceData, meta interfa } func resourceProfitBricksLoadbalancerDelete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - resp := profitbricks.DeleteLoadbalancer(d.Get("datacenter_id").(string), d.Id()) if resp.StatusCode > 299 { diff --git a/builtin/providers/profitbricks/resource_profitbricks_loadbalancer_test.go b/builtin/providers/profitbricks/resource_profitbricks_loadbalancer_test.go index 3c7a57026..4fd3a1b02 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_loadbalancer_test.go +++ b/builtin/providers/profitbricks/resource_profitbricks_loadbalancer_test.go @@ -48,7 +48,11 @@ func testAccCheckDProfitBricksLoadbalancerDestroyCheck(s *terraform.State) error resp := profitbricks.GetLoadbalancer(rs.Primary.Attributes["datacenter_id"], rs.Primary.ID) if resp.StatusCode < 299 { - return fmt.Errorf("Firewall still exists %s %s", rs.Primary.ID, resp.Response) + resp := profitbricks.DeleteDatacenter(rs.Primary.Attributes["datacenter_id"]) + + if resp.StatusCode > 299 { + return fmt.Errorf("Firewall still exists %s %s", rs.Primary.ID, string(resp.Body)) + } } } diff --git a/builtin/providers/profitbricks/resource_profitbricks_nic.go b/builtin/providers/profitbricks/resource_profitbricks_nic.go index 2000282a8..a2f914cb0 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_nic.go +++ b/builtin/providers/profitbricks/resource_profitbricks_nic.go @@ -16,15 +16,15 @@ func resourceProfitBricksNic() *schema.Resource { Delete: resourceProfitBricksNicDelete, Schema: map[string]*schema.Schema{ - "lan": &schema.Schema{ + "lan": { Type: schema.TypeInt, Required: true, }, - "name": &schema.Schema{ + "name": { Type: schema.TypeString, Optional: true, }, - "dhcp": &schema.Schema{ + "dhcp": { Type: schema.TypeBool, Optional: true, }, @@ -32,10 +32,19 @@ func resourceProfitBricksNic() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "ips": { + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, "firewall_active": { Type: schema.TypeBool, Optional: true, }, + "nat": { + Type: schema.TypeBool, + Optional: true, + }, "server_id": { Type: schema.TypeString, Required: true, @@ -49,9 +58,6 @@ func resourceProfitBricksNic() *schema.Resource { } func resourceProfitBricksNicCreate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - nic := profitbricks.Nic{ Properties: profitbricks.NicProperties{ Lan: d.Get("lan").(int), @@ -69,6 +75,14 @@ func resourceProfitBricksNicCreate(d *schema.ResourceData, meta interface{}) err ips := strings.Split(raw, ",") nic.Properties.Ips = ips } + if _, ok := d.GetOk("firewall_active"); ok { + raw := d.Get("firewall_active").(bool) + nic.Properties.FirewallActive = raw + } + if _, ok := d.GetOk("nat"); ok { + raw := d.Get("nat").(bool) + nic.Properties.Nat = raw + } nic = profitbricks.CreateNic(d.Get("datacenter_id").(string), d.Get("server_id").(string), nic) if nic.StatusCode > 299 { @@ -93,27 +107,20 @@ func resourceProfitBricksNicCreate(d *schema.ResourceData, meta interface{}) err } func resourceProfitBricksNicRead(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - profitbricks.SetDepth("5") - nic := profitbricks.GetNic(d.Get("datacenter_id").(string), d.Get("server_id").(string), d.Id()) if nic.StatusCode > 299 { return fmt.Errorf("Error occured while fetching a nic ID %s %s", d.Id(), nic.Response) } - log.Printf("[INFO] LAN ON NIC: %q", nic.Properties.Lan) + log.Printf("[INFO] LAN ON NIC: %d", nic.Properties.Lan) d.Set("dhcp", nic.Properties.Dhcp) d.Set("lan", nic.Properties.Lan) d.Set("name", nic.Properties.Name) - d.Set("ip", nic.Properties.Ips) + d.Set("ips", nic.Properties.Ips) return nil } func resourceProfitBricksNicUpdate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - properties := profitbricks.NicProperties{} if d.HasChange("name") { @@ -134,6 +141,11 @@ func resourceProfitBricksNicUpdate(d *schema.ResourceData, meta interface{}) err ips := strings.Split(raw.(string), ",") properties.Ips = ips } + if d.HasChange("nat") { + _, raw := d.GetChange("nat") + nat := raw.(bool) + properties.Nat = nat + } nic := profitbricks.PatchNic(d.Get("datacenter_id").(string), d.Get("server_id").(string), d.Id(), properties) @@ -148,9 +160,6 @@ func resourceProfitBricksNicUpdate(d *schema.ResourceData, meta interface{}) err } func resourceProfitBricksNicDelete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - resp := profitbricks.DeleteNic(d.Get("datacenter_id").(string), d.Get("server_id").(string), d.Id()) err := waitTillProvisioned(meta, resp.Headers.Get("Location")) if err != nil { diff --git a/builtin/providers/profitbricks/resource_profitbricks_server.go b/builtin/providers/profitbricks/resource_profitbricks_server.go index b3f707854..d81c2d45d 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_server.go +++ b/builtin/providers/profitbricks/resource_profitbricks_server.go @@ -66,6 +66,10 @@ func resourceProfitBricksServer() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "primary_ip": { + Type: schema.TypeString, + Computed: true, + }, "datacenter_id": { Type: schema.TypeString, Required: true, @@ -97,7 +101,8 @@ func resourceProfitBricksServer() *schema.Resource { Optional: true, }, "ssh_key_path": { - Type: schema.TypeString, + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, }, "bus": { @@ -108,6 +113,10 @@ func resourceProfitBricksServer() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "availability_zone": { + Type: schema.TypeString, + Optional: true, + }, }, }, }, @@ -133,6 +142,15 @@ func resourceProfitBricksServer() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "ips": { + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, + "nat": { + Type: schema.TypeBool, + Optional: true, + }, "firewall_active": { Type: schema.TypeBool, Optional: true, @@ -167,6 +185,11 @@ func resourceProfitBricksServer() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "ips": { + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, "port_range_start": { Type: schema.TypeInt, Optional: true, @@ -207,9 +230,6 @@ func resourceProfitBricksServer() *schema.Resource { } func resourceProfitBricksServerCreate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - request := profitbricks.Server{ Properties: profitbricks.ServerProperties{ Name: d.Get("name").(string), @@ -233,15 +253,21 @@ func resourceProfitBricksServerCreate(d *schema.ResourceData, meta interface{}) for _, raw := range volumeRaw { rawMap := raw.(map[string]interface{}) - var imagePassword, sshkey_path string - var image, licenceType string + var imagePassword string + //Can be one file or a list of files + var sshkey_path []interface{} + var image, licenceType, availabilityZone string if rawMap["image_name"] != nil { - image = getImageId(d.Get("datacenter_id").(string), rawMap["image_name"].(string), rawMap["disk_type"].(string)) - if image == "" { - dc := profitbricks.GetDatacenter(d.Get("datacenter_id").(string)) - return fmt.Errorf("Image '%s' doesn't exist. in location %s", rawMap["image_name"], dc.Properties.Location) + if !IsValidUUID(rawMap["image_name"].(string)) { + image = getImageId(d.Get("datacenter_id").(string), rawMap["image_name"].(string), rawMap["disk_type"].(string)) + if image == "" { + dc := profitbricks.GetDatacenter(d.Get("datacenter_id").(string)) + return fmt.Errorf("Image '%s' doesn't exist. in location %s", rawMap["image_name"], dc.Properties.Location) + } + } else { + image = rawMap["image_name"].(string) } } if rawMap["licence_type"] != nil { @@ -252,24 +278,27 @@ func resourceProfitBricksServerCreate(d *schema.ResourceData, meta interface{}) imagePassword = rawMap["image_password"].(string) } if rawMap["ssh_key_path"] != nil { - sshkey_path = rawMap["ssh_key_path"].(string) + sshkey_path = rawMap["ssh_key_path"].([]interface{}) } if rawMap["image_name"] != nil { - if imagePassword == "" && sshkey_path == "" { - return fmt.Errorf("'image_password' and 'ssh_key_path' are not provided.") + if imagePassword == "" && len(sshkey_path) == 0 { + return fmt.Errorf("Either 'image_password' or 'ssh_key_path' must be provided.") } } - var publicKey string - var err error - if sshkey_path != "" { - log.Println("[DEBUG] GETTING THE KEY") - _, publicKey, err = getSshKey(d, sshkey_path) - if err != nil { - return fmt.Errorf("Error fetching sshkeys (%s)", err) + var publicKeys []string + if len(sshkey_path) != 0 { + for _, path := range sshkey_path { + log.Printf("[DEBUG] Reading file %s", path) + publicKey, err := readPublicKey(path.(string)) + if err != nil { + return fmt.Errorf("Error fetching sshkey from file (%s) (%s)", path, err.Error()) + } + publicKeys = append(publicKeys, publicKey) } - d.Set("sshkey", publicKey) } - + if rawMap["availability_zone"] != nil { + availabilityZone = rawMap["availability_zone"].(string) + } if image == "" && licenceType == "" { return fmt.Errorf("Either 'image', or 'licenceType' must be set.") } @@ -277,27 +306,26 @@ func resourceProfitBricksServerCreate(d *schema.ResourceData, meta interface{}) request.Entities = &profitbricks.ServerEntities{ Volumes: &profitbricks.Volumes{ Items: []profitbricks.Volume{ - profitbricks.Volume{ + { Properties: profitbricks.VolumeProperties{ - Name: rawMap["name"].(string), - Size: rawMap["size"].(int), - Type: rawMap["disk_type"].(string), - ImagePassword: imagePassword, - Image: image, - Bus: rawMap["bus"].(string), - LicenceType: licenceType, + Name: rawMap["name"].(string), + Size: rawMap["size"].(int), + Type: rawMap["disk_type"].(string), + ImagePassword: imagePassword, + Image: image, + Bus: rawMap["bus"].(string), + LicenceType: licenceType, + AvailabilityZone: availabilityZone, }, }, }, }, } - log.Printf("[DEBUG] PUBLIC KEY %s", publicKey) - - if publicKey == "" { + if len(publicKeys) == 0 { request.Entities.Volumes.Items[0].Properties.SshKeys = nil } else { - request.Entities.Volumes.Items[0].Properties.SshKeys = []string{publicKey} + request.Entities.Volumes.Items[0].Properties.SshKeys = publicKeys } } @@ -328,6 +356,9 @@ func resourceProfitBricksServerCreate(d *schema.ResourceData, meta interface{}) nic.Properties.Ips = ips } } + if rawMap["nat"] != nil { + nic.Properties.Nat = rawMap["nat"].(bool) + } request.Entities.Nics = &profitbricks.Nics{ Items: []profitbricks.Nic{ nic, @@ -416,9 +447,6 @@ func resourceProfitBricksServerCreate(d *schema.ResourceData, meta interface{}) } func resourceProfitBricksServerRead(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - dcId := d.Get("datacenter_id").(string) server := profitbricks.GetServer(dcId, d.Id()) @@ -456,9 +484,6 @@ func resourceProfitBricksServerRead(d *schema.ResourceData, meta interface{}) er } func resourceProfitBricksServerUpdate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - dcId := d.Get("datacenter_id").(string) request := profitbricks.ServerProperties{} @@ -484,7 +509,6 @@ func resourceProfitBricksServerUpdate(d *schema.ResourceData, meta interface{}) request.CpuFamily = n.(string) } server := profitbricks.PatchServer(dcId, d.Id(), request) - log.Println("[INFO] hlab hlab", request) //Volume stuff if d.HasChange("volume") { @@ -508,7 +532,6 @@ func resourceProfitBricksServerUpdate(d *schema.ResourceData, meta interface{}) } volume = profitbricks.PatchVolume(d.Get("datacenter_id").(string), server.Entities.Volumes.Items[0].Id, properties) - log.Println("[INFO] blah blah", properties) if volume.StatusCode > 299 { return fmt.Errorf("Error patching volume (%s) (%s)", d.Id(), volume.Response) @@ -553,10 +576,12 @@ func resourceProfitBricksServerUpdate(d *schema.ResourceData, meta interface{}) if rawMap["dhcp"] != nil { properties.Dhcp = rawMap["dhcp"].(bool) } + if rawMap["nat"] != nil { + properties.Nat = rawMap["nat"].(bool) + } } nic = profitbricks.PatchNic(d.Get("datacenter_id").(string), server.Id, server.Entities.Nics.Items[0].Id, properties) - log.Println("[INFO] blah blah", properties) if nic.StatusCode > 299 { return fmt.Errorf( @@ -577,9 +602,6 @@ func resourceProfitBricksServerUpdate(d *schema.ResourceData, meta interface{}) } func resourceProfitBricksServerDelete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - dcId := d.Get("datacenter_id").(string) server := profitbricks.GetServer(dcId, d.Id()) @@ -605,6 +627,19 @@ func resourceProfitBricksServerDelete(d *schema.ResourceData, meta interface{}) return nil } +//Reads public key from file and returns key string iff valid +func readPublicKey(path string) (key string, err error) { + bytes, err := ioutil.ReadFile(path) + if err != nil { + return "", err + } + pubKey, _, _, _, err := ssh.ParseAuthorizedKey(bytes) + if err != nil { + return "", err + } + return string(ssh.MarshalAuthorizedKey(pubKey)[:]), nil +} + func getSshKey(d *schema.ResourceData, path string) (privatekey string, publickey string, err error) { pemBytes, err := ioutil.ReadFile(path) @@ -623,7 +658,6 @@ func getSshKey(d *schema.ResourceData, path string) (privatekey string, publicke if err != nil { return "", "", err } - priv_blk := pem.Block{ Type: "RSA PRIVATE KEY", Headers: nil, diff --git a/builtin/providers/profitbricks/resource_profitbricks_server_test.go b/builtin/providers/profitbricks/resource_profitbricks_server_test.go index d4891ec5c..c14fc39e2 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_server_test.go +++ b/builtin/providers/profitbricks/resource_profitbricks_server_test.go @@ -141,6 +141,12 @@ resource "profitbricks_datacenter" "foobar" { location = "us/las" } +resource "profitbricks_lan" "webserver_lan" { + datacenter_id = "${profitbricks_datacenter.foobar.id}" + public = true + name = "public" +} + resource "profitbricks_server" "webserver" { name = "updated" datacenter_id = "${profitbricks_datacenter.foobar.id}" @@ -156,7 +162,7 @@ resource "profitbricks_server" "webserver" { image_password = "test1234" } nic { - lan = "1" + lan = "${profitbricks_lan.webserver_lan.id}" dhcp = true firewall_active = true firewall { diff --git a/builtin/providers/profitbricks/resource_profitbricks_volume.go b/builtin/providers/profitbricks/resource_profitbricks_volume.go index afc2a7297..6efed8e84 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_volume.go +++ b/builtin/providers/profitbricks/resource_profitbricks_volume.go @@ -1,7 +1,6 @@ package profitbricks import ( - "encoding/json" "fmt" "github.com/hashicorp/terraform/helper/schema" "github.com/profitbricks/profitbricks-sdk-go" @@ -37,7 +36,8 @@ func resourceProfitBricksVolume() *schema.Resource { Optional: true, }, "ssh_key_path": { - Type: schema.TypeString, + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, }, "sshkey": { @@ -52,6 +52,10 @@ func resourceProfitBricksVolume() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "availability_zone": { + Type: schema.TypeString, + Optional: true, + }, "server_id": { Type: schema.TypeString, Required: true, @@ -65,20 +69,17 @@ func resourceProfitBricksVolume() *schema.Resource { } func resourceProfitBricksVolumeCreate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - var err error + var ssh_keypath []interface{} dcId := d.Get("datacenter_id").(string) serverId := d.Get("server_id").(string) - imagePassword := d.Get("image_password").(string) - sshkey := d.Get("ssh_key_path").(string) + ssh_keypath = d.Get("ssh_key_path").([]interface{}) image_name := d.Get("image_name").(string) if image_name != "" { - if imagePassword == "" && sshkey == "" { - return fmt.Errorf("'image_password' and 'sshkey' are not provided.") + if imagePassword == "" && len(ssh_keypath) == 0 { + return fmt.Errorf("Either 'image_password' or 'sshkey' must be provided.") } } @@ -88,19 +89,24 @@ func resourceProfitBricksVolumeCreate(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Either 'image_name', or 'licenceType' must be set.") } - var publicKey string - - if sshkey != "" { - _, publicKey, err = getSshKey(d, sshkey) - if err != nil { - return fmt.Errorf("Error fetching sshkeys (%s)", err) + var publicKeys []string + if len(ssh_keypath) != 0 { + for _, path := range ssh_keypath { + log.Printf("[DEBUG] Reading file %s", path) + publicKey, err := readPublicKey(path.(string)) + if err != nil { + return fmt.Errorf("Error fetching sshkey from file (%s) (%s)", path, err.Error()) + } + publicKeys = append(publicKeys, publicKey) } - d.Set("sshkey", publicKey) } - log.Printf("[INFO] public key: %s", publicKey) - - image := getImageId(d.Get("datacenter_id").(string), image_name, d.Get("disk_type").(string)) + var image string + if !IsValidUUID(image_name) { + image = getImageId(d.Get("datacenter_id").(string), image_name, d.Get("disk_type").(string)) + } else { + image = image_name + } volume := profitbricks.Volume{ Properties: profitbricks.VolumeProperties{ @@ -114,14 +120,17 @@ func resourceProfitBricksVolumeCreate(d *schema.ResourceData, meta interface{}) }, } - if publicKey != "" { - volume.Properties.SshKeys = []string{publicKey} + if len(publicKeys) != 0 { + volume.Properties.SshKeys = publicKeys } else { volume.Properties.SshKeys = nil } - jsn, _ := json.Marshal(volume) - log.Printf("[INFO] volume: %s", string(jsn)) + + if _, ok := d.GetOk("availability_zone"); ok { + raw := d.Get("availability_zone").(string) + volume.Properties.AvailabilityZone = raw + } volume = profitbricks.CreateVolume(dcId, volume) @@ -169,9 +178,6 @@ func resourceProfitBricksVolumeRead(d *schema.ResourceData, meta interface{}) er } func resourceProfitBricksVolumeUpdate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - properties := profitbricks.VolumeProperties{} dcId := d.Get("datacenter_id").(string) @@ -191,6 +197,10 @@ func resourceProfitBricksVolumeUpdate(d *schema.ResourceData, meta interface{}) _, newValue := d.GetChange("bus") properties.Bus = newValue.(string) } + if d.HasChange("availability_zone") { + _, newValue := d.GetChange("availability_zone") + properties.AvailabilityZone = newValue.(string) + } volume := profitbricks.PatchVolume(dcId, d.Id(), properties) err := waitTillProvisioned(meta, volume.Headers.Get("Location")) @@ -216,9 +226,6 @@ func resourceProfitBricksVolumeUpdate(d *schema.ResourceData, meta interface{}) } func resourceProfitBricksVolumeDelete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - profitbricks.SetAuth(config.Username, config.Password) - dcId := d.Get("datacenter_id").(string) resp := profitbricks.DeleteVolume(dcId, d.Id()) diff --git a/builtin/providers/profitbricks/resource_profitbricks_volume_test.go b/builtin/providers/profitbricks/resource_profitbricks_volume_test.go index e1586f024..50ced0e4f 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_volume_test.go +++ b/builtin/providers/profitbricks/resource_profitbricks_volume_test.go @@ -151,6 +151,12 @@ resource "profitbricks_datacenter" "foobar" { location = "us/las" } +resource "profitbricks_lan" "webserver_lan" { + datacenter_id = "${profitbricks_datacenter.foobar.id}" + public = true + name = "public" +} + resource "profitbricks_server" "webserver" { name = "webserver" datacenter_id = "${profitbricks_datacenter.foobar.id}" @@ -166,7 +172,7 @@ resource "profitbricks_server" "webserver" { image_password = "test1234" } nic { - lan = "1" + lan = "${profitbricks_lan.webserver_lan.id}" dhcp = true firewall_active = true firewall { diff --git a/vendor/github.com/profitbricks/profitbricks-sdk-go/config.go b/vendor/github.com/profitbricks/profitbricks-sdk-go/config.go index 567108951..0df1841cf 100644 --- a/vendor/github.com/profitbricks/profitbricks-sdk-go/config.go +++ b/vendor/github.com/profitbricks/profitbricks-sdk-go/config.go @@ -1,7 +1,7 @@ package profitbricks -// Endpoint is the base url for REST requests . -var Endpoint = "https://api.profitbricks.com/rest/v2" +// Endpoint is the base url for REST requests. +var Endpoint = "https://api.profitbricks.com/cloudapi/v3" // Username for authentication . var Username string @@ -21,3 +21,7 @@ func SetAuth(u, p string) { Username = u Passwd = p } + +func SetUserAgent(userAgent string) { + AgentHeader = userAgent +} diff --git a/vendor/github.com/profitbricks/profitbricks-sdk-go/location.go b/vendor/github.com/profitbricks/profitbricks-sdk-go/location.go index 279429464..9a222ca13 100644 --- a/vendor/github.com/profitbricks/profitbricks-sdk-go/location.go +++ b/vendor/github.com/profitbricks/profitbricks-sdk-go/location.go @@ -27,7 +27,8 @@ type Locations struct { } type Properties struct { - Name string `json:"name,omitempty"` + Name string `json:"name,omitempty"` + Features []string `json:"features,omitempty"` } // ListLocations returns location collection data diff --git a/vendor/github.com/profitbricks/profitbricks-sdk-go/nic.go b/vendor/github.com/profitbricks/profitbricks-sdk-go/nic.go index 74764f56b..1d082a64d 100644 --- a/vendor/github.com/profitbricks/profitbricks-sdk-go/nic.go +++ b/vendor/github.com/profitbricks/profitbricks-sdk-go/nic.go @@ -25,6 +25,7 @@ type NicProperties struct { Dhcp bool `json:"dhcp,omitempty"` Lan int `json:"lan,omitempty"` FirewallActive bool `json:"firewallActive,omitempty"` + Nat bool `json:"nat,omitempty"` } type NicEntities struct { diff --git a/vendor/github.com/profitbricks/profitbricks-sdk-go/paths.go b/vendor/github.com/profitbricks/profitbricks-sdk-go/paths.go index 3b3c7f50d..a4ba71822 100644 --- a/vendor/github.com/profitbricks/profitbricks-sdk-go/paths.go +++ b/vendor/github.com/profitbricks/profitbricks-sdk-go/paths.go @@ -45,31 +45,11 @@ func location_path(locid string) string { return location_col_path() + slash(locid) } -// request_col_path returns the string "/requests" -func request_col_path() string { - return slash("requests") -} - -// request_path returns the string "/requests/" -func request_path(requestid string) string { - return request_col_path() + slash(requestid) -} - -// request_status_path returns the string "/requests/status" -func request_status_path(requestid string) string { - return request_path(requestid) + slash("status") -} - // snapshot_col_path returns the string "/snapshots" func snapshot_col_path() string { return slash("snapshots") } -// snapshot_path returns the string "/snapshots/" -func snapshot_path(snapid string) string { - return snapshot_col_path() + slash(snapid) -} - // lan_col_path returns the string "/datacenters//lans" func lan_col_path(dcid string) string { return dc_path(dcid) + slash("lans") @@ -115,12 +95,6 @@ func volume_path(dcid, volid string) string { return volume_col_path(dcid) + slash(volid) } -// lan_nic_col_path returns the string /datacenters//lans//nics -func lan_nic_col(dcid, lanid string) string { - return lan_path(dcid, lanid) + slash("nics") - -} - // balnic_col_path returns the string "/loadbalancers//balancednics" func balnic_col_path(dcid, lbalid string) string { return lbal_path(dcid, lbalid) + slash("balancednics") diff --git a/vendor/github.com/profitbricks/profitbricks-sdk-go/req.go b/vendor/github.com/profitbricks/profitbricks-sdk-go/req.go index b5f21c17d..e1038affc 100644 --- a/vendor/github.com/profitbricks/profitbricks-sdk-go/req.go +++ b/vendor/github.com/profitbricks/profitbricks-sdk-go/req.go @@ -9,10 +9,12 @@ import ( ) //FullHeader is the standard header to include with all http requests except is_patch and is_command -const FullHeader = "application/vnd.profitbricks.resource+json" +const FullHeader = "application/json" + +var AgentHeader = "profitbricks-sdk-go/3.0.1" //PatchHeader is used with is_patch . -const PatchHeader = "application/vnd.profitbricks.partial-properties+json" +const PatchHeader = "application/json" //CommandHeader is used with is_command const CommandHeader = "application/x-www-form-urlencoded" @@ -32,7 +34,8 @@ func SetDepth(newdepth string) string { func mk_url(path string) string { if strings.HasPrefix(path, "http") { //REMOVE AFTER TESTING - path := strings.Replace(path, "https://api.profitbricks.com/rest/v2", Endpoint, 1) + //FIXME @jasmin Is this still relevant? + path := strings.Replace(path, "https://api.profitbricks.com/cloudapi/v3", Endpoint, 1) // END REMOVE return path } @@ -48,6 +51,7 @@ func mk_url(path string) string { func do(req *http.Request) Resp { client := &http.Client{} req.SetBasicAuth(Username, Passwd) + req.Header.Add("User-Agent", AgentHeader) resp, err := client.Do(req) if err != nil { panic(err) @@ -67,6 +71,7 @@ func is_delete(path string) Resp { url := mk_url(path) req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("Content-Type", FullHeader) + req.Header.Add("User-Agent", AgentHeader) return do(req) } @@ -76,5 +81,6 @@ func is_command(path string, jason string) Resp { body := json.RawMessage(jason) req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body)) req.Header.Add("Content-Type", CommandHeader) + req.Header.Add("User-Agent", AgentHeader) return do(req) } diff --git a/vendor/github.com/profitbricks/profitbricks-sdk-go/resp.go b/vendor/github.com/profitbricks/profitbricks-sdk-go/resp.go index 12d68e0d2..20975d4e1 100644 --- a/vendor/github.com/profitbricks/profitbricks-sdk-go/resp.go +++ b/vendor/github.com/profitbricks/profitbricks-sdk-go/resp.go @@ -15,13 +15,6 @@ func MkJson(i interface{}) string { return string(jason) } -// MetaData is a map for metadata returned in a Resp.Body -type StringMap map[string]string - -type StringIfaceMap map[string]interface{} - -type StringCollectionMap map[string]Collection - // Resp is the struct returned by all Rest request functions type Resp struct { Req *http.Request @@ -36,26 +29,4 @@ func (r *Resp) PrintHeaders() { fmt.Println(key, " : ", value[0]) } -} - -type Id_Type_Href struct { - Id string `json:"id"` - Type string `json:"type"` - Href string `json:"href"` -} - -type MetaData StringIfaceMap - -type Instance struct { - Id_Type_Href - MetaData StringMap `json:"metaData,omitempty"` - Properties StringIfaceMap `json:"properties,omitempty"` - Entities StringCollectionMap `json:"entities,omitempty"` - Resp Resp `json:"-"` -} - -type Collection struct { - Id_Type_Href - Items []Instance `json:"items,omitempty"` - Resp Resp `json:"-"` -} +} \ No newline at end of file diff --git a/vendor/github.com/profitbricks/profitbricks-sdk-go/test_helpers.go b/vendor/github.com/profitbricks/profitbricks-sdk-go/test_helpers.go index 35df1f929..91f01027c 100644 --- a/vendor/github.com/profitbricks/profitbricks-sdk-go/test_helpers.go +++ b/vendor/github.com/profitbricks/profitbricks-sdk-go/test_helpers.go @@ -3,6 +3,8 @@ package profitbricks import ( "fmt" "time" + "strings" + "os" ) func mkdcid(name string) string { @@ -55,18 +57,49 @@ func mknic(lbal_dcid, serverid string) string { fmt.Println("created a nic with id " + resp.Id) fmt.Println(resp.StatusCode) fmt.Println("===========================") + waitTillProvisioned(resp.Headers.Get("Location")) return resp.Id } func waitTillProvisioned(path string) { - waitCount := 20 + waitCount := 120 fmt.Println(path) for i := 0; i < waitCount; i++ { request := GetRequestStatus(path) if request.Metadata.Status == "DONE" { break } - time.Sleep(10 * time.Second) + time.Sleep(1 * time.Second) i++ } } + +func getImageId(location string, imageName string, imageType string) string { + if imageName == "" { + return "" + } + + SetAuth(os.Getenv("PROFITBRICKS_USERNAME"), os.Getenv("PROFITBRICKS_PASSWORD")) + + images := ListImages() + if images.StatusCode > 299 { + fmt.Printf("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 == location && i.Properties.Public == true { + return i.Id + } + } + } + return "" +} diff --git a/vendor/github.com/profitbricks/profitbricks-sdk-go/volume.go b/vendor/github.com/profitbricks/profitbricks-sdk-go/volume.go index 2982448d7..1f22eba32 100644 --- a/vendor/github.com/profitbricks/profitbricks-sdk-go/volume.go +++ b/vendor/github.com/profitbricks/profitbricks-sdk-go/volume.go @@ -21,6 +21,7 @@ type VolumeProperties struct { Name string `json:"name,omitempty"` Type string `json:"type,omitempty"` Size int `json:"size,omitempty"` + AvailabilityZone string `json:"availabilityZone,omitempty"` Image string `json:"image,omitempty"` ImagePassword string `json:"imagePassword,omitempty"` SshKeys []string `json:"sshKeys,omitempty"` diff --git a/vendor/vendor.json b/vendor/vendor.json index 9eff8e41c..cf48a6168 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -2331,10 +2331,10 @@ "revisionTime": "2016-10-29T09:36:37Z" }, { - "checksumSHA1": "atkFfe1CrxF+Cz4p4Z78RMXC0Kg=", + "checksumSHA1": "WPDz/Ed0OwaP7F/HvzZQ8OwT3fM=", "path": "github.com/profitbricks/profitbricks-sdk-go", - "revision": "ccbdd70b24ec5c6cd000a34aba264d708c106af9", - "revisionTime": "2016-07-28T11:13:56Z" + "revision": "b279e1adcaf1c9cae4d6520c1bdbbf4674e7806e", + "revisionTime": "2017-01-11T22:35:15Z" }, { "checksumSHA1": "hcyoctYs0NjsvAXXVRc4mVt+FBg=", diff --git a/website/source/docs/providers/profitbricks/d/profitbricks_datacenter.html.markdown b/website/source/docs/providers/profitbricks/d/profitbricks_datacenter.html.markdown new file mode 100644 index 000000000..fab58ad2a --- /dev/null +++ b/website/source/docs/providers/profitbricks/d/profitbricks_datacenter.html.markdown @@ -0,0 +1,29 @@ +--- +layout: "profitbricks" +page_title: "ProfitBricks : profitbricks_datacenter" +sidebar_current: "docs-profitbricks-datasource-datacenter" +description: |- + Get information on a ProfitBricks Data Centers +--- + +# profitbricks\_datacenter + +The data centers data source can be used to search for and return an existing Virtual Data Center. You can provide a string for the name and location parameters which will be compared with provisioned Virtual Data Centers. If a single match is found, it will be returned. If your search results in multiple matches, an error will be generated. When this happens, please refine your search string so that it is specific enough to return only one result. + +## Example Usage + +``` +data "profitbricks_datacenter" "dc_example" { + name = "test_dc" + location = "us" +} +``` + +## Argument Reference + + * `name` - (Required) Name or part of the name of an existing Virtual Data Center that you want to search for. + * `location` - (Optional) Id of the existing Virtual Data Center's location. + +## Attributes Reference + + * `id` - UUID of the Virtual Data Center diff --git a/website/source/docs/providers/profitbricks/d/profitbricks_image.html.markdown b/website/source/docs/providers/profitbricks/d/profitbricks_image.html.markdown new file mode 100644 index 000000000..378c507ed --- /dev/null +++ b/website/source/docs/providers/profitbricks/d/profitbricks_image.html.markdown @@ -0,0 +1,35 @@ +--- +layout: "profitbricks" +page_title: "ProfitBricks : profitbrick_image" +sidebar_current: "docs-profitbricks-datasource-image" +description: |- + Get information on a ProfitBricks Images +--- + +# profitbricks\_image + +The images data source can be used to search for and return an existing image which can then be used to provision a server. + +## Example Usage + +``` +data "profitbricks_image" "image_example" { + name = "Ubuntu" + type = "HDD" + version = "14" + location = "location_id" +} +``` + +## Argument Reference + + * `name` - (Required) Name or part of the name of an existing image that you want to search for. + * `version` - (Optional) Version of the image (see details below). + * `location` - (Optional) Id of the existing image's location. + * `type` - (Optional) The image type, HDD or CD-ROM. + +If both "name" and "version" are provided the plugin will concatenate the two strings in this format [name]-[version]. + +## Attributes Reference + + * `id` - UUID of the image diff --git a/website/source/docs/providers/profitbricks/d/profitbricks_location.html.markdown b/website/source/docs/providers/profitbricks/d/profitbricks_location.html.markdown new file mode 100644 index 000000000..42949113e --- /dev/null +++ b/website/source/docs/providers/profitbricks/d/profitbricks_location.html.markdown @@ -0,0 +1,29 @@ +--- +layout: "profitbricks" +page_title: "ProfitBricks : profitbrick_location" +sidebar_current: "docs-profitbricks-datasource-location" +description: |- + Get information on a ProfitBricks Locations +--- + +# profitbricks\_location + +The locations data source can be used to search for and return an existing location which can then be used elsewhere in the configuration. + +## Example Usage + +``` +data "profitbricks_location" "loc1" { + name = "karlsruhe" + feature = "SSD" +} +``` + +## Argument Reference + + * `name` - (Required) Name or part of the location name to search for. + * `feature` - (Optional) A desired feature that the location must be able to provide. + +## Attributes Reference + + * `id` - UUID of the location diff --git a/website/source/docs/providers/profitbricks/r/profitbricks_nic.html.markdown b/website/source/docs/providers/profitbricks/r/profitbricks_nic.html.markdown index 8c2ed01dc..96ff0e2d9 100644 --- a/website/source/docs/providers/profitbricks/r/profitbricks_nic.html.markdown +++ b/website/source/docs/providers/profitbricks/r/profitbricks_nic.html.markdown @@ -31,4 +31,4 @@ resource "profitbricks_nic" "example" { * `dhcp` - (Optional) [boolean] * `ip` - (Optional) [string] IP assigned to the NIC. * `firewall_active` - (Optional) [boolean] If this resource is set to true and is nested under a server resource firewall, with open SSH port, resource must be nested under the nic. - \ No newline at end of file +* `nat` - (Optional) [boolean] Boolean value indicating if the private IP address has outbound access to the public internet. diff --git a/website/source/docs/providers/profitbricks/r/profitbricks_volume.html.markdown b/website/source/docs/providers/profitbricks/r/profitbricks_volume.html.markdown index df3549946..34ff5bfb3 100644 --- a/website/source/docs/providers/profitbricks/r/profitbricks_volume.html.markdown +++ b/website/source/docs/providers/profitbricks/r/profitbricks_volume.html.markdown @@ -21,7 +21,7 @@ resource "profitbricks_volume" "example" { image_name = "${var.ubuntu}" size = 5 disk_type = "HDD" - sshkey_path = "${var.private_key_path}" + ssh_key_path = "${var.private_key_path}" bus = "VIRTIO" } ``` @@ -33,7 +33,9 @@ resource "profitbricks_volume" "example" { * `disk_type` - (Required) [string] The volume type, HDD or SSD. * `bus` - (Required) [boolean] The bus type of the volume. * `size` - (Required)[integer] The size of the volume in GB. +* `ssh_key_path` - (Required)[list] List of paths to files containing a public SSH key that will be injected into ProfitBricks provided Linux images. Required if `image_password` is not provided. * `image_password` - [string] Required if `sshkey_path` is not provided. * `image_name` - [string] The image or snapshot ID. It is required if `licence_type` is not provided. * `licence_type` - [string] Required if `image_name` is not provided. -* `name` - (Optional) [string] The name of the volume. \ No newline at end of file +* `name` - (Optional) [string] The name of the volume. +* `availability_zone` - (Optional) [string] The storage availability zone assigned to the volume. AUTO, ZONE_1, ZONE_2, or ZONE_3 \ No newline at end of file