From cd7f69ab1112b091b212fc0a1f970c4c4fd4c85e Mon Sep 17 00:00:00 2001 From: Kazumichi Yamamoto Date: Tue, 14 Feb 2017 04:11:30 +0900 Subject: [PATCH] New provider arukas (#11171) * Add a Arukas provider * Add dependencies for the Arukas provider * Add documents for the Arukas --- builtin/bins/provider-arukas/main.go | 12 + builtin/bins/provider-arukas/main_test.go | 1 + builtin/providers/arukas/config.go | 52 +++ builtin/providers/arukas/provider.go | 59 +++ builtin/providers/arukas/provider_test.go | 38 ++ .../arukas/resource_arukas_container.go | 293 +++++++++++++ .../arukas/resource_arukas_container_test.go | 279 +++++++++++++ builtin/providers/arukas/structure.go | 110 +++++ builtin/providers/arukas/validators.go | 92 +++++ command/internal_plugin_list.go | 2 + vendor/github.com/arukasio/cli/BUILDING.md | 61 +++ vendor/github.com/arukasio/cli/Dockerfile | 15 + vendor/github.com/arukasio/cli/LICENSE.txt | 21 + vendor/github.com/arukasio/cli/Makefile | 71 ++++ vendor/github.com/arukasio/cli/README.md | 37 ++ vendor/github.com/arukasio/cli/Vagrantfile | 65 +++ vendor/github.com/arukasio/cli/app_sets.go | 114 +++++ vendor/github.com/arukasio/cli/apps.go | 51 +++ vendor/github.com/arukasio/cli/circle.yml | 36 ++ vendor/github.com/arukasio/cli/client.go | 324 +++++++++++++++ vendor/github.com/arukasio/cli/container.go | 144 +++++++ vendor/github.com/arukasio/cli/json_time.go | 35 ++ vendor/github.com/arukasio/cli/user.go | 32 ++ .../gedex/inflector/CakePHP_LICENSE.txt | 28 ++ vendor/github.com/gedex/inflector/LICENSE.md | 29 ++ vendor/github.com/gedex/inflector/README.md | 25 ++ .../github.com/gedex/inflector/inflector.go | 355 ++++++++++++++++ vendor/github.com/manyminds/api2go/LICENSE | 21 + .../manyminds/api2go/jsonapi/Readme.md | 14 + .../manyminds/api2go/jsonapi/data_structs.go | 150 +++++++ .../manyminds/api2go/jsonapi/entity_namer.go | 9 + .../manyminds/api2go/jsonapi/helpers.go | 65 +++ .../manyminds/api2go/jsonapi/marshal.go | 388 ++++++++++++++++++ .../manyminds/api2go/jsonapi/unmarshal.go | 233 +++++++++++ vendor/vendor.json | 18 + website/source/assets/stylesheets/_docs.scss | 1 + .../docs/providers/arukas/index.html.markdown | 82 ++++ .../arukas/r/container.html.markdown | 98 +++++ website/source/layouts/arukas.erb | 26 ++ website/source/layouts/docs.erb | 4 + 40 files changed, 3490 insertions(+) create mode 100644 builtin/bins/provider-arukas/main.go create mode 100644 builtin/bins/provider-arukas/main_test.go create mode 100644 builtin/providers/arukas/config.go create mode 100644 builtin/providers/arukas/provider.go create mode 100644 builtin/providers/arukas/provider_test.go create mode 100644 builtin/providers/arukas/resource_arukas_container.go create mode 100644 builtin/providers/arukas/resource_arukas_container_test.go create mode 100644 builtin/providers/arukas/structure.go create mode 100644 builtin/providers/arukas/validators.go create mode 100644 vendor/github.com/arukasio/cli/BUILDING.md create mode 100644 vendor/github.com/arukasio/cli/Dockerfile create mode 100644 vendor/github.com/arukasio/cli/LICENSE.txt create mode 100644 vendor/github.com/arukasio/cli/Makefile create mode 100644 vendor/github.com/arukasio/cli/README.md create mode 100644 vendor/github.com/arukasio/cli/Vagrantfile create mode 100644 vendor/github.com/arukasio/cli/app_sets.go create mode 100644 vendor/github.com/arukasio/cli/apps.go create mode 100644 vendor/github.com/arukasio/cli/circle.yml create mode 100644 vendor/github.com/arukasio/cli/client.go create mode 100644 vendor/github.com/arukasio/cli/container.go create mode 100644 vendor/github.com/arukasio/cli/json_time.go create mode 100644 vendor/github.com/arukasio/cli/user.go create mode 100644 vendor/github.com/gedex/inflector/CakePHP_LICENSE.txt create mode 100644 vendor/github.com/gedex/inflector/LICENSE.md create mode 100644 vendor/github.com/gedex/inflector/README.md create mode 100644 vendor/github.com/gedex/inflector/inflector.go create mode 100644 vendor/github.com/manyminds/api2go/LICENSE create mode 100644 vendor/github.com/manyminds/api2go/jsonapi/Readme.md create mode 100644 vendor/github.com/manyminds/api2go/jsonapi/data_structs.go create mode 100644 vendor/github.com/manyminds/api2go/jsonapi/entity_namer.go create mode 100644 vendor/github.com/manyminds/api2go/jsonapi/helpers.go create mode 100644 vendor/github.com/manyminds/api2go/jsonapi/marshal.go create mode 100644 vendor/github.com/manyminds/api2go/jsonapi/unmarshal.go create mode 100644 website/source/docs/providers/arukas/index.html.markdown create mode 100644 website/source/docs/providers/arukas/r/container.html.markdown create mode 100644 website/source/layouts/arukas.erb diff --git a/builtin/bins/provider-arukas/main.go b/builtin/bins/provider-arukas/main.go new file mode 100644 index 000000000..39504e9e1 --- /dev/null +++ b/builtin/bins/provider-arukas/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/hashicorp/terraform/builtin/providers/arukas" + "github.com/hashicorp/terraform/plugin" +) + +func main() { + plugin.Serve(&plugin.ServeOpts{ + ProviderFunc: arukas.Provider, + }) +} diff --git a/builtin/bins/provider-arukas/main_test.go b/builtin/bins/provider-arukas/main_test.go new file mode 100644 index 000000000..06ab7d0f9 --- /dev/null +++ b/builtin/bins/provider-arukas/main_test.go @@ -0,0 +1 @@ +package main diff --git a/builtin/providers/arukas/config.go b/builtin/providers/arukas/config.go new file mode 100644 index 000000000..330c4a76b --- /dev/null +++ b/builtin/providers/arukas/config.go @@ -0,0 +1,52 @@ +package arukas + +import ( + API "github.com/arukasio/cli" + "os" + "time" +) + +const ( + JSONTokenParamName = "ARUKAS_JSON_API_TOKEN" + JSONSecretParamName = "ARUKAS_JSON_API_SECRET" + JSONUrlParamName = "ARUKAS_JSON_API_URL" + JSONDebugParamName = "ARUKAS_DEBUG" + JSONTimeoutParamName = "ARUKAS_TIMEOUT" +) + +type Config struct { + Token string + Secret string + URL string + Trace string + Timeout int +} + +func (c *Config) NewClient() (*ArukasClient, error) { + + os.Setenv(JSONTokenParamName, c.Token) + os.Setenv(JSONSecretParamName, c.Secret) + os.Setenv(JSONUrlParamName, c.URL) + os.Setenv(JSONDebugParamName, c.Trace) + + client, err := API.NewClient() + if err != nil { + return nil, err + } + client.UserAgent = "Terraform for Arukas" + + timeout := time.Duration(0) + if c.Timeout > 0 { + timeout = time.Duration(c.Timeout) * time.Second + } + + return &ArukasClient{ + Client: client, + Timeout: timeout, + }, nil +} + +type ArukasClient struct { + *API.Client + Timeout time.Duration +} diff --git a/builtin/providers/arukas/provider.go b/builtin/providers/arukas/provider.go new file mode 100644 index 000000000..d977e68a8 --- /dev/null +++ b/builtin/providers/arukas/provider.go @@ -0,0 +1,59 @@ +package arukas + +import ( + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +// Provider returns a terraform.ResourceProvider. +func Provider() terraform.ResourceProvider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "token": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc(JSONTokenParamName, nil), + Description: "your Arukas APIKey(token)", + }, + "secret": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc(JSONSecretParamName, nil), + Description: "your Arukas APIKey(secret)", + }, + "api_url": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc(JSONUrlParamName, "https://app.arukas.io/api/"), + Description: "default Arukas API url", + }, + "trace": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc(JSONDebugParamName, ""), + }, + "timeout": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc(JSONTimeoutParamName, "600"), + }, + }, + ResourcesMap: map[string]*schema.Resource{ + "arukas_container": resourceArukasContainer(), + }, + ConfigureFunc: providerConfigure, + } +} + +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + + config := Config{ + Token: d.Get("token").(string), + Secret: d.Get("secret").(string), + URL: d.Get("api_url").(string), + Trace: d.Get("trace").(string), + Timeout: d.Get("timeout").(int), + } + + return config.NewClient() +} diff --git a/builtin/providers/arukas/provider_test.go b/builtin/providers/arukas/provider_test.go new file mode 100644 index 000000000..d1b5d87e6 --- /dev/null +++ b/builtin/providers/arukas/provider_test.go @@ -0,0 +1,38 @@ +package arukas + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +var testAccProviders map[string]terraform.ResourceProvider +var testAccProvider *schema.Provider + +func init() { + testAccProvider = Provider().(*schema.Provider) + testAccProviders = map[string]terraform.ResourceProvider{ + "arukas": testAccProvider, + } +} + +func TestProvider(t *testing.T) { + if err := Provider().(*schema.Provider).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestProvider_impl(t *testing.T) { + var _ terraform.ResourceProvider = Provider() +} + +func testAccPreCheck(t *testing.T) { + if v := os.Getenv("ARUKAS_JSON_API_TOKEN"); v == "" { + t.Fatal("ARUKAS_JSON_API_TOKEN must be set for acceptance tests") + } + if v := os.Getenv("ARUKAS_JSON_API_SECRET"); v == "" { + t.Fatal("ARUKAS_JSON_API_SECRET must be set for acceptance tests") + } +} diff --git a/builtin/providers/arukas/resource_arukas_container.go b/builtin/providers/arukas/resource_arukas_container.go new file mode 100644 index 000000000..bc2132815 --- /dev/null +++ b/builtin/providers/arukas/resource_arukas_container.go @@ -0,0 +1,293 @@ +package arukas + +import ( + "fmt" + API "github.com/arukasio/cli" + "github.com/hashicorp/terraform/helper/schema" + "strings" + "time" +) + +func resourceArukasContainer() *schema.Resource { + return &schema.Resource{ + Create: resourceArukasContainerCreate, + Read: resourceArukasContainerRead, + Update: resourceArukasContainerUpdate, + Delete: resourceArukasContainerDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "image": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "instances": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 1, + ValidateFunc: validateIntegerInRange(1, 10), + }, + "memory": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 256, + ValidateFunc: validateIntInWord([]string{"256", "512"}), + }, + "endpoint": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "ports": &schema.Schema{ + Type: schema.TypeList, + Required: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "protocol": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "tcp", + ValidateFunc: validateStringInWord([]string{"tcp", "udp"}), + }, + "number": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: "80", + ValidateFunc: validateIntegerInRange(1, 65535), + }, + }, + }, + }, + "environments": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "value": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "cmd": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "port_mappings": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "ipaddress": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "container_port": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + "service_port": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "endpoint_full_hostname": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "endpoint_full_url": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "app_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceArukasContainerCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArukasClient) + + var appSet API.AppSet + + // create an app + newApp := API.App{Name: d.Get("name").(string)} + + var parsedEnvs API.Envs + var parsedPorts API.Ports + + if rawEnvs, ok := d.GetOk("environments"); ok { + parsedEnvs = expandEnvs(rawEnvs) + } + if rawPorts, ok := d.GetOk("ports"); ok { + parsedPorts = expandPorts(rawPorts) + } + + newContainer := API.Container{ + Envs: parsedEnvs, + Ports: parsedPorts, + ImageName: d.Get("image").(string), + Mem: d.Get("memory").(int), + Instances: d.Get("instances").(int), + Cmd: d.Get("cmd").(string), + + Name: d.Get("endpoint").(string), + } + newAppSet := API.AppSet{ + App: newApp, + Container: newContainer, + } + + // create + if err := client.Post(&appSet, "/app-sets", newAppSet); err != nil { + return err + } + + // start container + if err := client.Post(nil, fmt.Sprintf("/containers/%s/power", appSet.Container.ID), nil); err != nil { + return err + } + + if err := sleepUntilUp(client, appSet.Container.ID, client.Timeout); err != nil { + return err + } + + d.SetId(appSet.Container.ID) + return resourceArukasContainerRead(d, meta) +} + +func resourceArukasContainerRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArukasClient) + + var container API.Container + var app API.App + + if err := client.Get(&container, fmt.Sprintf("/containers/%s", d.Id())); err != nil { + return err + } + if err := client.Get(&app, fmt.Sprintf("/apps/%s", container.AppID)); err != nil { + return err + } + + d.Set("app_id", container.AppID) + d.Set("name", app.Name) + d.Set("image", container.ImageName) + d.Set("instances", container.Instances) + d.Set("memory", container.Mem) + endpoint := container.Endpoint + if strings.HasSuffix(endpoint, ".arukascloud.io") { + endpoint = strings.Replace(endpoint, ".arukascloud.io", "", -1) + } + + d.Set("endpoint", endpoint) + d.Set("endpoint_full_hostname", container.Endpoint) + d.Set("endpoint_full_url", fmt.Sprintf("https://%s", container.Endpoint)) + + d.Set("cmd", container.Cmd) + + //ports + d.Set("ports", flattenPorts(container.Ports)) + + //port mappings + d.Set("port_mappings", flattenPortMappings(container.PortMappings)) + + //envs + d.Set("environments", flattenEnvs(container.Envs)) + + return nil +} + +func resourceArukasContainerUpdate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*ArukasClient) + var container API.Container + + if err := client.Get(&container, fmt.Sprintf("/containers/%s", d.Id())); err != nil { + return err + } + + var parsedEnvs API.Envs + var parsedPorts API.Ports + + if rawEnvs, ok := d.GetOk("environments"); ok { + parsedEnvs = expandEnvs(rawEnvs) + } + if rawPorts, ok := d.GetOk("ports"); ok { + parsedPorts = expandPorts(rawPorts) + } + + newContainer := API.Container{ + Envs: parsedEnvs, + Ports: parsedPorts, + ImageName: d.Get("image").(string), + Mem: d.Get("memory").(int), + Instances: d.Get("instances").(int), + Cmd: d.Get("cmd").(string), + Name: d.Get("endpoint").(string), + } + + // update + if err := client.Patch(nil, fmt.Sprintf("/containers/%s", d.Id()), newContainer); err != nil { + return err + } + + return resourceArukasContainerRead(d, meta) + +} + +func resourceArukasContainerDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArukasClient) + var container API.Container + + if err := client.Get(&container, fmt.Sprintf("/containers/%s", d.Id())); err != nil { + return err + } + + if err := client.Delete(fmt.Sprintf("/apps/%s", container.AppID)); err != nil { + return err + } + + return nil +} + +func sleepUntilUp(client *ArukasClient, containerID string, timeout time.Duration) error { + current := 0 * time.Second + interval := 5 * time.Second + for { + var container API.Container + if err := client.Get(&container, fmt.Sprintf("/containers/%s", containerID)); err != nil { + return err + } + + if container.IsRunning { + return nil + } + time.Sleep(interval) + current += interval + + if timeout > 0 && current > timeout { + return fmt.Errorf("Timeout: sleepUntilUp") + } + } +} diff --git a/builtin/providers/arukas/resource_arukas_container_test.go b/builtin/providers/arukas/resource_arukas_container_test.go new file mode 100644 index 000000000..88b69f2d8 --- /dev/null +++ b/builtin/providers/arukas/resource_arukas_container_test.go @@ -0,0 +1,279 @@ +package arukas + +import ( + "fmt" + API "github.com/arukasio/cli" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "testing" +) + +func TestAccArukasContainer_Basic(t *testing.T) { + var container API.Container + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckArukasContainerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckArukasContainerConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckArukasContainerExists("arukas_container.foobar", &container), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "name", "terraform_for_arukas_test_foobar"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "image", "nginx:latest"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "instances", "1"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "memory", "256"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "endpoint", "terraform-for-arukas-test-endpoint"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.#", "1"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.0.protocol", "tcp"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.0.number", "80"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "environments.#", "1"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "environments.0.key", "key"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "environments.0.value", "value"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "port_mappings.#", "1"), + ), + }, + }, + }) +} + +func TestAccArukasContainer_Update(t *testing.T) { + var container API.Container + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckArukasContainerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckArukasContainerConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckArukasContainerExists("arukas_container.foobar", &container), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "name", "terraform_for_arukas_test_foobar"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "image", "nginx:latest"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "instances", "1"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "memory", "256"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "endpoint", "terraform-for-arukas-test-endpoint"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.#", "1"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.0.protocol", "tcp"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.0.number", "80"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "environments.#", "1"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "environments.0.key", "key"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "environments.0.value", "value"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "port_mappings.#", "1"), + ), + }, + resource.TestStep{ + Config: testAccCheckArukasContainerConfig_update, + Check: resource.ComposeTestCheckFunc( + testAccCheckArukasContainerExists("arukas_container.foobar", &container), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "name", "terraform_for_arukas_test_foobar_upd"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "image", "nginx:latest"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "instances", "2"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "memory", "512"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "endpoint", "terraform-for-arukas-test-endpoint-upd"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.#", "2"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.0.protocol", "tcp"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.0.number", "80"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.1.protocol", "tcp"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.1.number", "443"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "environments.#", "2"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "environments.0.key", "key"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "environments.0.value", "value"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "environments.1.key", "key_upd"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "environments.1.value", "value_upd"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "port_mappings.#", "4"), + ), + }, + }, + }) +} + +func TestAccArukasContainer_Minimum(t *testing.T) { + var container API.Container + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckArukasContainerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckArukasContainerConfig_minimum, + Check: resource.ComposeTestCheckFunc( + testAccCheckArukasContainerExists("arukas_container.foobar", &container), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "name", "terraform_for_arukas_test_foobar"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "image", "nginx:latest"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "instances", "1"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "memory", "256"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.#", "1"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.0.protocol", "tcp"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "ports.0.number", "80"), + resource.TestCheckResourceAttr( + "arukas_container.foobar", "port_mappings.#", "1"), + ), + }, + }, + }) +} + +func TestAccArukasContainer_Import(t *testing.T) { + resourceName := "arukas_container.foobar" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckArukasContainerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckArukasContainerConfig_basic, + }, + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckArukasContainerExists(n string, container *API.Container) 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 Container ID is set") + } + client := testAccProvider.Meta().(*ArukasClient) + var foundContainer API.Container + err := client.Get(&foundContainer, fmt.Sprintf("/containers/%s", rs.Primary.ID)) + + if err != nil { + return err + } + + if foundContainer.ID != rs.Primary.ID { + return fmt.Errorf("Container not found") + } + + *container = foundContainer + + return nil + } +} + +func testAccCheckArukasContainerDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArukasClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "arukas_container" { + continue + } + + err := client.Get(nil, fmt.Sprintf("/containers/%s", rs.Primary.ID)) + + if err == nil { + return fmt.Errorf("Note still exists") + } + } + + return nil +} + +const testAccCheckArukasContainerConfig_basic = ` +resource "arukas_container" "foobar" { + name = "terraform_for_arukas_test_foobar" + image = "nginx:latest" + instances = 1 + memory = 256 + endpoint = "terraform-for-arukas-test-endpoint" + ports = { + protocol = "tcp" + number = "80" + } + environments { + key = "key" + value = "value" + } +}` + +const testAccCheckArukasContainerConfig_update = ` +resource "arukas_container" "foobar" { + name = "terraform_for_arukas_test_foobar_upd" + image = "nginx:latest" + instances = 2 + memory = 512 + endpoint = "terraform-for-arukas-test-endpoint-upd" + ports = { + protocol = "tcp" + number = "80" + } + ports = { + protocol = "tcp" + number = "443" + } + environments { + key = "key" + value = "value" + } + environments { + key = "key_upd" + value = "value_upd" + } +}` + +const testAccCheckArukasContainerConfig_minimum = ` +resource "arukas_container" "foobar" { + name = "terraform_for_arukas_test_foobar" + image = "nginx:latest" + ports = { + number = "80" + } +}` diff --git a/builtin/providers/arukas/structure.go b/builtin/providers/arukas/structure.go new file mode 100644 index 000000000..155de034c --- /dev/null +++ b/builtin/providers/arukas/structure.go @@ -0,0 +1,110 @@ +package arukas + +import ( + API "github.com/arukasio/cli" + "github.com/hashicorp/terraform/helper/schema" + "net" +) + +// Takes the result of flatmap.Expand for an array of strings +// and returns a []string +func expandStringList(configured []interface{}) []string { + vs := make([]string, 0, len(configured)) + for _, v := range configured { + vs = append(vs, string(v.(string))) + } + return vs +} + +// Takes the result of schema.Set of strings and returns a []string +func expandStringSet(configured *schema.Set) []string { + return expandStringList(configured.List()) +} + +// Takes list of pointers to strings. Expand to an array +// of raw strings and returns a []interface{} +// to keep compatibility w/ schema.NewSetschema.NewSet +func flattenStringList(list []string) []interface{} { + vs := make([]interface{}, 0, len(list)) + for _, v := range list { + vs = append(vs, v) + } + return vs +} + +func expandEnvs(configured interface{}) API.Envs { + var envs API.Envs + if configured == nil { + return envs + } + rawEnvs := configured.([]interface{}) + for _, raw := range rawEnvs { + env := raw.(map[string]interface{}) + envs = append(envs, API.Env{Key: env["key"].(string), Value: env["value"].(string)}) + } + return envs +} + +func flattenEnvs(envs API.Envs) []interface{} { + var ret []interface{} + for _, env := range envs { + r := map[string]interface{}{} + r["key"] = env.Key + r["value"] = env.Value + ret = append(ret, r) + } + return ret +} + +func expandPorts(configured interface{}) API.Ports { + var ports API.Ports + if configured == nil { + return ports + } + rawPorts := configured.([]interface{}) + for _, raw := range rawPorts { + port := raw.(map[string]interface{}) + ports = append(ports, API.Port{Protocol: port["protocol"].(string), Number: port["number"].(int)}) + } + return ports +} + +func flattenPorts(ports API.Ports) []interface{} { + var ret []interface{} + for _, port := range ports { + r := map[string]interface{}{} + r["protocol"] = port.Protocol + r["number"] = port.Number + ret = append(ret, r) + } + return ret +} +func flattenPortMappings(ports API.PortMappings) []interface{} { + var ret []interface{} + for _, tasks := range ports { + for _, port := range tasks { + r := map[string]interface{}{} + ip := "" + + addrs, err := net.LookupHost(port.Host) + if err == nil && len(addrs) > 0 { + ip = addrs[0] + } + + r["host"] = port.Host + r["ipaddress"] = ip + r["container_port"] = port.ContainerPort + r["service_port"] = port.ServicePort + ret = append(ret, r) + } + } + return ret +} + +func forceString(target interface{}) string { + if target == nil { + return "" + } + + return target.(string) +} diff --git a/builtin/providers/arukas/validators.go b/builtin/providers/arukas/validators.go new file mode 100644 index 000000000..e1c6a866c --- /dev/null +++ b/builtin/providers/arukas/validators.go @@ -0,0 +1,92 @@ +package arukas + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func validateMaxLength(minLength, maxLength int) schema.SchemaValidateFunc { + return func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < minLength { + errors = append(errors, fmt.Errorf( + "%q cannot be shorter than %d characters: %q", k, minLength, value)) + } + if len(value) > maxLength { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than %d characters: %q", k, maxLength, value)) + } + return + } +} + +func validateIntegerInRange(min, max int) schema.SchemaValidateFunc { + return func(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < min { + errors = append(errors, fmt.Errorf( + "%q cannot be lower than %d: %d", k, min, value)) + } + if value > max { + errors = append(errors, fmt.Errorf( + "%q cannot be higher than %d: %d", k, max, value)) + } + return + } +} + +func validateStringInWord(allowWords []string) schema.SchemaValidateFunc { + return func(v interface{}, k string) (ws []string, errors []error) { + var found bool + for _, t := range allowWords { + if v.(string) == t { + found = true + } + } + if !found { + errors = append(errors, fmt.Errorf("%q must be one of [%s]", k, strings.Join(allowWords, "/"))) + + } + return + } +} + +func validateIntInWord(allowWords []string) schema.SchemaValidateFunc { + return func(v interface{}, k string) (ws []string, errors []error) { + var found bool + for _, t := range allowWords { + if fmt.Sprintf("%d", v.(int)) == t { + found = true + } + } + if !found { + errors = append(errors, fmt.Errorf("%q must be one of [%s]", k, strings.Join(allowWords, "/"))) + + } + return + } +} + +func validateDNSRecordValue() schema.SchemaValidateFunc { + return func(v interface{}, k string) (ws []string, errors []error) { + var rtype, value string + + values := v.(map[string]interface{}) + rtype = values["type"].(string) + value = values["value"].(string) + switch rtype { + case "MX", "NS", "CNAME": + if rtype == "MX" { + if values["priority"] == nil { + errors = append(errors, fmt.Errorf("%q required when TYPE was MX", k)) + } + } + if !strings.HasSuffix(value, ".") { + errors = append(errors, fmt.Errorf("%q must be period at the end [%s]", k, value)) + } + } + return + } + +} diff --git a/command/internal_plugin_list.go b/command/internal_plugin_list.go index dab3e3cbc..ba4a23616 100644 --- a/command/internal_plugin_list.go +++ b/command/internal_plugin_list.go @@ -8,6 +8,7 @@ package command import ( alicloudprovider "github.com/hashicorp/terraform/builtin/providers/alicloud" archiveprovider "github.com/hashicorp/terraform/builtin/providers/archive" + arukasprovider "github.com/hashicorp/terraform/builtin/providers/arukas" atlasprovider "github.com/hashicorp/terraform/builtin/providers/atlas" awsprovider "github.com/hashicorp/terraform/builtin/providers/aws" azureprovider "github.com/hashicorp/terraform/builtin/providers/azure" @@ -79,6 +80,7 @@ import ( var InternalProviders = map[string]plugin.ProviderFunc{ "alicloud": alicloudprovider.Provider, "archive": archiveprovider.Provider, + "arukas": arukasprovider.Provider, "atlas": atlasprovider.Provider, "aws": awsprovider.Provider, "azure": azureprovider.Provider, diff --git a/vendor/github.com/arukasio/cli/BUILDING.md b/vendor/github.com/arukasio/cli/BUILDING.md new file mode 100644 index 000000000..c3e9ae96e --- /dev/null +++ b/vendor/github.com/arukasio/cli/BUILDING.md @@ -0,0 +1,61 @@ +# Building Arukas CLI + +This document contains details about the process for building binaries for Arukas CLI + +## QuickBuild + +**Please note: Replaced by your arukas token and aruaks api secret is + `YOUR_API_TOKEN` and `YOUR_API_SECRET`** + +* Clone the repo: `git clone https://github.com/arukasio/cli.git` +* CLI Build: `docker build -t arukasio/arukas:patch .` +* Test execute the CLI: `docker run --rm -e ARUKAS_JSON_API_TOKEN="YOUR_API_TOKEN" +-e ARUKAS_JSON_API_SECRET="YOUR_API_SECRET" arukasio/arukas:patch` + +### Godep + +You can use the `godep` in order to install the external package that depends. +It will install the package versions specified in `Godeps/Godeps.json` to your `$GOPATH` + +``` +go get -u github.com/tools/godep +godep restore +``` + +## Cross Compilation and Building for Distribution + +If you wish to cross-compile arukas-cli for another architecture, you can set the `XC_OS` and `XC_ARCH` environment variables to values representing the target operating system and architecture before calling `make`. The output is placed in the `pkg` subdirectory tree both expanded in a directory representing the OS/architecture combination and as a ZIP archive. + +For example, to compile 64-bit Linux binaries on Mac OS X Linux, you can run: + +```sh +$ XC_OS=linux XC_ARCH=amd64 make bin +... +$ file pkg/linux_amd64/arukas +arukas: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped +``` + +`XC_OS` and `XC_ARCH` can be space separated lists representing different combinations of operating system and architecture. For example, to compile for both Linux and Mac OS X, targeting both 32- and 64-bit architectures, you can run: + +```sh +$ XC_OS="linux darwin" XC_ARCH="386 amd64" make bin +... +$ tree ./pkg/ -P "arukas|*.zip" +./pkg/ +├── darwin_386 +│ └── arukas +├── darwin_386.zip +├── darwin_amd64 +│ └── arukas +├── darwin_amd64.zip +├── linux_386 +│ └── arukas +├── linux_386.zip +├── linux_amd64 +│ └── arukas +└── linux_amd64.zip + +4 directories, 8 files +``` + +_Note: Cross-compilation uses [gox](https://github.com/mitchellh/gox), which requires toolchains to be built with versions of Go prior to 1.5. In order to successfully cross-compile with older versions of Go, you will need to run `gox -build-toolchain` before running the commands detailed above._ diff --git a/vendor/github.com/arukasio/cli/Dockerfile b/vendor/github.com/arukasio/cli/Dockerfile new file mode 100644 index 000000000..5e3451ab2 --- /dev/null +++ b/vendor/github.com/arukasio/cli/Dockerfile @@ -0,0 +1,15 @@ +FROM arukasio/arukas:dev +MAINTAINER "Shuji Yamada " + +ENV REPO_ROOT $GOPATH/src/github.com/arukasio/cli + +COPY . $REPO_ROOT +WORKDIR $REPO_ROOT + +RUN godep restore +RUN for package in $(go list ./...| grep -v vendor); do golint ${package}; done +RUN ARUKAS_DEV=1 scripts/build.sh + +WORKDIR $GOPATH + +ENTRYPOINT ["bin/arukas"] diff --git a/vendor/github.com/arukasio/cli/LICENSE.txt b/vendor/github.com/arukasio/cli/LICENSE.txt new file mode 100644 index 000000000..d4e94d7cc --- /dev/null +++ b/vendor/github.com/arukasio/cli/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (C) 2015 Arukas +All Rights Reserved. + +MIT LICENSE + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/arukasio/cli/Makefile b/vendor/github.com/arukasio/cli/Makefile new file mode 100644 index 000000000..e105a7fd3 --- /dev/null +++ b/vendor/github.com/arukasio/cli/Makefile @@ -0,0 +1,71 @@ +TEST?=$$(go list ./... | grep -v /vendor/) +VETARGS?=-all +GOFMT_FILES?=$$(find . -name '*.go' | grep -v vendor) + +default: vet + +# bin generates the releaseable binaries for Arukas +bin: fmtcheck generate + @sh -c "'$(CURDIR)/scripts/build.sh'" + +# dev creates binaries for testing Arukas locally. These are put +# into ./bin/ as well as $GOPATH/bin +dev: fmtcheck generate + @ARUKAS_DEV=1 sh -c "'$(CURDIR)/scripts/build.sh'" + +quickdev: generate + @ARUKAS_QUICKDEV=1 ARUKAS_DEV=1 sh -c "'$(CURDIR)/scripts/build.sh'" + +# Shorthand for quickly building the core of Arukas. Note that some +# changes will require a rebuild of everything, in which case the dev +# target should be used. +core-dev: fmtcheck generate + go install github.com/arukasio/cli + +# Shorthand for quickly testing the core of Arukas (i.e. "not providers") +core-test: generate + @echo "Testing core packages..." && go test $(shell go list ./... | grep -v -E 'builtin|vendor') + +# Shorthand for building and installing just one plugin for local testing. +# Run as (for example): make plugin-dev PLUGIN=provider-aws +plugin-dev: fmtcheck generate + go install github.com/hashicorp/terraform/builtin/bins/$(PLUGIN) + mv $(GOPATH)/bin/$(PLUGIN) $(GOPATH)/bin/terraform-$(PLUGIN) + +# test runs the unit tests +test: fmtcheck generate + ARUKAS_ACC= go test $(TEST) $(TESTARGS) -timeout=30s -parallel=4 + +# testrace runs the race checker +testrace: fmtcheck generate + ARUKAS_ACC= go test -race $(TEST) $(TESTARGS) + +# vet runs the Go source code static analysis tool `vet` to find +# any common errors. +vet: + @go tool vet 2>/dev/null ; if [ $$? -eq 3 ]; then \ + go get golang.org/x/tools/cmd/vet; \ + fi + @echo "go tool vet $(VETARGS) ." + @go tool vet $(VETARGS) $$(ls -d */ | grep -v vendor) ; if [ $$? -eq 1 ]; then \ + echo ""; \ + echo "Vet found suspicious constructs. Please check the reported constructs"; \ + echo "and fix them if necessary before submitting the code for review."; \ + exit 1; \ + fi + +# generate runs `go generate` to build the dynamically generated +# source files. +generate: + @which stringer ; if [ $$? -ne 0 ]; then \ + go get -u golang.org/x/tools/cmd/stringer; \ + fi + go generate $$(go list ./... | grep -v /vendor/) + +fmt: + gofmt -w $(GOFMT_FILES) + +fmtcheck: + @sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'" + +.PHONY: bin default generate test updatedeps vet fmt fmtcheck diff --git a/vendor/github.com/arukasio/cli/README.md b/vendor/github.com/arukasio/cli/README.md new file mode 100644 index 000000000..b68ae11b0 --- /dev/null +++ b/vendor/github.com/arukasio/cli/README.md @@ -0,0 +1,37 @@ + Arukas CLI +========== + +[![Circle CI](https://circleci.com/gh/arukasio/cli.svg?style=shield)](https://circleci.com/gh/arukasio/cli) + +The Arukas CLI is used to manage Arukas apps from the command line. +* Website: https://arukas.io + +### Binary Releases + +The official binary of Arukas CLI: https://github.com/arukasio/cli/releases/ + +### Dockerized + +A dockerized version of Arukas CLI: https://hub.docker.com/r/arukasio/arukas/ + +## Setup + +* Get API key here: https://app.arukas.io/settings/api-keys +* Edit it `.env` file + +You can overload and customize specific variables when running scripts. + +Simply create `.env` with the environment variables you need, +for example, `ARUKAS_JSON_API_TOKEN` and `ARUKAS_JSON_API_SECRET` + +``` +# .env +ARUKAS_JSON_API_TOKEN=YOUR_API_TOKEN +ARUKAS_JSON_API_SECRET=YOUR_API_SECRET +``` + +You can look at `.env.sample` for other variables used by this application. + +## License + +This project is licensed under the terms of the MIT license. diff --git a/vendor/github.com/arukasio/cli/Vagrantfile b/vendor/github.com/arukasio/cli/Vagrantfile new file mode 100644 index 000000000..de3b284b9 --- /dev/null +++ b/vendor/github.com/arukasio/cli/Vagrantfile @@ -0,0 +1,65 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! +VAGRANTFILE_API_VERSION = "2" + +$script = <