provider/ovh: new provider (#12669)
* vendor: add go-ovh * provider/ovh: new provider * provider/ovh: Adding PublicCloud User Resource * provider/ovh: Adding VRack Attachment Resource * provider/ovh: Adding PublicCloud Network Resource * provider/ovh: Adding PublicCloud Subnet Resource * provider/ovh: Adding PublicCloud Regions Datasource * provider/ovh: Adding PublicCloud Region Datasource * provider/ovh: Fix Acctests using project's regions
This commit is contained in:
parent
048767d591
commit
21d8125d5c
|
@ -0,0 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/builtin/providers/ovh"
|
||||
"github.com/hashicorp/terraform/plugin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
plugin.Serve(&plugin.ServeOpts{
|
||||
ProviderFunc: ovh.Provider,
|
||||
})
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/ovh/go-ovh/ovh"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Endpoint string
|
||||
ApplicationKey string
|
||||
ApplicationSecret string
|
||||
ConsumerKey string
|
||||
OVHClient *ovh.Client
|
||||
}
|
||||
|
||||
/* type used to verify client access to ovh api
|
||||
*/
|
||||
type PartialMe struct {
|
||||
Firstname string `json:"firstname"`
|
||||
}
|
||||
|
||||
func clientDefault(c *Config) (*ovh.Client, error) {
|
||||
client, err := ovh.NewClient(
|
||||
c.Endpoint,
|
||||
c.ApplicationKey,
|
||||
c.ApplicationSecret,
|
||||
c.ConsumerKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Config) loadAndValidate() error {
|
||||
validEndpoint := false
|
||||
|
||||
ovhEndpoints := [2]string{ovh.OvhEU, ovh.OvhCA}
|
||||
|
||||
for _, e := range ovhEndpoints {
|
||||
if ovh.Endpoints[c.Endpoint] == e {
|
||||
validEndpoint = true
|
||||
}
|
||||
}
|
||||
|
||||
if !validEndpoint {
|
||||
return fmt.Errorf("%s must be one of %#v endpoints\n", c.Endpoint, ovh.Endpoints)
|
||||
}
|
||||
|
||||
targetClient, err := clientDefault(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting ovh client: %q\n", err)
|
||||
}
|
||||
|
||||
var me PartialMe
|
||||
err = targetClient.Get("/me", &me)
|
||||
if err != nil {
|
||||
return fmt.Errorf("OVH client seems to be misconfigured: %q\n", err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Logged in on OVH API as %s!", me.Firstname)
|
||||
c.OVHClient = targetClient
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourcePublicCloudRegion() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourcePublicCloudRegionRead,
|
||||
Schema: map[string]*schema.Schema{
|
||||
"project_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OVH_PROJECT_ID", nil),
|
||||
},
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"services": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Set: publicCloudServiceHash,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"status": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"continentCode": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"datacenterLocation": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourcePublicCloudRegionRead(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
projectId := d.Get("project_id").(string)
|
||||
name := d.Get("name").(string)
|
||||
|
||||
log.Printf("[DEBUG] Will read public cloud region %s for project: %s", name, projectId)
|
||||
d.Partial(true)
|
||||
|
||||
response := &PublicCloudRegionResponse{}
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s", projectId, name)
|
||||
err := config.OVHClient.Get(endpoint, response)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error calling %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
|
||||
d.Set("datacenterLocation", response.DatacenterLocation)
|
||||
d.Set("continentCode", response.ContinentCode)
|
||||
|
||||
services := &schema.Set{
|
||||
F: publicCloudServiceHash,
|
||||
}
|
||||
for i := range response.Services {
|
||||
service := map[string]interface{}{
|
||||
"name": response.Services[i].Name,
|
||||
"status": response.Services[i].Status,
|
||||
}
|
||||
services.Add(service)
|
||||
}
|
||||
|
||||
d.Set("services", services)
|
||||
|
||||
d.Partial(false)
|
||||
d.SetId(fmt.Sprintf("%s_%s", projectId, name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func publicCloudServiceHash(v interface{}) int {
|
||||
r := v.(map[string]interface{})
|
||||
return hashcode.String(r["name"].(string))
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccPublicCloudRegionDataSource_basic(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccPublicCloudRegionDatasourceConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccPublicCloudRegionDatasource("data.ovh_publiccloud_region.region_attr.0"),
|
||||
testAccPublicCloudRegionDatasource("data.ovh_publiccloud_region.region_attr.1"),
|
||||
testAccPublicCloudRegionDatasource("data.ovh_publiccloud_region.region_attr.2"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccPublicCloudRegionDatasource(n string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("Can't find regions data source: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("Cannot find region attributes for project %s and region %s", rs.Primary.Attributes["project_id"], rs.Primary.Attributes["region"])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var testAccPublicCloudRegionDatasourceConfig = fmt.Sprintf(`
|
||||
data "ovh_publiccloud_regions" "regions" {
|
||||
project_id = "%s"
|
||||
}
|
||||
|
||||
data "ovh_publiccloud_region" "region_attr" {
|
||||
count = 3
|
||||
project_id = "${data.ovh_publiccloud_regions.regions.project_id}"
|
||||
name = "${element(data.ovh_publiccloud_regions.regions.names, count.index)}"
|
||||
}
|
||||
`, os.Getenv("OVH_PUBLIC_CLOUD"))
|
|
@ -0,0 +1,51 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourcePublicCloudRegions() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourcePublicCloudRegionsRead,
|
||||
Schema: map[string]*schema.Schema{
|
||||
"project_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OVH_PROJECT_ID", nil),
|
||||
},
|
||||
"names": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourcePublicCloudRegionsRead(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
projectId := d.Get("project_id").(string)
|
||||
|
||||
log.Printf("[DEBUG] Will read public cloud regions for project: %s", projectId)
|
||||
d.Partial(true)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/region", projectId)
|
||||
names := make([]string, 0)
|
||||
err := config.OVHClient.Get(endpoint, &names)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error calling %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
|
||||
d.Set("names", names)
|
||||
d.Partial(false)
|
||||
d.SetId(projectId)
|
||||
|
||||
log.Printf("[DEBUG] Read Public Cloud Regions %s", names)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccPublicCloudRegionsDataSource_basic(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccPublicCloudRegionsDatasourceConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccPublicCloudRegionsDatasource("data.ovh_publiccloud_regions.regions"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccPublicCloudRegionsDatasource(n string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("Can't find regions data source: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("Cannot find regions for project %s", rs.Primary.Attributes["project_id"])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var testAccPublicCloudRegionsDatasourceConfig = fmt.Sprintf(`
|
||||
data "ovh_publiccloud_regions" "regions" {
|
||||
project_id = "%s"
|
||||
}
|
||||
`, os.Getenv("OVH_PUBLIC_CLOUD"))
|
|
@ -0,0 +1,80 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// Provider returns a schema.Provider for OVH.
|
||||
func Provider() terraform.ResourceProvider {
|
||||
return &schema.Provider{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"endpoint": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OVH_ENDPOINT", nil),
|
||||
Description: descriptions["endpoint"],
|
||||
},
|
||||
"application_key": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OVH_APPLICATION_KEY", ""),
|
||||
Description: descriptions["application_key"],
|
||||
},
|
||||
"application_secret": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OVH_APPLICATION_SECRET", ""),
|
||||
Description: descriptions["application_secret"],
|
||||
},
|
||||
"consumer_key": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OVH_CONSUMER_KEY", ""),
|
||||
Description: descriptions["consumer_key"],
|
||||
},
|
||||
},
|
||||
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
"ovh_publiccloud_region": dataSourcePublicCloudRegion(),
|
||||
"ovh_publiccloud_regions": dataSourcePublicCloudRegions(),
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"ovh_publiccloud_private_network": resourcePublicCloudPrivateNetwork(),
|
||||
"ovh_publiccloud_private_network_subnet": resourcePublicCloudPrivateNetworkSubnet(),
|
||||
"ovh_publiccloud_user": resourcePublicCloudUser(),
|
||||
"ovh_vrack_publiccloud_attachment": resourceVRackPublicCloudAttachment(),
|
||||
},
|
||||
|
||||
ConfigureFunc: configureProvider,
|
||||
}
|
||||
}
|
||||
|
||||
var descriptions map[string]string
|
||||
|
||||
func init() {
|
||||
descriptions = map[string]string{
|
||||
"endpoint": "The OVH API endpoint to target (ex: \"ovh-eu\").",
|
||||
|
||||
"application_key": "The OVH API Application Key.",
|
||||
|
||||
"application_secret": "The OVH API Application Secret.",
|
||||
"consumer_key": "The OVH API Consumer key.",
|
||||
}
|
||||
}
|
||||
|
||||
func configureProvider(d *schema.ResourceData) (interface{}, error) {
|
||||
config := Config{
|
||||
Endpoint: d.Get("endpoint").(string),
|
||||
ApplicationKey: d.Get("application_key").(string),
|
||||
ApplicationSecret: d.Get("application_secret").(string),
|
||||
ConsumerKey: d.Get("consumer_key").(string),
|
||||
}
|
||||
|
||||
if err := config.loadAndValidate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
|
||||
"github.com/ovh/go-ovh/ovh"
|
||||
)
|
||||
|
||||
var testAccProviders map[string]terraform.ResourceProvider
|
||||
var testAccProvider *schema.Provider
|
||||
var testAccOVHClient *ovh.Client
|
||||
|
||||
func init() {
|
||||
log.SetOutput(os.Stdout)
|
||||
testAccProvider = Provider().(*schema.Provider)
|
||||
testAccProviders = map[string]terraform.ResourceProvider{
|
||||
"ovh": 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) {
|
||||
v := os.Getenv("OVH_ENDPOINT")
|
||||
if v == "" {
|
||||
t.Fatal("OVH_ENDPOINT must be set for acceptance tests")
|
||||
}
|
||||
|
||||
v = os.Getenv("OVH_APPLICATION_KEY")
|
||||
if v == "" {
|
||||
t.Fatal("OVH_APPLICATION_KEY must be set for acceptance tests")
|
||||
}
|
||||
|
||||
v = os.Getenv("OVH_APPLICATION_SECRET")
|
||||
if v == "" {
|
||||
t.Fatal("OVH_APPLICATION_SECRET must be set for acceptance tests")
|
||||
}
|
||||
|
||||
v = os.Getenv("OVH_CONSUMER_KEY")
|
||||
if v == "" {
|
||||
t.Fatal("OVH_CONSUMER_KEY must be set for acceptance tests")
|
||||
}
|
||||
|
||||
v = os.Getenv("OVH_VRACK")
|
||||
if v == "" {
|
||||
t.Fatal("OVH_VRACK must be set for acceptance tests")
|
||||
}
|
||||
|
||||
v = os.Getenv("OVH_PUBLIC_CLOUD")
|
||||
if v == "" {
|
||||
t.Fatal("OVH_PUBLIC_CLOUD must be set for acceptance tests")
|
||||
}
|
||||
|
||||
if testAccOVHClient == nil {
|
||||
config := Config{
|
||||
Endpoint: os.Getenv("OVH_ENDPOINT"),
|
||||
ApplicationKey: os.Getenv("OVH_APPLICATION_KEY"),
|
||||
ApplicationSecret: os.Getenv("OVH_APPLICATION_SECRET"),
|
||||
ConsumerKey: os.Getenv("OVH_CONSUMER_KEY"),
|
||||
}
|
||||
|
||||
if err := config.loadAndValidate(); err != nil {
|
||||
t.Fatalf("couln't load OVH Client: %s", err)
|
||||
} else {
|
||||
testAccOVHClient = config.OVHClient
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckVRackExists(t *testing.T) {
|
||||
type vrackResponse struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
r := vrackResponse{}
|
||||
|
||||
endpoint := fmt.Sprintf("/vrack/%s", os.Getenv("OVH_VRACK"))
|
||||
|
||||
err := testAccOVHClient.Get(endpoint, &r)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %q\n", err)
|
||||
}
|
||||
t.Logf("Read VRack %s -> name:'%s', desc:'%s' ", endpoint, r.Name, r.Description)
|
||||
|
||||
}
|
||||
|
||||
func testAccCheckPublicCloudExists(t *testing.T) {
|
||||
type cloudProjectResponse struct {
|
||||
ID string `json:"project_id"`
|
||||
Status string `json:"status"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
r := cloudProjectResponse{}
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s", os.Getenv("OVH_PUBLIC_CLOUD"))
|
||||
|
||||
err := testAccOVHClient.Get(endpoint, &r)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %q\n", err)
|
||||
}
|
||||
t.Logf("Read Cloud Project %s -> status: '%s', desc: '%s'", endpoint, r.Status, r.Description)
|
||||
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
||||
"github.com/ovh/go-ovh/ovh"
|
||||
)
|
||||
|
||||
func resourcePublicCloudPrivateNetwork() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourcePublicCloudPrivateNetworkCreate,
|
||||
Read: resourcePublicCloudPrivateNetworkRead,
|
||||
Update: resourcePublicCloudPrivateNetworkUpdate,
|
||||
Delete: resourcePublicCloudPrivateNetworkDelete,
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
|
||||
return []*schema.ResourceData{d}, nil
|
||||
},
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"project_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OVH_PROJECT_ID", nil),
|
||||
},
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: false,
|
||||
},
|
||||
"vlan_id": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: 0,
|
||||
},
|
||||
"regions": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
|
||||
"regions_status": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"status": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"region": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"status": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourcePublicCloudPrivateNetworkCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
projectId := d.Get("project_id").(string)
|
||||
params := &PublicCloudPrivateNetworkCreateOpts{
|
||||
ProjectId: d.Get("project_id").(string),
|
||||
VlanId: d.Get("vlan_id").(int),
|
||||
Name: d.Get("name").(string),
|
||||
Regions: regionsOptsFromSchema(d),
|
||||
}
|
||||
|
||||
r := &PublicCloudPrivateNetworkResponse{}
|
||||
|
||||
log.Printf("[DEBUG] Will create public cloud private network: %s", params)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/network/private", params.ProjectId)
|
||||
|
||||
err := config.OVHClient.Post(endpoint, params, r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling %s with params %s:\n\t %q", endpoint, params, err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Waiting for Private Network %s:", r)
|
||||
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"BUILDING"},
|
||||
Target: []string{"ACTIVE"},
|
||||
Refresh: waitForPublicCloudPrivateNetworkActive(config.OVHClient, projectId, r.Id),
|
||||
Timeout: 10 * time.Minute,
|
||||
Delay: 10 * time.Second,
|
||||
MinTimeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
_, err = stateConf.WaitForState()
|
||||
if err != nil {
|
||||
return fmt.Errorf("waiting for private network (%s): %s", params, err)
|
||||
}
|
||||
log.Printf("[DEBUG] Created Private Network %s", r)
|
||||
|
||||
//set id
|
||||
d.SetId(r.Id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePublicCloudPrivateNetworkRead(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
projectId := d.Get("project_id").(string)
|
||||
|
||||
r := &PublicCloudPrivateNetworkResponse{}
|
||||
|
||||
log.Printf("[DEBUG] Will read public cloud private network for project: %s, id: %s", projectId, d.Id())
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s", projectId, d.Id())
|
||||
|
||||
d.Partial(true)
|
||||
err := config.OVHClient.Get(endpoint, r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error calling %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
|
||||
err = readPublicCloudPrivateNetwork(config, d, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Partial(false)
|
||||
|
||||
log.Printf("[DEBUG] Read Public Cloud Private Network %s", r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePublicCloudPrivateNetworkUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
projectId := d.Get("project_id").(string)
|
||||
params := &PublicCloudPrivateNetworkUpdateOpts{
|
||||
Name: d.Get("name").(string),
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Will update public cloud private network: %s", params)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s", projectId, d.Id())
|
||||
|
||||
err := config.OVHClient.Put(endpoint, params, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling %s with params %s:\n\t %q", endpoint, params, err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Updated Public cloud %s Private Network %s:", projectId, d.Id())
|
||||
|
||||
return resourcePublicCloudPrivateNetworkRead(d, meta)
|
||||
}
|
||||
|
||||
func resourcePublicCloudPrivateNetworkDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
projectId := d.Get("project_id").(string)
|
||||
id := d.Id()
|
||||
|
||||
log.Printf("[DEBUG] Will delete public cloud private network for project: %s, id: %s", projectId, id)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s", projectId, id)
|
||||
|
||||
err := config.OVHClient.Delete(endpoint, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"DELETING"},
|
||||
Target: []string{"DELETED"},
|
||||
Refresh: waitForPublicCloudPrivateNetworkDelete(config.OVHClient, projectId, id),
|
||||
Timeout: 10 * time.Minute,
|
||||
Delay: 10 * time.Second,
|
||||
MinTimeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
_, err = stateConf.WaitForState()
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting for private network (%s): %s", id, err)
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
|
||||
log.Printf("[DEBUG] Deleted Public Cloud %s Private Network %s", projectId, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func regionsOptsFromSchema(d *schema.ResourceData) []string {
|
||||
var regions []string
|
||||
if v := d.Get("regions"); v != nil {
|
||||
rs := v.(*schema.Set).List()
|
||||
if len(rs) > 0 {
|
||||
for _, v := range v.(*schema.Set).List() {
|
||||
regions = append(regions, v.(string))
|
||||
}
|
||||
}
|
||||
}
|
||||
return regions
|
||||
}
|
||||
|
||||
func readPublicCloudPrivateNetwork(config *Config, d *schema.ResourceData, r *PublicCloudPrivateNetworkResponse) error {
|
||||
d.Set("name", r.Name)
|
||||
d.Set("status", r.Status)
|
||||
d.Set("type", r.Type)
|
||||
d.Set("vlan_id", r.Vlanid)
|
||||
|
||||
regions_status := make([]map[string]interface{}, 0)
|
||||
regions := make([]string, 0)
|
||||
|
||||
for i := range r.Regions {
|
||||
region := make(map[string]interface{})
|
||||
region["region"] = r.Regions[i].Region
|
||||
region["status"] = r.Regions[i].Status
|
||||
regions_status = append(regions_status, region)
|
||||
regions = append(regions, fmt.Sprintf(r.Regions[i].Region))
|
||||
}
|
||||
d.Set("regions_status", regions_status)
|
||||
d.Set("regions", regions)
|
||||
|
||||
d.SetId(r.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func publicCloudPrivateNetworkExists(projectId, id string, c *ovh.Client) error {
|
||||
r := &PublicCloudPrivateNetworkResponse{}
|
||||
|
||||
log.Printf("[DEBUG] Will read public cloud private network for project: %s, id: %s", projectId, id)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s", projectId, id)
|
||||
|
||||
err := c.Get(endpoint, r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
log.Printf("[DEBUG] Read public cloud private network: %s", r)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AttachmentStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
|
||||
// an Attachment Task.
|
||||
func waitForPublicCloudPrivateNetworkActive(c *ovh.Client, projectId, PublicCloudPrivateNetworkId string) resource.StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
r := &PublicCloudPrivateNetworkResponse{}
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s", projectId, PublicCloudPrivateNetworkId)
|
||||
err := c.Get(endpoint, r)
|
||||
if err != nil {
|
||||
return r, "", err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Pending Private Network: %s", r)
|
||||
return r, r.Status, nil
|
||||
}
|
||||
}
|
||||
|
||||
// AttachmentStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
|
||||
// an Attachment Task.
|
||||
func waitForPublicCloudPrivateNetworkDelete(c *ovh.Client, projectId, PublicCloudPrivateNetworkId string) resource.StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
r := &PublicCloudPrivateNetworkResponse{}
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s", projectId, PublicCloudPrivateNetworkId)
|
||||
err := c.Get(endpoint, r)
|
||||
if err != nil {
|
||||
if err.(*ovh.APIError).Code == 404 {
|
||||
log.Printf("[DEBUG] private network id %s on project %s deleted", PublicCloudPrivateNetworkId, projectId)
|
||||
return r, "DELETED", nil
|
||||
} else {
|
||||
return r, "", err
|
||||
}
|
||||
}
|
||||
log.Printf("[DEBUG] Pending Private Network: %s", r)
|
||||
return r, r.Status, nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
||||
"net"
|
||||
|
||||
"github.com/ovh/go-ovh/ovh"
|
||||
)
|
||||
|
||||
func resourcePublicCloudPrivateNetworkSubnet() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourcePublicCloudPrivateNetworkSubnetCreate,
|
||||
Read: resourcePublicCloudPrivateNetworkSubnetRead,
|
||||
Delete: resourcePublicCloudPrivateNetworkSubnetDelete,
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
|
||||
return []*schema.ResourceData{d}, nil
|
||||
},
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"project_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OVH_PROJECT_ID", ""),
|
||||
},
|
||||
"network_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"dhcp": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: false,
|
||||
},
|
||||
"start": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: resourcePubliccloudPrivateNetworkSubnetValidateIP,
|
||||
},
|
||||
"end": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: resourcePubliccloudPrivateNetworkSubnetValidateIP,
|
||||
},
|
||||
"network": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: resourcePubliccloudPrivateNetworkSubnetValidateNetwork,
|
||||
},
|
||||
"region": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"no_gateway": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: false,
|
||||
},
|
||||
"gateway_ip": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"cidr": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"ip_pools": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"network": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"region": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"dhcp": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Computed: true,
|
||||
},
|
||||
"end": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"start": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourcePublicCloudPrivateNetworkSubnetCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
projectId := d.Get("project_id").(string)
|
||||
networkId := d.Get("network_id").(string)
|
||||
|
||||
params := &PublicCloudPrivateNetworksCreateOpts{
|
||||
ProjectId: projectId,
|
||||
NetworkId: networkId,
|
||||
Dhcp: d.Get("dhcp").(bool),
|
||||
NoGateway: d.Get("no_gateway").(bool),
|
||||
Start: d.Get("start").(string),
|
||||
End: d.Get("end").(string),
|
||||
Network: d.Get("network").(string),
|
||||
Region: d.Get("region").(string),
|
||||
}
|
||||
|
||||
r := &PublicCloudPrivateNetworksResponse{}
|
||||
|
||||
log.Printf("[DEBUG] Will create public cloud private network subnet: %s", params)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s/subnet", projectId, networkId)
|
||||
|
||||
err := config.OVHClient.Post(endpoint, params, r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling %s with params %s:\n\t %q", endpoint, params, err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Created Private Network Subnet %s", r)
|
||||
|
||||
//set id
|
||||
d.SetId(r.Id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePublicCloudPrivateNetworkSubnetRead(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
projectId := d.Get("project_id").(string)
|
||||
networkId := d.Get("network_id").(string)
|
||||
|
||||
r := []*PublicCloudPrivateNetworksResponse{}
|
||||
|
||||
log.Printf("[DEBUG] Will read public cloud private network subnet for project: %s, network: %s, id: %s", projectId, networkId, d.Id())
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s/subnet", projectId, networkId)
|
||||
|
||||
err := config.OVHClient.Get(endpoint, &r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
|
||||
err = readPublicCloudPrivateNetworkSubnet(d, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Read Public Cloud Private Network %v", r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePublicCloudPrivateNetworkSubnetDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
projectId := d.Get("project_id").(string)
|
||||
networkId := d.Get("network_id").(string)
|
||||
id := d.Id()
|
||||
|
||||
log.Printf("[DEBUG] Will delete public cloud private network subnet for project: %s, network: %s, id: %s", projectId, networkId, id)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s/subnet/%s", projectId, id, id)
|
||||
|
||||
err := config.OVHClient.Delete(endpoint, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
|
||||
log.Printf("[DEBUG] Deleted Public Cloud %s Private Network %s Subnet %s", projectId, networkId, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func publicCloudPrivateNetworkSubnetExists(projectId, networkId, id string, c *ovh.Client) error {
|
||||
r := []*PublicCloudPrivateNetworksResponse{}
|
||||
|
||||
log.Printf("[DEBUG] Will read public cloud private network subnet for project: %s, network: %s, id: %s", projectId, networkId, id)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s/subnet", projectId, networkId)
|
||||
|
||||
err := c.Get(endpoint, &r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
|
||||
s := findPublicCloudPrivateNetworkSubnet(r, id)
|
||||
if s == nil {
|
||||
return fmt.Errorf("Subnet %s doesn't exists for project %s and network %s", id, projectId, networkId)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findPublicCloudPrivateNetworkSubnet(rs []*PublicCloudPrivateNetworksResponse, id string) *PublicCloudPrivateNetworksResponse {
|
||||
for i := range rs {
|
||||
if rs[i].Id == id {
|
||||
return rs[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readPublicCloudPrivateNetworkSubnet(d *schema.ResourceData, rs []*PublicCloudPrivateNetworksResponse) error {
|
||||
r := findPublicCloudPrivateNetworkSubnet(rs, d.Id())
|
||||
if r == nil {
|
||||
return fmt.Errorf("%s subnet not found", d.Id())
|
||||
}
|
||||
|
||||
d.Set("gateway_ip", r.GatewayIp)
|
||||
d.Set("cidr", r.Cidr)
|
||||
|
||||
ippools := make([]map[string]interface{}, 0)
|
||||
for i := range r.IPPools {
|
||||
ippool := make(map[string]interface{})
|
||||
ippool["network"] = r.IPPools[i].Network
|
||||
ippool["region"] = r.IPPools[i].Region
|
||||
ippool["dhcp"] = r.IPPools[i].Dhcp
|
||||
ippool["start"] = r.IPPools[i].Start
|
||||
ippool["end"] = r.IPPools[i].End
|
||||
ippools = append(ippools, ippool)
|
||||
}
|
||||
|
||||
d.Set("network", ippools[0]["network"])
|
||||
d.Set("region", ippools[0]["region"])
|
||||
d.Set("dhcp", ippools[0]["dhcp"])
|
||||
d.Set("start", ippools[0]["start"])
|
||||
d.Set("end", ippools[0]["end"])
|
||||
d.Set("ip_pools", ippools)
|
||||
|
||||
if r.GatewayIp == "" {
|
||||
d.Set("no_gateway", true)
|
||||
} else {
|
||||
d.Set("no_gateway", false)
|
||||
}
|
||||
|
||||
d.SetId(r.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePubliccloudPrivateNetworkSubnetValidateIP(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(string)
|
||||
ip := net.ParseIP(value)
|
||||
if ip == nil {
|
||||
errors = append(errors, fmt.Errorf("%q must be a valid IP", k))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func resourcePubliccloudPrivateNetworkSubnetValidateNetwork(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(string)
|
||||
_, _, err := net.ParseCIDR(value)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("%q is not a valid network value: %#v", k, err))
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var testAccPublicCloudPrivateNetworkSubnetConfig = fmt.Sprintf(`
|
||||
resource "ovh_vrack_publiccloud_attachment" "attach" {
|
||||
vrack_id = "%s"
|
||||
project_id = "%s"
|
||||
}
|
||||
|
||||
data "ovh_publiccloud_regions" "regions" {
|
||||
project_id = "${ovh_vrack_publiccloud_attachment.attach.project_id}"
|
||||
}
|
||||
|
||||
data "ovh_publiccloud_region" "region_attr" {
|
||||
count = 2
|
||||
project_id = "${data.ovh_publiccloud_regions.regions.project_id}"
|
||||
name = "${element(data.ovh_publiccloud_regions.regions.names, count.index)}"
|
||||
}
|
||||
|
||||
resource "ovh_publiccloud_private_network" "network" {
|
||||
project_id = "${ovh_vrack_publiccloud_attachment.attach.project_id}"
|
||||
vlan_id = 0
|
||||
name = "terraform_testacc_private_net"
|
||||
regions = ["${data.ovh_publiccloud_regions.regions.names}"]
|
||||
}
|
||||
|
||||
resource "ovh_publiccloud_private_network_subnet" "subnet" {
|
||||
project_id = "${ovh_publiccloud_private_network.network.project_id}"
|
||||
network_id = "${ovh_publiccloud_private_network.network.id}"
|
||||
region = "${element(data.ovh_publiccloud_regions.regions.names, 0)}"
|
||||
start = "192.168.168.100"
|
||||
end = "192.168.168.200"
|
||||
network = "192.168.168.0/24"
|
||||
dhcp = true
|
||||
no_gateway = false
|
||||
}
|
||||
`, os.Getenv("OVH_VRACK"), os.Getenv("OVH_PUBLIC_CLOUD"))
|
||||
|
||||
func TestAccPublicCloudPrivateNetworkSubnet_basic(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccCheckPublicCloudPrivateNetworkSubnetPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckPublicCloudPrivateNetworkSubnetDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccPublicCloudPrivateNetworkSubnetConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckVRackPublicCloudAttachmentExists("ovh_vrack_publiccloud_attachment.attach", t),
|
||||
testAccCheckPublicCloudPrivateNetworkExists("ovh_publiccloud_private_network.network", t),
|
||||
testAccCheckPublicCloudPrivateNetworkSubnetExists("ovh_publiccloud_private_network_subnet.subnet", t),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckPublicCloudPrivateNetworkSubnetPreCheck(t *testing.T) {
|
||||
testAccPreCheck(t)
|
||||
testAccCheckPublicCloudExists(t)
|
||||
}
|
||||
|
||||
func testAccCheckPublicCloudPrivateNetworkSubnetExists(n string, t *testing.T) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["project_id"] == "" {
|
||||
return fmt.Errorf("No Project ID is set")
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["network_id"] == "" {
|
||||
return fmt.Errorf("No Network ID is set")
|
||||
}
|
||||
|
||||
return publicCloudPrivateNetworkSubnetExists(
|
||||
rs.Primary.Attributes["project_id"],
|
||||
rs.Primary.Attributes["network_id"],
|
||||
rs.Primary.ID,
|
||||
config.OVHClient,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckPublicCloudPrivateNetworkSubnetDestroy(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "ovh_publiccloud_private_network_subnet" {
|
||||
continue
|
||||
}
|
||||
|
||||
err := publicCloudPrivateNetworkSubnetExists(
|
||||
rs.Primary.Attributes["project_id"],
|
||||
rs.Primary.Attributes["network_id"],
|
||||
rs.Primary.ID,
|
||||
config.OVHClient,
|
||||
)
|
||||
|
||||
if err == nil {
|
||||
return fmt.Errorf("VRack > Public Cloud Private Network Subnet still exists")
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var testAccPublicCloudPrivateNetworkConfig = fmt.Sprintf(`
|
||||
resource "ovh_vrack_publiccloud_attachment" "attach" {
|
||||
vrack_id = "%s"
|
||||
project_id = "%s"
|
||||
}
|
||||
|
||||
data "ovh_publiccloud_regions" "regions" {
|
||||
project_id = "${ovh_vrack_publiccloud_attachment.attach.project_id}"
|
||||
}
|
||||
|
||||
data "ovh_publiccloud_region" "region_attr" {
|
||||
count = 2
|
||||
project_id = "${data.ovh_publiccloud_regions.regions.project_id}"
|
||||
name = "${element(data.ovh_publiccloud_regions.regions.names, count.index)}"
|
||||
}
|
||||
|
||||
resource "ovh_publiccloud_private_network" "network" {
|
||||
project_id = "${ovh_vrack_publiccloud_attachment.attach.project_id}"
|
||||
vlan_id = 0
|
||||
name = "terraform_testacc_private_net"
|
||||
regions = ["${data.ovh_publiccloud_regions.regions.names}"]
|
||||
}
|
||||
`, os.Getenv("OVH_VRACK"), os.Getenv("OVH_PUBLIC_CLOUD"))
|
||||
|
||||
func TestAccPublicCloudPrivateNetwork_basic(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccCheckPublicCloudPrivateNetworkPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckPublicCloudPrivateNetworkDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccPublicCloudPrivateNetworkConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckVRackPublicCloudAttachmentExists("ovh_vrack_publiccloud_attachment.attach", t),
|
||||
testAccCheckPublicCloudPrivateNetworkExists("ovh_publiccloud_private_network.network", t),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckPublicCloudPrivateNetworkPreCheck(t *testing.T) {
|
||||
testAccPreCheck(t)
|
||||
testAccCheckPublicCloudExists(t)
|
||||
}
|
||||
|
||||
func testAccCheckPublicCloudPrivateNetworkExists(n string, t *testing.T) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["project_id"] == "" {
|
||||
return fmt.Errorf("No Project ID is set")
|
||||
}
|
||||
|
||||
return publicCloudPrivateNetworkExists(rs.Primary.Attributes["project_id"], rs.Primary.ID, config.OVHClient)
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckPublicCloudPrivateNetworkDestroy(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "ovh_publiccloud_private_network" {
|
||||
continue
|
||||
}
|
||||
|
||||
err := publicCloudPrivateNetworkExists(rs.Primary.Attributes["project_id"], rs.Primary.ID, config.OVHClient)
|
||||
if err == nil {
|
||||
return fmt.Errorf("VRack > Public Cloud Private Network still exists")
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
||||
"github.com/ovh/go-ovh/ovh"
|
||||
)
|
||||
|
||||
func resourcePublicCloudUser() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourcePublicCloudUserCreate,
|
||||
Read: resourcePublicCloudUserRead,
|
||||
Delete: resourcePublicCloudUserDelete,
|
||||
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
|
||||
return []*schema.ResourceData{d}, nil
|
||||
},
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"project_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OVH_PROJECT_ID", nil),
|
||||
},
|
||||
"description": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"username": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"password": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Sensitive: true,
|
||||
},
|
||||
"status": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"creation_date": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"openstack_rc": &schema.Schema{
|
||||
Type: schema.TypeMap,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourcePublicCloudUserCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
projectId := d.Get("project_id").(string)
|
||||
params := &PublicCloudUserCreateOpts{
|
||||
ProjectId: projectId,
|
||||
Description: d.Get("description").(string),
|
||||
}
|
||||
|
||||
r := &PublicCloudUserResponse{}
|
||||
|
||||
log.Printf("[DEBUG] Will create public cloud user: %s", params)
|
||||
|
||||
// Resource is partial because we will also compute Openstack RC & creds
|
||||
d.Partial(true)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/user", params.ProjectId)
|
||||
|
||||
err := config.OVHClient.Post(endpoint, params, r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling Post %s with params %s:\n\t %q", endpoint, params, err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Waiting for User %s:", r)
|
||||
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"creating"},
|
||||
Target: []string{"ok"},
|
||||
Refresh: waitForPublicCloudUserActive(config.OVHClient, projectId, strconv.Itoa(r.Id)),
|
||||
Timeout: 10 * time.Minute,
|
||||
Delay: 10 * time.Second,
|
||||
MinTimeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
_, err = stateConf.WaitForState()
|
||||
if err != nil {
|
||||
return fmt.Errorf("waiting for user (%s): %s", params, err)
|
||||
}
|
||||
log.Printf("[DEBUG] Created User %s", r)
|
||||
|
||||
readPublicCloudUser(d, r, true)
|
||||
|
||||
openstackrc := make(map[string]string)
|
||||
err = publicCloudUserGetOpenstackRC(projectId, d.Id(), config.OVHClient, openstackrc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Creating openstack creds for user %s: %s", d.Id(), err)
|
||||
}
|
||||
|
||||
d.Set("openstack_rc", &openstackrc)
|
||||
|
||||
d.Partial(false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePublicCloudUserRead(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
projectId := d.Get("project_id").(string)
|
||||
|
||||
d.Partial(true)
|
||||
r := &PublicCloudUserResponse{}
|
||||
|
||||
log.Printf("[DEBUG] Will read public cloud user %s from project: %s", d.Id(), projectId)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s", projectId, d.Id())
|
||||
|
||||
err := config.OVHClient.Get(endpoint, r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling Get %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
|
||||
readPublicCloudUser(d, r, false)
|
||||
|
||||
openstackrc := make(map[string]string)
|
||||
err = publicCloudUserGetOpenstackRC(projectId, d.Id(), config.OVHClient, openstackrc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Reading openstack creds for user %s: %s", d.Id(), err)
|
||||
}
|
||||
|
||||
d.Set("openstack_rc", &openstackrc)
|
||||
d.Partial(false)
|
||||
log.Printf("[DEBUG] Read Public Cloud User %s", r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourcePublicCloudUserDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
projectId := d.Get("project_id").(string)
|
||||
id := d.Id()
|
||||
|
||||
log.Printf("[DEBUG] Will delete public cloud user %s from project: %s", id, projectId)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s", projectId, id)
|
||||
|
||||
err := config.OVHClient.Delete(endpoint, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling Delete %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Deleting Public Cloud User %s from project %s:", id, projectId)
|
||||
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"deleting"},
|
||||
Target: []string{"deleted"},
|
||||
Refresh: waitForPublicCloudUserDelete(config.OVHClient, projectId, id),
|
||||
Timeout: 10 * time.Minute,
|
||||
Delay: 10 * time.Second,
|
||||
MinTimeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
_, err = stateConf.WaitForState()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Deleting Public Cloud user %s from project %s", id, projectId)
|
||||
}
|
||||
log.Printf("[DEBUG] Deleted Public Cloud User %s from project %s", id, projectId)
|
||||
|
||||
d.SetId("")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func publicCloudUserExists(projectId, id string, c *ovh.Client) error {
|
||||
r := &PublicCloudUserResponse{}
|
||||
|
||||
log.Printf("[DEBUG] Will read public cloud user for project: %s, id: %s", projectId, id)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s", projectId, id)
|
||||
|
||||
err := c.Get(endpoint, r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling Get %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
log.Printf("[DEBUG] Read public cloud user: %s", r)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var publicCloudUserOSTenantName = regexp.MustCompile("export OS_TENANT_NAME=\"?([[:alnum:]]+)\"?")
|
||||
var publicCloudUserOSTenantId = regexp.MustCompile("export OS_TENANT_ID=\"??([[:alnum:]]+)\"??")
|
||||
var publicCloudUserOSAuthURL = regexp.MustCompile("export OS_AUTH_URL=\"??([[:^space:]]+)\"??")
|
||||
var publicCloudUserOSUsername = regexp.MustCompile("export OS_USERNAME=\"?([[:alnum:]]+)\"?")
|
||||
|
||||
func publicCloudUserGetOpenstackRC(projectId, id string, c *ovh.Client, rc map[string]string) error {
|
||||
log.Printf("[DEBUG] Will read public cloud user openstack rc for project: %s, id: %s", projectId, id)
|
||||
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s/openrc?region=to_be_overriden", projectId, id)
|
||||
|
||||
r := &PublicCloudUserOpenstackRC{}
|
||||
|
||||
err := c.Get(endpoint, r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling Get %s:\n\t %q", endpoint, err)
|
||||
}
|
||||
|
||||
authURL := publicCloudUserOSAuthURL.FindStringSubmatch(r.Content)
|
||||
if authURL == nil {
|
||||
return fmt.Errorf("couln't extract OS_AUTH_URL from content: \n\t%s", r.Content)
|
||||
}
|
||||
tenantName := publicCloudUserOSTenantName.FindStringSubmatch(r.Content)
|
||||
if tenantName == nil {
|
||||
return fmt.Errorf("couln't extract OS_TENANT_NAME from content: \n\t%s", r.Content)
|
||||
}
|
||||
tenantId := publicCloudUserOSTenantId.FindStringSubmatch(r.Content)
|
||||
if tenantId == nil {
|
||||
return fmt.Errorf("couln't extract OS_TENANT_ID from content: \n\t%s", r.Content)
|
||||
}
|
||||
username := publicCloudUserOSUsername.FindStringSubmatch(r.Content)
|
||||
if username == nil {
|
||||
return fmt.Errorf("couln't extract OS_USERNAME from content: \n\t%s", r.Content)
|
||||
}
|
||||
|
||||
rc["OS_AUTH_URL"] = authURL[1]
|
||||
rc["OS_TENANT_ID"] = tenantId[1]
|
||||
rc["OS_TENANT_NAME"] = tenantName[1]
|
||||
rc["OS_USERNAME"] = username[1]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readPublicCloudUser(d *schema.ResourceData, r *PublicCloudUserResponse, setPassword bool) {
|
||||
d.Set("description", r.Description)
|
||||
d.Set("status", r.Status)
|
||||
d.Set("creation_date", r.CreationDate)
|
||||
d.Set("username", r.Username)
|
||||
if setPassword {
|
||||
d.Set("password", r.Password)
|
||||
}
|
||||
d.SetId(strconv.Itoa(r.Id))
|
||||
}
|
||||
|
||||
func waitForPublicCloudUserActive(c *ovh.Client, projectId, PublicCloudUserId string) resource.StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
r := &PublicCloudUserResponse{}
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s", projectId, PublicCloudUserId)
|
||||
err := c.Get(endpoint, r)
|
||||
if err != nil {
|
||||
return r, "", err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Pending User: %s", r)
|
||||
return r, r.Status, nil
|
||||
}
|
||||
}
|
||||
|
||||
func waitForPublicCloudUserDelete(c *ovh.Client, projectId, PublicCloudUserId string) resource.StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
r := &PublicCloudUserResponse{}
|
||||
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s", projectId, PublicCloudUserId)
|
||||
err := c.Get(endpoint, r)
|
||||
if err != nil {
|
||||
if err.(*ovh.APIError).Code == 404 {
|
||||
log.Printf("[DEBUG] user id %s on project %s deleted", PublicCloudUserId, projectId)
|
||||
return r, "deleted", nil
|
||||
} else {
|
||||
return r, "", err
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Pending User: %s", r)
|
||||
return r, r.Status, nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var testAccPublicCloudUserConfig = fmt.Sprintf(`
|
||||
resource "ovh_publiccloud_user" "user" {
|
||||
project_id = "%s"
|
||||
description = "my user for acceptance tests"
|
||||
}
|
||||
`, os.Getenv("OVH_PUBLIC_CLOUD"))
|
||||
|
||||
func TestAccPublicCloudUser_basic(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccCheckPublicCloudUserPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckPublicCloudUserDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccPublicCloudUserConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckPublicCloudUserExists("ovh_publiccloud_user.user", t),
|
||||
testAccCheckPublicCloudUserOpenRC("ovh_publiccloud_user.user", t),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckPublicCloudUserPreCheck(t *testing.T) {
|
||||
testAccPreCheck(t)
|
||||
testAccCheckPublicCloudExists(t)
|
||||
}
|
||||
|
||||
func testAccCheckPublicCloudUserExists(n string, t *testing.T) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["project_id"] == "" {
|
||||
return fmt.Errorf("No Project ID is set")
|
||||
}
|
||||
|
||||
return publicCloudUserExists(rs.Primary.Attributes["project_id"], rs.Primary.ID, config.OVHClient)
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckPublicCloudUserOpenRC(n string, t *testing.T) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["openstack_rc.OS_AUTH_URL"] == "" {
|
||||
return fmt.Errorf("No openstack_rc.OS_AUTH_URL is set")
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["openstack_rc.OS_TENANT_ID"] == "" {
|
||||
return fmt.Errorf("No openstack_rc.OS_TENANT_ID is set")
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["openstack_rc.OS_TENANT_NAME"] == "" {
|
||||
return fmt.Errorf("No openstack_rc.OS_TENANT_NAME is set")
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["openstack_rc.OS_USERNAME"] == "" {
|
||||
return fmt.Errorf("No openstack_rc.OS_USERNAME is set")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckPublicCloudUserDestroy(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "ovh_publiccloud_user" {
|
||||
continue
|
||||
}
|
||||
|
||||
err := publicCloudUserExists(rs.Primary.Attributes["project_id"], rs.Primary.ID, config.OVHClient)
|
||||
if err == nil {
|
||||
return fmt.Errorf("VRack > Public Cloud User still exists")
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/ovh/go-ovh/ovh"
|
||||
)
|
||||
|
||||
func resourceVRackPublicCloudAttachment() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceVRackPublicCloudAttachmentCreate,
|
||||
Read: resourceVRackPublicCloudAttachmentRead,
|
||||
Delete: resourceVRackPublicCloudAttachmentDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"vrack_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OVH_VRACK_ID", ""),
|
||||
},
|
||||
"project_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OVH_PROJECT_ID", ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceVRackPublicCloudAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
vrackId := d.Get("vrack_id").(string)
|
||||
projectId := d.Get("project_id").(string)
|
||||
|
||||
if err := vrackPublicCloudAttachmentExists(vrackId, projectId, config.OVHClient); err == nil {
|
||||
//set id
|
||||
d.SetId(fmt.Sprintf("vrack_%s-cloudproject_%s-attach", vrackId, projectId))
|
||||
return nil
|
||||
}
|
||||
|
||||
params := &VRackAttachOpts{Project: projectId}
|
||||
r := VRackAttachTaskResponse{}
|
||||
|
||||
log.Printf("[DEBUG] Will Attach VRack %s -> PublicCloud %s", vrackId, params.Project)
|
||||
endpoint := fmt.Sprintf("/vrack/%s/cloudProject", vrackId)
|
||||
|
||||
err := config.OVHClient.Post(endpoint, params, &r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error calling %s with params %s:\n\t %q", endpoint, params, err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Waiting for Attachement Task id %d: VRack %s -> PublicCloud %s", r.Id, vrackId, params.Project)
|
||||
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"init", "todo", "doing"},
|
||||
Target: []string{"completed"},
|
||||
Refresh: waitForVRackTaskCompleted(config.OVHClient, vrackId, r.Id),
|
||||
Timeout: 10 * time.Minute,
|
||||
Delay: 10 * time.Second,
|
||||
MinTimeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
_, err = stateConf.WaitForState()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error waiting for vrack (%s) to attach to public cloud (%s): %s", vrackId, params.Project, err)
|
||||
}
|
||||
log.Printf("[DEBUG] Created Attachement Task id %d: VRack %s -> PublicCloud %s", r.Id, vrackId, params.Project)
|
||||
|
||||
//set id
|
||||
d.SetId(fmt.Sprintf("vrack_%s-cloudproject_%s-attach", vrackId, params.Project))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceVRackPublicCloudAttachmentRead(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
vrackId := d.Get("vrack_id").(string)
|
||||
params := &VRackAttachOpts{Project: d.Get("project_id").(string)}
|
||||
r := VRackAttachTaskResponse{}
|
||||
endpoint := fmt.Sprintf("/vrack/%s/cloudProject/%s", vrackId, params.Project)
|
||||
|
||||
err := config.OVHClient.Get(endpoint, &r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("[DEBUG] Read VRack %s -> PublicCloud %s", vrackId, params.Project)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceVRackPublicCloudAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
vrackId := d.Get("vrack_id").(string)
|
||||
params := &VRackAttachOpts{Project: d.Get("project_id").(string)}
|
||||
|
||||
r := VRackAttachTaskResponse{}
|
||||
endpoint := fmt.Sprintf("/vrack/%s/cloudProject/%s", vrackId, params.Project)
|
||||
|
||||
err := config.OVHClient.Delete(endpoint, &r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Waiting for Attachment Deletion Task id %d: VRack %s -> PublicCloud %s", r.Id, vrackId, params.Project)
|
||||
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"init", "todo", "doing"},
|
||||
Target: []string{"completed"},
|
||||
Refresh: waitForVRackTaskCompleted(config.OVHClient, vrackId, r.Id),
|
||||
Timeout: 10 * time.Minute,
|
||||
Delay: 10 * time.Second,
|
||||
MinTimeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
_, err = stateConf.WaitForState()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error waiting for vrack (%s) to attach to public cloud (%s): %s", vrackId, params.Project, err)
|
||||
}
|
||||
log.Printf("[DEBUG] Removed Attachement id %d: VRack %s -> PublicCloud %s", r.Id, vrackId, params.Project)
|
||||
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
func vrackPublicCloudAttachmentExists(vrackId, projectId string, c *ovh.Client) error {
|
||||
type attachResponse struct {
|
||||
VRack string `json:"vrack"`
|
||||
Project string `json:"project"`
|
||||
}
|
||||
|
||||
r := attachResponse{}
|
||||
|
||||
endpoint := fmt.Sprintf("/vrack/%s/cloudProject/%s", vrackId, projectId)
|
||||
|
||||
err := c.Get(endpoint, &r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error while querying %s: %q\n", endpoint, err)
|
||||
}
|
||||
log.Printf("[DEBUG] Read Attachment %s -> VRack:%s, Cloud Project: %s", endpoint, r.VRack, r.Project)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AttachmentStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
|
||||
// an Attachment Task.
|
||||
func waitForVRackTaskCompleted(c *ovh.Client, serviceName string, taskId int) resource.StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
r := VRackAttachTaskResponse{}
|
||||
endpoint := fmt.Sprintf("/vrack/%s/task/%d", serviceName, taskId)
|
||||
err := c.Get(endpoint, &r)
|
||||
if err != nil {
|
||||
if err.(*ovh.APIError).Code == 404 {
|
||||
log.Printf("[DEBUG] Task id %d on VRack %s completed", taskId, serviceName)
|
||||
return taskId, "completed", nil
|
||||
} else {
|
||||
return taskId, "", err
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Pending Task id %d on VRack %s status: %s", r.Id, serviceName, r.Status)
|
||||
return taskId, r.Status, nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var testAccVRackPublicCloudAttachmentConfig = fmt.Sprintf(`
|
||||
resource "ovh_vrack_publiccloud_attachment" "attach" {
|
||||
vrack_id = "%s"
|
||||
project_id = "%s"
|
||||
}
|
||||
`, os.Getenv("OVH_VRACK"), os.Getenv("OVH_PUBLIC_CLOUD"))
|
||||
|
||||
func TestAccVRackPublicCloudAttachment_basic(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccCheckVRackPublicCloudAttachmentPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckVRackPublicCloudAttachmentDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccVRackPublicCloudAttachmentConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckVRackPublicCloudAttachmentExists("ovh_vrack_publiccloud_attachment.attach", t),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckVRackPublicCloudAttachmentPreCheck(t *testing.T) {
|
||||
testAccPreCheck(t)
|
||||
testAccCheckVRackExists(t)
|
||||
testAccCheckPublicCloudExists(t)
|
||||
}
|
||||
|
||||
func testAccCheckVRackPublicCloudAttachmentExists(n string, t *testing.T) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["vrack_id"] == "" {
|
||||
return fmt.Errorf("No VRack ID is set")
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["project_id"] == "" {
|
||||
return fmt.Errorf("No Project ID is set")
|
||||
}
|
||||
|
||||
return vrackPublicCloudAttachmentExists(rs.Primary.Attributes["vrack_id"], rs.Primary.Attributes["project_id"], config.OVHClient)
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckVRackPublicCloudAttachmentDestroy(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "ovh_vrack_publiccloud_attachment" {
|
||||
continue
|
||||
}
|
||||
|
||||
err := vrackPublicCloudAttachmentExists(rs.Primary.Attributes["vrack_id"], rs.Primary.Attributes["project_id"], config.OVHClient)
|
||||
if err == nil {
|
||||
return fmt.Errorf("VRack > Public Cloud Attachment still exists")
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Opts
|
||||
type PublicCloudPrivateNetworkCreateOpts struct {
|
||||
ProjectId string `json:"serviceName"`
|
||||
VlanId int `json:"vlanId"`
|
||||
Name string `json:"name"`
|
||||
Regions []string `json:"regions"`
|
||||
}
|
||||
|
||||
func (p *PublicCloudPrivateNetworkCreateOpts) String() string {
|
||||
return fmt.Sprintf("projectId: %s, vlanId:%d, name: %s, regions: %s", p.ProjectId, p.VlanId, p.Name, p.Regions)
|
||||
}
|
||||
|
||||
// Opts
|
||||
type PublicCloudPrivateNetworkUpdateOpts struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type PublicCloudPrivateNetworkRegion struct {
|
||||
Status string `json:"status"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
func (p *PublicCloudPrivateNetworkRegion) String() string {
|
||||
return fmt.Sprintf("Status:%s, Region: %s", p.Status, p.Region)
|
||||
}
|
||||
|
||||
type PublicCloudPrivateNetworkResponse struct {
|
||||
Id string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Vlanid int `json:"vlanId"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Regions []*PublicCloudPrivateNetworkRegion `json:"regions"`
|
||||
}
|
||||
|
||||
func (p *PublicCloudPrivateNetworkResponse) String() string {
|
||||
return fmt.Sprintf("Id: %s, Status: %s, Name: %s, Vlanid: %d, Type: %s, Regions: %s", p.Id, p.Status, p.Name, p.Vlanid, p.Type, p.Regions)
|
||||
}
|
||||
|
||||
// Opts
|
||||
type PublicCloudPrivateNetworksCreateOpts struct {
|
||||
ProjectId string `json:"serviceName"`
|
||||
NetworkId string `json:"networkId"`
|
||||
Dhcp bool `json:"dhcp"`
|
||||
NoGateway bool `json:"noGateway"`
|
||||
Start string `json:"start"`
|
||||
End string `json:"end"`
|
||||
Network string `json:"network"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
func (p *PublicCloudPrivateNetworksCreateOpts) String() string {
|
||||
return fmt.Sprintf("PCPNSCreateOpts[projectId: %s, networkId:%s, dchp: %v, noGateway: %v, network: %s, start: %s, end: %s, region: %s]",
|
||||
p.ProjectId, p.NetworkId, p.Dhcp, p.NoGateway, p.Network, p.Start, p.End, p.Region)
|
||||
}
|
||||
|
||||
type IPPool struct {
|
||||
Network string `json:"network"`
|
||||
Region string `json:"region"`
|
||||
Dhcp bool `json:"dhcp"`
|
||||
Start string `json:"start"`
|
||||
End string `json:"end"`
|
||||
}
|
||||
|
||||
func (p *IPPool) String() string {
|
||||
return fmt.Sprintf("IPPool[Network: %s, Region: %s, Dhcp: %v, Start: %s, End: %s]", p.Network, p.Region, p.Dhcp, p.Start, p.End)
|
||||
}
|
||||
|
||||
type PublicCloudPrivateNetworksResponse struct {
|
||||
Id string `json:"id"`
|
||||
GatewayIp string `json:"gatewayIp"`
|
||||
Cidr string `json:"cidr"`
|
||||
IPPools []*IPPool `json:"ipPools"`
|
||||
}
|
||||
|
||||
func (p *PublicCloudPrivateNetworksResponse) String() string {
|
||||
return fmt.Sprintf("PCPNSResponse[Id: %s, GatewayIp: %s, Cidr: %s, IPPools: %s]", p.Id, p.GatewayIp, p.Cidr, p.IPPools)
|
||||
}
|
||||
|
||||
// Opts
|
||||
type PublicCloudUserCreateOpts struct {
|
||||
ProjectId string `json:"serviceName"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
func (p *PublicCloudUserCreateOpts) String() string {
|
||||
return fmt.Sprintf("UserOpts[projectId: %s, description:%s]", p.ProjectId, p.Description)
|
||||
}
|
||||
|
||||
type PublicCloudUserResponse struct {
|
||||
Id int `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Status string `json:"status"`
|
||||
Description string `json:"description"`
|
||||
Password string `json:"password"`
|
||||
CreationDate string `json:"creationDate"`
|
||||
}
|
||||
|
||||
func (p *PublicCloudUserResponse) String() string {
|
||||
return fmt.Sprintf("UserResponse[Id: %v, Username: %s, Status: %s, Description: %s, CreationDate: %s]", p.Id, p.Username, p.Status, p.Description, p.CreationDate)
|
||||
}
|
||||
|
||||
type PublicCloudUserOpenstackRC struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// Opts
|
||||
type VRackAttachOpts struct {
|
||||
Project string `json:"project"`
|
||||
}
|
||||
|
||||
// Task Opts
|
||||
type TaskOpts struct {
|
||||
ServiceName string `json:"serviceName"`
|
||||
TaskId string `json:"taskId"`
|
||||
}
|
||||
|
||||
type VRackAttachTaskResponse struct {
|
||||
Id int `json:"id"`
|
||||
Function string `json:"function"`
|
||||
TargetDomain string `json:"targetDomain"`
|
||||
Status string `json:"status"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
OrderId int `json:"orderId"`
|
||||
LastUpdate time.Time `json:"lastUpdate"`
|
||||
TodoDate time.Time `json:"TodoDate"`
|
||||
}
|
||||
|
||||
type PublicCloudRegionResponse struct {
|
||||
ContinentCode string `json:"continentCode"`
|
||||
DatacenterLocation string `json:"datacenterLocation"`
|
||||
Name string `json:"name"`
|
||||
Services []PublicCloudServiceStatusResponse `json:"services"`
|
||||
}
|
||||
|
||||
func (r *PublicCloudRegionResponse) String() string {
|
||||
return fmt.Sprintf("Region: %s, Services: %s", r.Name, r.Services)
|
||||
}
|
||||
|
||||
type PublicCloudServiceStatusResponse struct {
|
||||
Status string `json:"status"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (s *PublicCloudServiceStatusResponse) String() string {
|
||||
return fmt.Sprintf("%s: %s", s.Name, s.Status)
|
||||
}
|
|
@ -53,6 +53,7 @@ import (
|
|||
opcprovider "github.com/hashicorp/terraform/builtin/providers/opc"
|
||||
openstackprovider "github.com/hashicorp/terraform/builtin/providers/openstack"
|
||||
opsgenieprovider "github.com/hashicorp/terraform/builtin/providers/opsgenie"
|
||||
ovhprovider "github.com/hashicorp/terraform/builtin/providers/ovh"
|
||||
packetprovider "github.com/hashicorp/terraform/builtin/providers/packet"
|
||||
pagerdutyprovider "github.com/hashicorp/terraform/builtin/providers/pagerduty"
|
||||
postgresqlprovider "github.com/hashicorp/terraform/builtin/providers/postgresql"
|
||||
|
@ -134,6 +135,7 @@ var InternalProviders = map[string]plugin.ProviderFunc{
|
|||
"opc": opcprovider.Provider,
|
||||
"openstack": openstackprovider.Provider,
|
||||
"opsgenie": opsgenieprovider.Provider,
|
||||
"ovh": ovhprovider.Provider,
|
||||
"packet": packetprovider.Provider,
|
||||
"pagerduty": pagerdutyprovider.Provider,
|
||||
"postgresql": postgresqlprovider.Provider,
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
Copyright (c) 2015-2017, OVH SAS.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of OVH SAS nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY OVH SAS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL OVH SAS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// Use variables for easier test overload
|
||||
var (
|
||||
systemConfigPath = "/etc/ovh.conf"
|
||||
userConfigPath = "/.ovh.conf" // prefixed with homeDir
|
||||
localConfigPath = "./ovh.conf"
|
||||
)
|
||||
|
||||
// currentUserHome attempts to get current user's home directory
|
||||
func currentUserHome() (string, error) {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return usr.HomeDir, nil
|
||||
}
|
||||
|
||||
// appendConfigurationFile only if it exists. We need to do this because
|
||||
// ini package will fail to load configuration at all if a configuration
|
||||
// file is missing. This is racy, but better than always failing.
|
||||
func appendConfigurationFile(cfg *ini.File, path string) {
|
||||
if file, err := os.Open(path); err == nil {
|
||||
file.Close()
|
||||
cfg.Append(path)
|
||||
}
|
||||
}
|
||||
|
||||
// loadConfig loads client configuration from params, environments or configuration
|
||||
// files (by order of decreasing precedence).
|
||||
//
|
||||
// loadConfig will check OVH_CONSUMER_KEY, OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET
|
||||
// and OVH_ENDPOINT environment variables. If any is present, it will take precedence
|
||||
// over any configuration from file.
|
||||
//
|
||||
// Configuration files are ini files. They share the same format as python-ovh,
|
||||
// node-ovh, php-ovh and all other wrappers. If any wrapper is configured, all
|
||||
// can re-use the same configuration. loadConfig will check for configuration in:
|
||||
//
|
||||
// - ./ovh.conf
|
||||
// - $HOME/.ovh.conf
|
||||
// - /etc/ovh.conf
|
||||
//
|
||||
func (c *Client) loadConfig(endpointName string) error {
|
||||
// Load configuration files by order of increasing priority. All configuration
|
||||
// files are optional. Only load file from user home if home could be resolve
|
||||
cfg := ini.Empty()
|
||||
appendConfigurationFile(cfg, systemConfigPath)
|
||||
if home, err := currentUserHome(); err == nil {
|
||||
userConfigFullPath := filepath.Join(home, userConfigPath)
|
||||
appendConfigurationFile(cfg, userConfigFullPath)
|
||||
}
|
||||
appendConfigurationFile(cfg, localConfigPath)
|
||||
|
||||
// Canonicalize configuration
|
||||
if endpointName == "" {
|
||||
endpointName = getConfigValue(cfg, "default", "endpoint", "ovh-eu")
|
||||
}
|
||||
|
||||
if c.AppKey == "" {
|
||||
c.AppKey = getConfigValue(cfg, endpointName, "application_key", "")
|
||||
}
|
||||
|
||||
if c.AppSecret == "" {
|
||||
c.AppSecret = getConfigValue(cfg, endpointName, "application_secret", "")
|
||||
}
|
||||
|
||||
if c.ConsumerKey == "" {
|
||||
c.ConsumerKey = getConfigValue(cfg, endpointName, "consumer_key", "")
|
||||
}
|
||||
|
||||
// Load real endpoint URL by name. If endpoint contains a '/', consider it as a URL
|
||||
if strings.Contains(endpointName, "/") {
|
||||
c.endpoint = endpointName
|
||||
} else {
|
||||
c.endpoint = Endpoints[endpointName]
|
||||
}
|
||||
|
||||
// If we still have no valid endpoint, AppKey or AppSecret, return an error
|
||||
if c.endpoint == "" {
|
||||
return fmt.Errorf("Unknown endpoint '%s'. Consider checking 'Endpoints' list of using an URL.", endpointName)
|
||||
}
|
||||
if c.AppKey == "" {
|
||||
return fmt.Errorf("Missing application key. Please check your configuration or consult the documentation to create one.")
|
||||
}
|
||||
if c.AppSecret == "" {
|
||||
return fmt.Errorf("Missing application secret. Please check your configuration or consult the documentation to create one.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getConfigValue returns the value of OVH_<NAME> or ``name`` value from ``section``. If
|
||||
// the value could not be read from either env or any configuration files, return 'def'
|
||||
func getConfigValue(cfg *ini.File, section, name, def string) string {
|
||||
// Attempt to load from environment
|
||||
fromEnv := os.Getenv("OVH_" + strings.ToUpper(name))
|
||||
if len(fromEnv) > 0 {
|
||||
return fromEnv
|
||||
}
|
||||
|
||||
// Attempt to load from configuration
|
||||
fromSection := cfg.Section(section)
|
||||
if fromSection == nil {
|
||||
return def
|
||||
}
|
||||
|
||||
fromSectionKey := fromSection.Key(name)
|
||||
if fromSectionKey == nil {
|
||||
return def
|
||||
}
|
||||
return fromSectionKey.String()
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package ovh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Map user friendly access level names to corresponding HTTP verbs
|
||||
var (
|
||||
ReadOnly = []string{"GET"}
|
||||
ReadWrite = []string{"GET", "POST", "PUT", "DELETE"}
|
||||
ReadWriteSafe = []string{"GET", "POST", "PUT"}
|
||||
)
|
||||
|
||||
// AccessRule represents a method allowed for a path
|
||||
type AccessRule struct {
|
||||
// Allowed HTTP Method for the requested AccessRule.
|
||||
// Can be set to GET/POST/PUT/DELETE.
|
||||
Method string `json:"method"`
|
||||
// Allowed path.
|
||||
// Can be an exact string or a string with '*' char.
|
||||
// Example :
|
||||
// /me : only /me is authorized
|
||||
// /* : all calls are authorized
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
// CkValidationState represents the response when asking a new consumerKey.
|
||||
type CkValidationState struct {
|
||||
// Consumer key, which need to be validated by customer.
|
||||
ConsumerKey string `json:"consumerKey"`
|
||||
// Current status, should be always "pendingValidation".
|
||||
State string `json:"state"`
|
||||
// URL to redirect user in order to log in.
|
||||
ValidationURL string `json:"validationUrl"`
|
||||
}
|
||||
|
||||
// CkRequest represents the parameters to fill in order to ask a new
|
||||
// consumerKey.
|
||||
type CkRequest struct {
|
||||
client *Client
|
||||
AccessRules []AccessRule `json:"accessRules"`
|
||||
Redirection string `json:"redirection,omitempty"`
|
||||
}
|
||||
|
||||
func (ck *CkValidationState) String() string {
|
||||
return fmt.Sprintf("CK: %q\nStatus: %q\nValidation URL: %q\n",
|
||||
ck.ConsumerKey,
|
||||
ck.State,
|
||||
ck.ValidationURL,
|
||||
)
|
||||
}
|
||||
|
||||
// NewCkRequest helps create a new ck request
|
||||
func (c *Client) NewCkRequest() *CkRequest {
|
||||
return &CkRequest{
|
||||
client: c,
|
||||
AccessRules: []AccessRule{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewCkRequestWithRedirection helps create a new ck request with a redirect URL
|
||||
func (c *Client) NewCkRequestWithRedirection(redirection string) *CkRequest {
|
||||
return &CkRequest{
|
||||
client: c,
|
||||
AccessRules: []AccessRule{},
|
||||
Redirection: redirection,
|
||||
}
|
||||
}
|
||||
|
||||
// AddRule adds a new rule to the ckRequest
|
||||
func (ck *CkRequest) AddRule(method, path string) {
|
||||
ck.AccessRules = append(ck.AccessRules, AccessRule{
|
||||
Method: method,
|
||||
Path: path,
|
||||
})
|
||||
}
|
||||
|
||||
// AddRules adds grant requests on "path" for all methods. "ReadOnly",
|
||||
// "ReadWrite" and "ReadWriteSafe" should be used for "methods" unless
|
||||
// specific access are required.
|
||||
func (ck *CkRequest) AddRules(methods []string, path string) {
|
||||
for _, method := range methods {
|
||||
ck.AddRule(method, path)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// AddRecursiveRules adds grant requests on "path" and "path/*", for all
|
||||
// methods "ReadOnly", "ReadWrite" and "ReadWriteSafe" should be used for
|
||||
// "methods" unless specific access are required.
|
||||
func (ck *CkRequest) AddRecursiveRules(methods []string, path string) {
|
||||
path = strings.TrimRight(path, "/")
|
||||
|
||||
// Add rules. Skip base rule when requesting access to "/"
|
||||
if path != "" {
|
||||
ck.AddRules(methods, path)
|
||||
}
|
||||
ck.AddRules(methods, path+"/*")
|
||||
}
|
||||
|
||||
// Do executes the request. On success, set the consumer key in the client
|
||||
// and return the URL the user needs to visit to validate the key
|
||||
func (ck *CkRequest) Do() (*CkValidationState, error) {
|
||||
state := CkValidationState{}
|
||||
err := ck.client.PostUnAuth("/auth/credential", ck, &state)
|
||||
|
||||
if err == nil {
|
||||
ck.client.ConsumerKey = state.ConsumerKey
|
||||
}
|
||||
|
||||
return &state, err
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package ovh
|
||||
|
||||
import "fmt"
|
||||
|
||||
// APIError represents an error that can occurred while calling the API.
|
||||
type APIError struct {
|
||||
// Error message.
|
||||
Message string
|
||||
// HTTP code.
|
||||
Code int
|
||||
// ID of the request
|
||||
QueryID string
|
||||
}
|
||||
|
||||
func (err *APIError) Error() string {
|
||||
return fmt.Sprintf("Error %d: %q", err.Code, err.Message)
|
||||
}
|
|
@ -0,0 +1,331 @@
|
|||
// Package ovh provides a HTTP wrapper for the OVH API.
|
||||
package ovh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultTimeout api requests after 180s
|
||||
const DefaultTimeout = 180 * time.Second
|
||||
|
||||
// Endpoints
|
||||
const (
|
||||
OvhEU = "https://eu.api.ovh.com/1.0"
|
||||
OvhCA = "https://ca.api.ovh.com/1.0"
|
||||
KimsufiEU = "https://eu.api.kimsufi.com/1.0"
|
||||
KimsufiCA = "https://ca.api.kimsufi.com/1.0"
|
||||
SoyoustartEU = "https://eu.api.soyoustart.com/1.0"
|
||||
SoyoustartCA = "https://ca.api.soyoustart.com/1.0"
|
||||
RunaboveCA = "https://api.runabove.com/1.0"
|
||||
)
|
||||
|
||||
// Endpoints conveniently maps endpoints names to their URI for external configuration
|
||||
var Endpoints = map[string]string{
|
||||
"ovh-eu": OvhEU,
|
||||
"ovh-ca": OvhCA,
|
||||
"kimsufi-eu": KimsufiEU,
|
||||
"kimsufi-ca": KimsufiCA,
|
||||
"soyoustart-eu": SoyoustartEU,
|
||||
"soyoustart-ca": SoyoustartCA,
|
||||
"runabove-ca": RunaboveCA,
|
||||
}
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrAPIDown = errors.New("go-vh: the OVH API is down, it does't respond to /time anymore")
|
||||
)
|
||||
|
||||
// Client represents a client to call the OVH API
|
||||
type Client struct {
|
||||
// Self generated tokens. Create one by visiting
|
||||
// https://eu.api.ovh.com/createApp/
|
||||
// AppKey holds the Application key
|
||||
AppKey string
|
||||
|
||||
// AppSecret holds the Application secret key
|
||||
AppSecret string
|
||||
|
||||
// ConsumerKey holds the user/app specific token. It must have been validated before use.
|
||||
ConsumerKey string
|
||||
|
||||
// API endpoint
|
||||
endpoint string
|
||||
|
||||
// Client is the underlying HTTP client used to run the requests. It may be overloaded but a default one is instanciated in ``NewClient`` by default.
|
||||
Client *http.Client
|
||||
|
||||
// Ensures that the timeDelta function is only ran once
|
||||
// sync.Once would consider init done, even in case of error
|
||||
// hence a good old flag
|
||||
timeDeltaMutex *sync.Mutex
|
||||
timeDeltaDone bool
|
||||
timeDelta time.Duration
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// NewClient represents a new client to call the API
|
||||
func NewClient(endpoint, appKey, appSecret, consumerKey string) (*Client, error) {
|
||||
client := Client{
|
||||
AppKey: appKey,
|
||||
AppSecret: appSecret,
|
||||
ConsumerKey: consumerKey,
|
||||
Client: &http.Client{},
|
||||
timeDeltaMutex: &sync.Mutex{},
|
||||
timeDeltaDone: false,
|
||||
Timeout: time.Duration(DefaultTimeout),
|
||||
}
|
||||
|
||||
// Get and check the configuration
|
||||
if err := client.loadConfig(endpoint); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
// NewEndpointClient will create an API client for specified
|
||||
// endpoint and load all credentials from environment or
|
||||
// configuration files
|
||||
func NewEndpointClient(endpoint string) (*Client, error) {
|
||||
return NewClient(endpoint, "", "", "")
|
||||
}
|
||||
|
||||
// NewDefaultClient will load all it's parameter from environment
|
||||
// or configuration files
|
||||
func NewDefaultClient() (*Client, error) {
|
||||
return NewClient("", "", "", "")
|
||||
}
|
||||
|
||||
//
|
||||
// High level helpers
|
||||
//
|
||||
|
||||
// Ping performs a ping to OVH API.
|
||||
// In fact, ping is just a /auth/time call, in order to check if API is up.
|
||||
func (c *Client) Ping() error {
|
||||
_, err := c.getTime()
|
||||
return err
|
||||
}
|
||||
|
||||
// TimeDelta represents the delay between the machine that runs the code and the
|
||||
// OVH API. The delay shouldn't change, let's do it only once.
|
||||
func (c *Client) TimeDelta() (time.Duration, error) {
|
||||
return c.getTimeDelta()
|
||||
}
|
||||
|
||||
// Time returns time from the OVH API, by asking GET /auth/time.
|
||||
func (c *Client) Time() (*time.Time, error) {
|
||||
return c.getTime()
|
||||
}
|
||||
|
||||
//
|
||||
// Common request wrappers
|
||||
//
|
||||
|
||||
// Get is a wrapper for the GET method
|
||||
func (c *Client) Get(url string, resType interface{}) error {
|
||||
return c.CallAPI("GET", url, nil, resType, true)
|
||||
}
|
||||
|
||||
// GetUnAuth is a wrapper for the unauthenticated GET method
|
||||
func (c *Client) GetUnAuth(url string, resType interface{}) error {
|
||||
return c.CallAPI("GET", url, nil, resType, false)
|
||||
}
|
||||
|
||||
// Post is a wrapper for the POST method
|
||||
func (c *Client) Post(url string, reqBody, resType interface{}) error {
|
||||
return c.CallAPI("POST", url, reqBody, resType, true)
|
||||
}
|
||||
|
||||
// PostUnAuth is a wrapper for the unauthenticated POST method
|
||||
func (c *Client) PostUnAuth(url string, reqBody, resType interface{}) error {
|
||||
return c.CallAPI("POST", url, reqBody, resType, false)
|
||||
}
|
||||
|
||||
// Put is a wrapper for the PUT method
|
||||
func (c *Client) Put(url string, reqBody, resType interface{}) error {
|
||||
return c.CallAPI("PUT", url, reqBody, resType, true)
|
||||
}
|
||||
|
||||
// PutUnAuth is a wrapper for the unauthenticated PUT method
|
||||
func (c *Client) PutUnAuth(url string, reqBody, resType interface{}) error {
|
||||
return c.CallAPI("PUT", url, reqBody, resType, false)
|
||||
}
|
||||
|
||||
// Delete is a wrapper for the DELETE method
|
||||
func (c *Client) Delete(url string, resType interface{}) error {
|
||||
return c.CallAPI("DELETE", url, nil, resType, true)
|
||||
}
|
||||
|
||||
// DeleteUnAuth is a wrapper for the unauthenticated DELETE method
|
||||
func (c *Client) DeleteUnAuth(url string, resType interface{}) error {
|
||||
return c.CallAPI("DELETE", url, nil, resType, false)
|
||||
}
|
||||
|
||||
//
|
||||
// Low level API access
|
||||
//
|
||||
|
||||
// getResult check the response and unmarshals it into the response type if needed.
|
||||
// Helper function, called from CallAPI.
|
||||
func (c *Client) getResponse(response *http.Response, resType interface{}) error {
|
||||
// Read all the response body
|
||||
defer response.Body.Close()
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// < 200 && >= 300 : API error
|
||||
if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices {
|
||||
apiError := &APIError{Code: response.StatusCode}
|
||||
if err = json.Unmarshal(body, apiError); err != nil {
|
||||
return err
|
||||
}
|
||||
apiError.QueryID = response.Header.Get("X-Ovh-QueryID")
|
||||
return apiError
|
||||
}
|
||||
|
||||
// Nothing to unmarshal
|
||||
if len(body) == 0 || resType == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return json.Unmarshal(body, &resType)
|
||||
}
|
||||
|
||||
// timeDelta returns the time delta between the host and the remote API
|
||||
func (c *Client) getTimeDelta() (time.Duration, error) {
|
||||
|
||||
if !c.timeDeltaDone {
|
||||
// Ensure only one thread is updating
|
||||
c.timeDeltaMutex.Lock()
|
||||
|
||||
// Did we wait ? Maybe no more needed
|
||||
if !c.timeDeltaDone {
|
||||
ovhTime, err := c.getTime()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
c.timeDelta = time.Since(*ovhTime)
|
||||
c.timeDeltaDone = true
|
||||
}
|
||||
c.timeDeltaMutex.Unlock()
|
||||
}
|
||||
|
||||
return c.timeDelta, nil
|
||||
}
|
||||
|
||||
// getTime t returns time from for a given api client endpoint
|
||||
func (c *Client) getTime() (*time.Time, error) {
|
||||
var timestamp int64
|
||||
|
||||
err := c.GetUnAuth("/auth/time", ×tamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverTime := time.Unix(timestamp, 0)
|
||||
return &serverTime, nil
|
||||
}
|
||||
|
||||
// getLocalTime is a function to be overwritten during the tests, it return the time
|
||||
// on the the local machine
|
||||
var getLocalTime = func() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// getEndpointForSignature is a function to be overwritten during the tests, it returns a
|
||||
// the endpoint
|
||||
var getEndpointForSignature = func(c *Client) string {
|
||||
return c.endpoint
|
||||
}
|
||||
|
||||
// CallAPI is the lowest level call helper. If needAuth is true,
|
||||
// inject authentication headers and sign the request.
|
||||
//
|
||||
// Request signature is a sha1 hash on following fields, joined by '+':
|
||||
// - applicationSecret (from Client instance)
|
||||
// - consumerKey (from Client instance)
|
||||
// - capitalized method (from arguments)
|
||||
// - full request url, including any query string argument
|
||||
// - full serialized request body
|
||||
// - server current time (takes time delta into account)
|
||||
//
|
||||
// Call will automatically assemble the target url from the endpoint
|
||||
// configured in the client instance and the path argument. If the reqBody
|
||||
// argument is not nil, it will also serialize it as json and inject
|
||||
// the required Content-Type header.
|
||||
//
|
||||
// If everyrthing went fine, unmarshall response into resType and return nil
|
||||
// otherwise, return the error
|
||||
func (c *Client) CallAPI(method, path string, reqBody, resType interface{}, needAuth bool) error {
|
||||
var body []byte
|
||||
var err error
|
||||
|
||||
if reqBody != nil {
|
||||
body, err = json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
target := fmt.Sprintf("%s%s", c.endpoint, path)
|
||||
req, err := http.NewRequest(method, target, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Inject headers
|
||||
if body != nil {
|
||||
req.Header.Add("Content-Type", "application/json;charset=utf-8")
|
||||
}
|
||||
req.Header.Add("X-Ovh-Application", c.AppKey)
|
||||
req.Header.Add("Accept", "application/json")
|
||||
|
||||
// Inject signature. Some methods do not need authentication, especially /time,
|
||||
// /auth and some /order methods are actually broken if authenticated.
|
||||
if needAuth {
|
||||
timeDelta, err := c.TimeDelta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timestamp := getLocalTime().Add(-timeDelta).Unix()
|
||||
|
||||
req.Header.Add("X-Ovh-Timestamp", strconv.FormatInt(timestamp, 10))
|
||||
req.Header.Add("X-Ovh-Consumer", c.ConsumerKey)
|
||||
|
||||
h := sha1.New()
|
||||
h.Write([]byte(fmt.Sprintf("%s+%s+%s+%s%s+%s+%d",
|
||||
c.AppSecret,
|
||||
c.ConsumerKey,
|
||||
method,
|
||||
getEndpointForSignature(c),
|
||||
path,
|
||||
body,
|
||||
timestamp,
|
||||
)))
|
||||
req.Header.Add("X-Ovh-Signature", fmt.Sprintf("$1$%x", h.Sum(nil)))
|
||||
}
|
||||
|
||||
// Send the request with requested timeout
|
||||
c.Client.Timeout = c.Timeout
|
||||
response, err := c.Client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unmarshal the result into the resType if possible
|
||||
return c.getResponse(response, resType)
|
||||
}
|
|
@ -2774,6 +2774,12 @@
|
|||
"revision": "2654b36ec837f913b546aec27f7717b5ac664cc8",
|
||||
"revisionTime": "2017-01-24T12:11:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "9vQ5+o0oO/0deG+uokvvdpeSv0Y=",
|
||||
"path": "github.com/ovh/go-ovh/ovh",
|
||||
"revision": "d2207178e10e4527e8f222fd8707982df8c3af17",
|
||||
"revisionTime": "2017-01-02T12:05:21Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "d1VHczIGt2bhYR0BNkTedIS/9uk=",
|
||||
"path": "github.com/packer-community/winrmcp/winrmcp",
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
layout: "ovh"
|
||||
page_title: "OVH: publiccloud_region"
|
||||
sidebar_current: "docs-ovh-datasource-publiccloud-region"
|
||||
description: |-
|
||||
Get information & status of a region associated with a public cloud project.
|
||||
---
|
||||
|
||||
# publiccloud\_region
|
||||
|
||||
Use this data source to retrieve information about a region associated with a
|
||||
public cloud project. The region must be associated with the project.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```hcl
|
||||
data "ovh_publiccloud_region" "GRA1" {
|
||||
project_id = "XXXXXX"
|
||||
region = "GRA1"
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
|
||||
* `project_id` - (Required) The id of the public cloud project. If omitted,
|
||||
the `OVH_PROJECT_ID` environment variable is used.
|
||||
|
||||
* `region` - (Required) The name of the region associated with the public cloud
|
||||
project.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
`id` is set to the ID of the project concatenated with the name of the region.
|
||||
In addition, the following attributes are exported:
|
||||
|
||||
* `continentCode` - the code of the geographic continent the region is running.
|
||||
E.g.: EU for Europe, US for America...
|
||||
* `datacenterLocation` - The location code of the datacenter.
|
||||
E.g.: "GRA", meaning Gravelines, for region "GRA1"
|
||||
* `services` - The list of public cloud services running within the region
|
||||
* `name` - the name of the public cloud service
|
||||
* `status` - the status of the service
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
layout: "ovh"
|
||||
page_title: "OVH: publiccloud_regions"
|
||||
sidebar_current: "docs-ovh-datasource-publiccloud-regions"
|
||||
description: |-
|
||||
Get the list of regions associated with a public cloud project.
|
||||
---
|
||||
|
||||
# publiccloud\_regions
|
||||
|
||||
Use this data source to get the regions of a public cloud project.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```hcl
|
||||
data "ovh_publiccloud_regions" "regions" {
|
||||
project_id = "XXXXXX"
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
|
||||
* `project_id` - (Required) The id of the public cloud project. If omitted,
|
||||
the `OVH_PROJECT_ID` environment variable is used.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
`id` is set to the ID of the project. In addition, the following attributes
|
||||
are exported:
|
||||
|
||||
* `regions` - The list of regions associated with the project
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
layout: "ovh"
|
||||
page_title: "Provider: OVH"
|
||||
sidebar_current: "docs-ovh-index"
|
||||
description: |-
|
||||
The OVH provider is used to interact with the many resources supported by OVH. The provider needs to be configured with the proper credentials before it can be used.
|
||||
---
|
||||
|
||||
# OVH Provider
|
||||
|
||||
The OVH provider is used to interact with the
|
||||
many resources supported by OVH. 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 OVH Provider
|
||||
provider "ovh" {
|
||||
endpoint = "ovh-eu"
|
||||
application_key = "yyyyyy"
|
||||
application_secret = "xxxxxxxxxxxxxx"
|
||||
consumer_key = "zzzzzzzzzzzzzz"
|
||||
}
|
||||
|
||||
# Create a public cloud user
|
||||
resource "ovh_publiccloud_user" "user-test" {
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `endpoint` - (Required) Specify which API endpoint to use.
|
||||
It can be set using the OVH_ENDPOINT environment
|
||||
variable. Value can be set to either "ovh-eu" or "ovh-ca".
|
||||
|
||||
* `application_key` - (Required) The API Application Key. If omitted,
|
||||
the `OVH_APPLICATION_KEY` environment variable is used.
|
||||
|
||||
* `application_secret` - (Required) The API Application Secret. If omitted,
|
||||
the `OVH_APPLICATION_SECRET` environment variable is used.
|
||||
|
||||
* `consumer_key` - (Required) The API Consumer key. If omitted,
|
||||
the `OVH_CONSUMER_KEY` environment variable is used.
|
||||
|
||||
|
||||
## Testing and Development
|
||||
|
||||
In order to run the Acceptance Tests for development, the following environment
|
||||
variables must also be set:
|
||||
|
||||
* `OVH_VRACK` - The id of the vrack to use.
|
||||
|
||||
* `OVH_PUBLIC_CLOUD` - The id of the public cloud project.
|
||||
|
||||
You should be able to use any OVH environment to develop on as long as the
|
||||
above environment variables are set.
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
layout: "ovh"
|
||||
page_title: "OVH: publiccloud_private_network"
|
||||
sidebar_current: "docs-ovh-resource-publiccloud-private-network"
|
||||
description: |-
|
||||
Creates a private network in a public cloud project.
|
||||
---
|
||||
|
||||
# ovh_publiccloud\_private_network
|
||||
|
||||
Creates a private network in a public cloud project.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
resource "ovh_publiccloud_private_network" "net" {
|
||||
project_id = "67890"
|
||||
name = "admin_network"
|
||||
regions = ["GRA1", "BHS1"]
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `project_id` - (Required) The id of the public cloud project. If omitted,
|
||||
the `OVH_PROJECT_ID` environment variable is used.
|
||||
|
||||
* `name` - (Required) The name of the network.
|
||||
|
||||
* `vlan_id` - a vlan id to associate with the network.
|
||||
Changing this value recreates the resource. Defaults to 0.
|
||||
|
||||
* `regions` - an array of valid OVH public cloud region ID in which the network
|
||||
will be available. Ex.: "GRA1". Defaults to all public cloud regions.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `project_id` - See Argument Reference above.
|
||||
* `name` - See Argument Reference above.
|
||||
* `vland_id` - See Argument Reference above.
|
||||
* `regions` - See Argument Reference above.
|
||||
* `regions_status` - A map representing the status of the network per region.
|
||||
* `regions_status/region` - The id of the region.
|
||||
* `regions_status/status` - The status of the network in the region.
|
||||
* `status` - the status of the network. should be normally set to 'ACTIVE'.
|
||||
* `type` - the type of the network. Either 'private' or 'public'.
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
layout: "ovh"
|
||||
page_title: "OVH: publiccloud_private_network_subnet"
|
||||
sidebar_current: "docs-ovh-resource-publiccloud-private-network-subnet"
|
||||
description: |-
|
||||
Creates a subnet in a private network of a public cloud project.
|
||||
---
|
||||
|
||||
# ovh_publiccloud\_private_network\_subnet
|
||||
|
||||
Creates a subnet in a private network of a public cloud project.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
resource "ovh_publiccloud_private_network_subnet" "subnet" {
|
||||
project_id = "67890"
|
||||
network_id = "0234543"
|
||||
region = "GRA1"
|
||||
start = "192.168.168.100"
|
||||
end = "192.168.168.200"
|
||||
network = "192.168.168.0/24"
|
||||
dhcp = true
|
||||
no_gateway = false
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `project_id` - (Required) The id of the public cloud project. If omitted,
|
||||
the `OVH_PROJECT_ID` environment variable is used.
|
||||
Changing this forces a new resource to be created.
|
||||
|
||||
* `network_id` - (Required) The id of the network.
|
||||
Changing this forces a new resource to be created.
|
||||
|
||||
* `dhcp` - (Optional) Enable DHCP.
|
||||
Changing this forces a new resource to be created. Defaults to false.
|
||||
_
|
||||
* `start` - (Required) First ip for this region.
|
||||
Changing this value recreates the subnet.
|
||||
|
||||
* `end` - (Required) Last ip for this region.
|
||||
Changing this value recreates the subnet.
|
||||
|
||||
* `network` - (Required) Global network in CIDR format.
|
||||
Changing this value recreates the subnet
|
||||
|
||||
* `region` - The region in which the network subnet will be created.
|
||||
Ex.: "GRA1". Changing this value recreates the resource.
|
||||
|
||||
* `no_gateway` - Set to true if you don't want to set a default gateway IP.
|
||||
Changing this value recreates the resource. Defaults to false.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `project_id` - See Argument Reference above.
|
||||
* `network_id` - See Argument Reference above.
|
||||
* `dhcp_id` - See Argument Reference above.
|
||||
* `start` - See Argument Reference above.
|
||||
* `end` - See Argument Reference above.
|
||||
* `network` - See Argument Reference above.
|
||||
* `region` - See Argument Reference above.
|
||||
* `no_gateway` - See Argument Reference above.
|
||||
* `cidr` - Ip Block representing the subnet cidr.
|
||||
* `ip_pools` - List of ip pools allocated in the subnet.
|
||||
* `ip_pools/network` - Global network with cidr.
|
||||
* `ip_pools/region` - Region where this subnet is created.
|
||||
* `ip_pools/dhcp` - DHCP enabled.
|
||||
* `ip_pools/end` - Last ip for this region.
|
||||
* `ip_pools/start` - First ip for this region.
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
layout: "ovh"
|
||||
page_title: "OVH: publiccloud_user"
|
||||
sidebar_current: "docs-ovh-resource-publiccloud-user"
|
||||
description: |-
|
||||
Creates a user in a public cloud project.
|
||||
---
|
||||
|
||||
# ovh_publiccloud\_user
|
||||
|
||||
Creates a user in a public cloud project.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
resource "ovh_publiccloud_user" "user1" {
|
||||
project_id = "67890"
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `project_id` - (Required) The id of the public cloud project. If omitted,
|
||||
the `OVH_PROJECT_ID` environment variable is used.
|
||||
|
||||
* `description` - A description associated with the user.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `project_id` - See Argument Reference above.
|
||||
* `description` - See Argument Reference above.
|
||||
* `username` - the username generated for the user. This username can be used with
|
||||
the Openstack API.
|
||||
* `password` - (Sensitive) the password generated for the user. The password can
|
||||
be used with the Openstack API. This attribute is sensitive and will only be
|
||||
retrieve once during creation.
|
||||
* `status` - the status of the user. should be normally set to 'ok'.
|
||||
* `creation_date` - the date the user was created.
|
||||
* `openstack_rc` - a convenient map representing an openstack_rc file.
|
||||
Note: no password nor sensitive token is set in this map.
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
layout: "ovh"
|
||||
page_title: "OVH: vrack_publiccloud_attachment"
|
||||
sidebar_current: "docs-ovh-resource-vrack-publiccloud-attachment"
|
||||
description: |-
|
||||
Attach an existing PublicCloud project to an existing VRack.
|
||||
---
|
||||
|
||||
# ovh_vrack\_publiccloud\_attachment
|
||||
|
||||
Attach an existing PublicCloud project to an existing VRack.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
resource "ovh_vrack_publiccloud_attachment" "attach" {
|
||||
vrack_id = "12345"
|
||||
project_id = "67890"
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `vrack_id` - (Required) The id of the vrack. If omitted, the `OVH_VRACK_ID`
|
||||
environment variable is used.
|
||||
|
||||
* `project_id` - (Required) The id of the public cloud project. If omitted,
|
||||
the `OVH_VRACK_ID` environment variable is used.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `vrack_id` - See Argument Reference above.
|
||||
* `project_id` - See Argument Reference above.
|
||||
|
||||
## Notes
|
||||
|
||||
The vrack attachment isn't a proper resource with an ID. As such, the resource id will
|
||||
be forged from the vrack and project ids and there's no correct way to import the
|
||||
resource in terraform. When the resource is created by terraform, it first checks if the
|
||||
attachment already exists within OVH infrastructure; if it exists it set the resource id
|
||||
without modifying anything. Otherwise, it will try to attach the vrack with the public
|
||||
cloud project.
|
|
@ -364,6 +364,10 @@
|
|||
<a href="/docs/providers/opsgenie/index.html">OpsGenie</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-providers-ovh") %>>
|
||||
<a href="/docs/providers/ovh/index.html">OVH</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-providers-packet") %>>
|
||||
<a href="/docs/providers/packet/index.html">Packet</a>
|
||||
</li>
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<% wrap_layout :inner do %>
|
||||
<% content_for :sidebar do %>
|
||||
<div class="docs-sidebar hidden-print affix-top" role="complementary">
|
||||
<ul class="nav docs-sidenav">
|
||||
<li<%= sidebar_current("docs-home") %>>
|
||||
<a href="/docs/providers/index.html">« Documentation Home</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-ovh-index") %>>
|
||||
<a href="/docs/providers/ovh/index.html">OVH Provider</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current(/^docs-ovh-resource-vrack/) %>>
|
||||
<a href="#">VRack Resources</a>
|
||||
<ul class="nav nav-visible">
|
||||
<li<%= sidebar_current("docs-ovh-resource-vrack-publicloud-attachment") %>>
|
||||
<a href="/docs/providers/ovh/r/vrack_publiccloud_attachment.html">ovh_vrack_publiccloud_attachment</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current(/^docs-ovh-publiccloud/) %>>
|
||||
<a href="#">Public Cloud Resources</a>
|
||||
<ul class="nav nav-visible">
|
||||
<li<%= sidebar_current("docs-ovh-resource-publiccloud-private-network") %>>
|
||||
<a href="/docs/providers/ovh/r/publiccloud_private_network.html">ovh_publiccloud_private_network</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-ovh-resource-publiccloud-private-network-subnet") %>>
|
||||
<a href="/docs/providers/ovh/r/publiccloud_private_network_subnet.html">ovh_publiccloud_private_network_subnet</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-ovh-resource-publiccloud-user") %>>
|
||||
<a href="/docs/providers/ovh/r/publiccloud_user.html">ovh_publiccloud_user</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= yield %>
|
||||
<% end %>
|
Loading…
Reference in New Issue