diff --git a/builtin/providers/alicloud/common.go b/builtin/providers/alicloud/common.go new file mode 100644 index 0000000000..24c3647db7 --- /dev/null +++ b/builtin/providers/alicloud/common.go @@ -0,0 +1,52 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/hashicorp/terraform/helper/schema" +) + +type InstanceNetWork string + +const ( + ClassicNet = InstanceNetWork("classic") + VpcNet = InstanceNetWork("vpc") +) + +const defaultTimeout = 120 + +func getRegion(d *schema.ResourceData, meta interface{}) common.Region { + return meta.(*AliyunClient).Region +} + +func notFoundError(err error) bool { + if e, ok := err.(*common.Error); ok && (e.StatusCode == 404 || e.ErrorResponse.Message == "Not found") { + return true + } + + return false +} + +// Protocal represents network protocal +type Protocal string + +// Constants of protocal definition +const ( + Http = Protocal("http") + Https = Protocal("https") + Tcp = Protocal("tcp") + Udp = Protocal("udp") +) + +// ValidProtocals network protocal list +var ValidProtocals = []Protocal{Http, Https, Tcp, Udp} + +// simple array value check method, support string type only +func isProtocalValid(value string) bool { + res := false + for _, v := range ValidProtocals { + if string(v) == value { + res = true + } + } + return res +} diff --git a/builtin/providers/alicloud/config.go b/builtin/providers/alicloud/config.go new file mode 100644 index 0000000000..352e2e21c1 --- /dev/null +++ b/builtin/providers/alicloud/config.go @@ -0,0 +1,103 @@ +package alicloud + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/denverdino/aliyungo/slb" +) + +// Config of aliyun +type Config struct { + AccessKey string + SecretKey string + Region common.Region +} + +// AliyunClient of aliyun +type AliyunClient struct { + Region common.Region + ecsconn *ecs.Client + vpcconn *ecs.Client + slbconn *slb.Client +} + +// Client for AliyunClient +func (c *Config) Client() (*AliyunClient, error) { + err := c.loadAndValidate() + if err != nil { + return nil, err + } + + ecsconn, err := c.ecsConn() + if err != nil { + return nil, err + } + + slbconn, err := c.slbConn() + if err != nil { + return nil, err + } + + vpcconn, err := c.vpcConn() + if err != nil { + return nil, err + } + + return &AliyunClient{ + Region: c.Region, + ecsconn: ecsconn, + vpcconn: vpcconn, + slbconn: slbconn, + }, nil +} + +func (c *Config) loadAndValidate() error { + err := c.validateRegion() + if err != nil { + return err + } + + return nil +} + +func (c *Config) validateRegion() error { + + for _, valid := range common.ValidRegions { + if c.Region == valid { + return nil + } + } + + return fmt.Errorf("Not a valid region: %s", c.Region) +} + +func (c *Config) ecsConn() (*ecs.Client, error) { + client := ecs.NewClient(c.AccessKey, c.SecretKey) + _, err := client.DescribeRegions() + + if err != nil { + return nil, err + } + + return client, nil +} + +func (c *Config) slbConn() (*slb.Client, error) { + client := slb.NewClient(c.AccessKey, c.SecretKey) + + return client, nil +} + +func (c *Config) vpcConn() (*ecs.Client, error) { + _, err := c.ecsConn() + + if err != nil { + return nil, err + } + + client := &ecs.Client{} + client.Init("https://vpc.aliyuncs.com/", "2016-04-28", c.AccessKey, c.SecretKey) + return client, nil +} diff --git a/builtin/providers/alicloud/data_source_alicloud_common.go b/builtin/providers/alicloud/data_source_alicloud_common.go new file mode 100644 index 0000000000..1da432cfc9 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_common.go @@ -0,0 +1,18 @@ +package alicloud + +import ( + "bytes" + "fmt" + "github.com/hashicorp/terraform/helper/hashcode" +) + +// Generates a hash for the set hash function used by the ID +func dataResourceIdHash(ids []string) string { + var buf bytes.Buffer + + for _, id := range ids { + buf.WriteString(fmt.Sprintf("%s-", id)) + } + + return fmt.Sprintf("%d", hashcode.String(buf.String())) +} diff --git a/builtin/providers/alicloud/data_source_alicloud_images.go b/builtin/providers/alicloud/data_source_alicloud_images.go new file mode 100644 index 0000000000..ae5b660693 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_images.go @@ -0,0 +1,324 @@ +package alicloud + +import ( + "fmt" + "log" + "regexp" + "sort" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func dataSourceAlicloudImages() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAlicloudImagesRead, + + Schema: map[string]*schema.Schema{ + "name_regex": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateNameRegex, + }, + "most_recent": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, + "owners": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateImageOwners, + }, + // Computed values. + "images": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "image_id": { + Type: schema.TypeString, + Computed: true, + }, + "architecture": { + Type: schema.TypeString, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "image_owner_alias": { + Type: schema.TypeString, + Computed: true, + }, + "os_type": { + Type: schema.TypeString, + Computed: true, + }, + "os_name": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "platform": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "size": { + Type: schema.TypeInt, + Computed: true, + }, + // Complex computed values + "disk_device_mappings": { + Type: schema.TypeList, + Computed: true, + //Set: imageDiskDeviceMappingHash, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "device": { + Type: schema.TypeString, + Computed: true, + }, + "size": { + Type: schema.TypeString, + Computed: true, + }, + "snapshot_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "product_code": { + Type: schema.TypeString, + Computed: true, + }, + "is_self_shared": { + Type: schema.TypeString, + Computed: true, + }, + "is_subscribed": { + Type: schema.TypeBool, + Computed: true, + }, + "is_copied": { + Type: schema.TypeBool, + Computed: true, + }, + "is_support_io_optimized": { + Type: schema.TypeBool, + Computed: true, + }, + "image_version": { + Type: schema.TypeString, + Computed: true, + }, + "progress": { + Type: schema.TypeString, + Computed: true, + }, + "usage": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tagsSchema(), + }, + }, + }, + }, + } +} + +// dataSourceAlicloudImagesDescriptionRead performs the Alicloud Image lookup. +func dataSourceAlicloudImagesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + nameRegex, nameRegexOk := d.GetOk("name_regex") + owners, ownersOk := d.GetOk("owners") + mostRecent, mostRecentOk := d.GetOk("most_recent") + + if nameRegexOk == false && ownersOk == false && mostRecentOk == false { + return fmt.Errorf("One of name_regex, owners or most_recent must be assigned") + } + + params := &ecs.DescribeImagesArgs{ + RegionId: getRegion(d, meta), + } + + if ownersOk { + params.ImageOwnerAlias = ecs.ImageOwnerAlias(owners.(string)) + } + + resp, _, err := conn.DescribeImages(params) + if err != nil { + return err + } + + var filteredImages []ecs.ImageType + if nameRegexOk { + r := regexp.MustCompile(nameRegex.(string)) + for _, image := range resp { + // Check for a very rare case where the response would include no + // image name. No name means nothing to attempt a match against, + // therefore we are skipping such image. + if image.ImageName == "" { + log.Printf("[WARN] Unable to find Image name to match against "+ + "for image ID %q, nothing to do.", + image.ImageId) + continue + } + if r.MatchString(image.ImageName) { + filteredImages = append(filteredImages, image) + } + } + } else { + filteredImages = resp[:] + } + + var images []ecs.ImageType + if len(filteredImages) < 1 { + return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") + } + + log.Printf("[DEBUG] alicloud_image - multiple results found and `most_recent` is set to: %t", mostRecent.(bool)) + if len(filteredImages) > 1 && mostRecent.(bool) { + // Query returned single result. + images = append(images, mostRecentImage(filteredImages)) + } else { + images = filteredImages + } + + log.Printf("[DEBUG] alicloud_image - Images found: %#v", images) + return imagesDescriptionAttributes(d, images, meta) +} + +// populate the numerous fields that the image description returns. +func imagesDescriptionAttributes(d *schema.ResourceData, images []ecs.ImageType, meta interface{}) error { + var ids []string + var s []map[string]interface{} + for _, image := range images { + mapping := map[string]interface{}{ + "id": image.ImageId, + "architecture": image.Architecture, + "creation_time": image.CreationTime.String(), + "description": image.Description, + "image_id": image.ImageId, + "image_owner_alias": image.ImageOwnerAlias, + "os_name": image.OSName, + "os_type": image.OSType, + "name": image.ImageName, + "platform": image.Platform, + "status": image.Status, + "state": image.Status, + "size": image.Size, + "is_self_shared": image.IsSelfShared, + "is_subscribed": image.IsSubscribed, + "is_copied": image.IsCopied, + "is_support_io_optimized": image.IsSupportIoOptimized, + "image_version": image.ImageVersion, + "progress": image.Progress, + "usage": image.Usage, + "product_code": image.ProductCode, + + // Complex types get their own functions + "disk_device_mappings": imageDiskDeviceMappings(image.DiskDeviceMappings.DiskDeviceMapping), + "tags": imageTagsMappings(d, image.ImageId, meta), + } + + log.Printf("[DEBUG] alicloud_image - adding image mapping: %v", mapping) + ids = append(ids, image.ImageId) + s = append(s, mapping) + } + + d.SetId(dataResourceIdHash(ids)) + if err := d.Set("images", s); err != nil { + return err + } + return nil +} + +//Find most recent image +type imageSort []ecs.ImageType + +func (a imageSort) Len() int { + return len(a) +} +func (a imageSort) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} +func (a imageSort) Less(i, j int) bool { + itime, _ := time.Parse(time.RFC3339, a[i].CreationTime.String()) + jtime, _ := time.Parse(time.RFC3339, a[j].CreationTime.String()) + return itime.Unix() < jtime.Unix() +} + +// Returns the most recent Image out of a slice of images. +func mostRecentImage(images []ecs.ImageType) ecs.ImageType { + sortedImages := images + sort.Sort(imageSort(sortedImages)) + return sortedImages[len(sortedImages)-1] +} + +// Returns a set of disk device mappings. +func imageDiskDeviceMappings(m []ecs.DiskDeviceMapping) []map[string]interface{} { + var s []map[string]interface{} + + for _, v := range m { + mapping := map[string]interface{}{ + "device": v.Device, + "size": v.Size, + "snapshot_id": v.SnapshotId, + } + + log.Printf("[DEBUG] alicloud_image - adding disk device mapping: %v", mapping) + s = append(s, mapping) + } + + return s +} + +//Returns a mapping of image tags +func imageTagsMappings(d *schema.ResourceData, imageId string, meta interface{}) map[string]string { + client := meta.(*AliyunClient) + conn := client.ecsconn + + tags, _, err := conn.DescribeTags(&ecs.DescribeTagsArgs{ + RegionId: getRegion(d, meta), + ResourceType: ecs.TagResourceImage, + ResourceId: imageId, + }) + + if err != nil { + log.Printf("[ERROR] DescribeTags for image got error: %#v", err) + return nil + } + + log.Printf("[DEBUG] DescribeTags for image : %v", tags) + return tagsToMap(tags) +} diff --git a/builtin/providers/alicloud/data_source_alicloud_images_test.go b/builtin/providers/alicloud/data_source_alicloud_images_test.go new file mode 100644 index 0000000000..7512c6e916 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_images_test.go @@ -0,0 +1,130 @@ +package alicloud + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAlicloudImagesDataSource_images(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudImagesDataSourceImagesConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_images.multi_image"), + + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.#", "2"), + + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.architecture", "x86_64"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.disk_device_mappings.#", "0"), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.0.creation_time", regexp.MustCompile("^20[0-9]{2}-")), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.0.image_id", regexp.MustCompile("^centos_6\\w{1,5}[64]{1}.")), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.image_owner_alias", "system"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.os_type", "linux"), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.0.name", regexp.MustCompile("^centos_6[a-zA-Z0-9_]{1,5}[64]{1}.")), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.progress", "100%"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.state", "Available"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.status", "Available"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.usage", "instance"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.tags.%", "0"), + + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.architecture", "i386"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.disk_device_mappings.#", "0"), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.1.creation_time", regexp.MustCompile("^20[0-9]{2}-")), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.1.image_id", regexp.MustCompile("^centos_6[a-zA-Z0-9_]{1,5}[32]{1}.")), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.image_owner_alias", "system"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.os_type", "linux"), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.1.name", regexp.MustCompile("^centos_6\\w{1,5}[32]{1}.")), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.progress", "100%"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.state", "Available"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.status", "Available"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.usage", "instance"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.tags.%", "0"), + ), + }, + }, + }) +} + +func TestAccAlicloudImagesDataSource_owners(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudImagesDataSourceOwnersConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_images.owners_filtered_image"), + ), + }, + }, + }) +} + +func TestAccAlicloudImagesDataSource_ownersEmpty(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudImagesDataSourceEmptyOwnersConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_images.empty_owners_filtered_image"), + resource.TestCheckResourceAttr("data.alicloud_images.empty_owners_filtered_image", "most_recent", "true"), + ), + }, + }, + }) +} + +func TestAccAlicloudImagesDataSource_nameRegexFilter(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudImagesDataSourceNameRegexConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_images.name_regex_filtered_image"), + resource.TestMatchResourceAttr("data.alicloud_images.name_regex_filtered_image", "images.0.image_id", regexp.MustCompile("^centos_")), + ), + }, + }, + }) +} + +// Instance store test - using centos images +const testAccCheckAlicloudImagesDataSourceImagesConfig = ` +data "alicloud_images" "multi_image" { + owners = "system" + name_regex = "^centos_6" +} +` + +// Testing owner parameter +const testAccCheckAlicloudImagesDataSourceOwnersConfig = ` +data "alicloud_images" "owners_filtered_image" { + most_recent = true + owners = "system" +} +` + +const testAccCheckAlicloudImagesDataSourceEmptyOwnersConfig = ` +data "alicloud_images" "empty_owners_filtered_image" { + most_recent = true + owners = "" +} +` + +// Testing name_regex parameter +const testAccCheckAlicloudImagesDataSourceNameRegexConfig = ` +data "alicloud_images" "name_regex_filtered_image" { + most_recent = true + owners = "system" + name_regex = "^centos_6\\w{1,5}[64]{1}.*" +} +` diff --git a/builtin/providers/alicloud/data_source_alicloud_instance_types.go b/builtin/providers/alicloud/data_source_alicloud_instance_types.go new file mode 100644 index 0000000000..87d6bfc432 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_instance_types.go @@ -0,0 +1,127 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "log" +) + +func dataSourceAlicloudInstanceTypes() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAlicloudInstanceTypesRead, + + Schema: map[string]*schema.Schema{ + "instance_type_family": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "cpu_core_count": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "memory_size": { + Type: schema.TypeFloat, + Optional: true, + ForceNew: true, + }, + // Computed values. + "instance_types": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "cpu_core_count": { + Type: schema.TypeInt, + Computed: true, + }, + "memory_size": { + Type: schema.TypeFloat, + Computed: true, + }, + "family": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceAlicloudInstanceTypesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + cpu, _ := d.Get("cpu_core_count").(int) + mem, _ := d.Get("memory_size").(float64) + + args, err := buildAliyunAlicloudInstanceTypesArgs(d, meta) + + if err != nil { + return err + } + + resp, err := conn.DescribeInstanceTypesNew(args) + if err != nil { + return err + } + + var instanceTypes []ecs.InstanceTypeItemType + for _, types := range resp { + if cpu > 0 && types.CpuCoreCount != cpu { + continue + } + + if mem > 0 && types.MemorySize != mem { + continue + } + instanceTypes = append(instanceTypes, types) + } + + if len(instanceTypes) < 1 { + return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") + } + + log.Printf("[DEBUG] alicloud_instance_type - Types found: %#v", instanceTypes) + return instanceTypesDescriptionAttributes(d, instanceTypes) +} + +func instanceTypesDescriptionAttributes(d *schema.ResourceData, types []ecs.InstanceTypeItemType) error { + var ids []string + var s []map[string]interface{} + for _, t := range types { + mapping := map[string]interface{}{ + "id": t.InstanceTypeId, + "cpu_core_count": t.CpuCoreCount, + "memory_size": t.MemorySize, + "family": t.InstanceTypeFamily, + } + + log.Printf("[DEBUG] alicloud_instance_type - adding type mapping: %v", mapping) + ids = append(ids, t.InstanceTypeId) + s = append(s, mapping) + } + + d.SetId(dataResourceIdHash(ids)) + if err := d.Set("instance_types", s); err != nil { + return err + } + return nil +} + +func buildAliyunAlicloudInstanceTypesArgs(d *schema.ResourceData, meta interface{}) (*ecs.DescribeInstanceTypesArgs, error) { + args := &ecs.DescribeInstanceTypesArgs{} + + if v := d.Get("instance_type_family").(string); v != "" { + args.InstanceTypeFamily = v + } + + return args, nil +} diff --git a/builtin/providers/alicloud/data_source_alicloud_instance_types_test.go b/builtin/providers/alicloud/data_source_alicloud_instance_types_test.go new file mode 100644 index 0000000000..43a180deff --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_instance_types_test.go @@ -0,0 +1,56 @@ +package alicloud + +import ( + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccAlicloudInstanceTypesDataSource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudInstanceTypesDataSourceBasicConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_instance_types.4c8g"), + + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.#", "4"), + + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.cpu_core_count", "4"), + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.memory_size", "8"), + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.id", "ecs.s3.large"), + ), + }, + + resource.TestStep{ + Config: testAccCheckAlicloudInstanceTypesDataSourceBasicConfigUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_instance_types.4c8g"), + + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.#", "1"), + + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.cpu_core_count", "4"), + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.memory_size", "8"), + ), + }, + }, + }) +} + +const testAccCheckAlicloudInstanceTypesDataSourceBasicConfig = ` +data "alicloud_instance_types" "4c8g" { + cpu_core_count = 4 + memory_size = 8 +} +` + +const testAccCheckAlicloudInstanceTypesDataSourceBasicConfigUpdate = ` +data "alicloud_instance_types" "4c8g" { + instance_type_family= "ecs.s3" + cpu_core_count = 4 + memory_size = 8 +} +` diff --git a/builtin/providers/alicloud/data_source_alicloud_regions.go b/builtin/providers/alicloud/data_source_alicloud_regions.go new file mode 100644 index 0000000000..8bdee26b6a --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_regions.go @@ -0,0 +1,114 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "log" +) + +func dataSourceAlicloudRegions() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAlicloudRegionsRead, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "current": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + + //Computed value + "regions": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "region_id": { + Type: schema.TypeString, + Computed: true, + }, + "local_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceAlicloudRegionsRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + currentRegion := getRegion(d, meta) + + resp, err := conn.DescribeRegions() + if err != nil { + return err + } + if resp == nil || len(resp) == 0 { + return fmt.Errorf("no matching regions found") + } + name, nameOk := d.GetOk("name") + current := d.Get("current").(bool) + var filterRegions []ecs.RegionType + for _, region := range resp { + if current { + if nameOk && common.Region(name.(string)) != currentRegion { + return fmt.Errorf("name doesn't match current region: %#v, please input again.", currentRegion) + } + if region.RegionId == currentRegion { + filterRegions = append(filterRegions, region) + break + } + continue + } + if nameOk { + if common.Region(name.(string)) == region.RegionId { + filterRegions = append(filterRegions, region) + break + } + continue + } + filterRegions = append(filterRegions, region) + } + if len(filterRegions) < 1 { + return fmt.Errorf("Your query region returned no results. Please change your search criteria and try again.") + } + + return regionsDescriptionAttributes(d, filterRegions) +} + +func regionsDescriptionAttributes(d *schema.ResourceData, regions []ecs.RegionType) error { + var ids []string + var s []map[string]interface{} + for _, region := range regions { + mapping := map[string]interface{}{ + "id": region.RegionId, + "region_id": region.RegionId, + "local_name": region.LocalName, + } + + log.Printf("[DEBUG] alicloud_regions - adding region mapping: %v", mapping) + ids = append(ids, string(region.RegionId)) + s = append(s, mapping) + } + + d.SetId(dataResourceIdHash(ids)) + if err := d.Set("regions", s); err != nil { + return err + } + return nil +} diff --git a/builtin/providers/alicloud/data_source_alicloud_regions_test.go b/builtin/providers/alicloud/data_source_alicloud_regions_test.go new file mode 100644 index 0000000000..f2aff1bbe4 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_regions_test.go @@ -0,0 +1,114 @@ +package alicloud + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAlicloudRegionsDataSource_regions(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRegionsDataSourceRegionsConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_regions.region"), + + resource.TestCheckResourceAttr("data.alicloud_regions.region", "name", "cn-beijing"), + resource.TestCheckResourceAttr("data.alicloud_regions.region", "current", "true"), + + resource.TestCheckResourceAttr("data.alicloud_regions.region", "regions.#", "1"), + + resource.TestCheckResourceAttr("data.alicloud_regions.region", "regions.0.id", "cn-beijing"), + resource.TestCheckResourceAttr("data.alicloud_regions.region", "regions.0.region_id", "cn-beijing"), + resource.TestCheckResourceAttr("data.alicloud_regions.region", "regions.0.local_name", "华北 2"), + ), + }, + }, + }) +} + +func TestAccAlicloudRegionsDataSource_name(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRegionsDataSourceNameConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_regions.name_filtered_region"), + resource.TestCheckResourceAttr("data.alicloud_regions.name_filtered_region", "name", "cn-hangzhou")), + }, + }, + }) +} + +func TestAccAlicloudRegionsDataSource_current(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRegionsDataSourceCurrentConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_regions.current_filtered_region"), + resource.TestCheckResourceAttr("data.alicloud_regions.current_filtered_region", "current", "true"), + ), + }, + }, + }) +} + +func TestAccAlicloudRegionsDataSource_empty(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRegionsDataSourceEmptyConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_regions.empty_params_region"), + + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "name", ""), + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "current", ""), + + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.#", "13"), + + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.id", "cn-shenzhen"), + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.region_id", "cn-shenzhen"), + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.local_name", "华南 1"), + ), + }, + }, + }) +} + +// Instance store test - using centos regions +const testAccCheckAlicloudRegionsDataSourceRegionsConfig = ` +data "alicloud_regions" "region" { + name = "cn-beijing" + current = true +} +` + +// Testing name parameter +const testAccCheckAlicloudRegionsDataSourceNameConfig = ` +data "alicloud_regions" "name_filtered_region" { + name = "cn-hangzhou" +} +` + +// Testing current parameter +const testAccCheckAlicloudRegionsDataSourceCurrentConfig = ` +data "alicloud_regions" "current_filtered_region" { + current = true +} +` + +// Testing empty parmas +const testAccCheckAlicloudRegionsDataSourceEmptyConfig = ` +data "alicloud_regions" "empty_params_region" { +} +` diff --git a/builtin/providers/alicloud/data_source_alicloud_zones.go b/builtin/providers/alicloud/data_source_alicloud_zones.go new file mode 100644 index 0000000000..689e98a167 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_zones.go @@ -0,0 +1,137 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "log" + "reflect" +) + +func dataSourceAlicloudZones() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAlicloudZonesRead, + + Schema: map[string]*schema.Schema{ + "available_instance_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "available_resource_creation": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "available_disk_category": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + // Computed values. + "zones": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "local_name": { + Type: schema.TypeString, + Computed: true, + }, + "available_instance_types": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "available_resource_creation": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "available_disk_categories": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + }, + } +} + +func dataSourceAlicloudZonesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + insType, _ := d.Get("available_instance_type").(string) + resType, _ := d.Get("available_resource_creation").(string) + diskType, _ := d.Get("available_disk_category").(string) + + resp, err := conn.DescribeZones(getRegion(d, meta)) + if err != nil { + return err + } + + var zoneTypes []ecs.ZoneType + for _, types := range resp { + if insType != "" && !constraints(types.AvailableInstanceTypes.InstanceTypes, insType) { + continue + } + + if resType != "" && !constraints(types.AvailableResourceCreation.ResourceTypes, resType) { + continue + } + + if diskType != "" && !constraints(types.AvailableDiskCategories.DiskCategories, diskType) { + continue + } + zoneTypes = append(zoneTypes, types) + } + + if len(zoneTypes) < 1 { + return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") + } + + log.Printf("[DEBUG] alicloud_zones - Zones found: %#v", zoneTypes) + return zonesDescriptionAttributes(d, zoneTypes) +} + +// check array constraints str +func constraints(arr interface{}, v string) bool { + arrs := reflect.ValueOf(arr) + len := arrs.Len() + for i := 0; i < len; i++ { + if arrs.Index(i).String() == v { + return true + } + } + return false +} + +func zonesDescriptionAttributes(d *schema.ResourceData, types []ecs.ZoneType) error { + var ids []string + var s []map[string]interface{} + for _, t := range types { + mapping := map[string]interface{}{ + "id": t.ZoneId, + "local_name": t.LocalName, + "available_instance_types": t.AvailableInstanceTypes.InstanceTypes, + "available_resource_creation": t.AvailableResourceCreation.ResourceTypes, + "available_disk_categories": t.AvailableDiskCategories.DiskCategories, + } + + log.Printf("[DEBUG] alicloud_zones - adding zone mapping: %v", mapping) + ids = append(ids, t.ZoneId) + s = append(s, mapping) + } + + d.SetId(dataResourceIdHash(ids)) + if err := d.Set("zones", s); err != nil { + return err + } + return nil +} diff --git a/builtin/providers/alicloud/data_source_alicloud_zones_test.go b/builtin/providers/alicloud/data_source_alicloud_zones_test.go new file mode 100644 index 0000000000..4b07c96717 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_zones_test.go @@ -0,0 +1,70 @@ +package alicloud + +import ( + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccAlicloudZonesDataSource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudZonesDataSourceBasicConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"), + ), + }, + }, + }) +} + +func TestAccAlicloudZonesDataSource_filter(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudZonesDataSourceFilter, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"), + resource.TestCheckResourceAttr("data.alicloud_zones.foo", "zones.#", "2"), + ), + }, + + resource.TestStep{ + Config: testAccCheckAlicloudZonesDataSourceFilterIoOptimized, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"), + resource.TestCheckResourceAttr("data.alicloud_zones.foo", "zones.#", "1"), + ), + }, + }, + }) +} + +const testAccCheckAlicloudZonesDataSourceBasicConfig = ` +data "alicloud_zones" "foo" { +} +` + +const testAccCheckAlicloudZonesDataSourceFilter = ` +data "alicloud_zones" "foo" { + "available_instance_type"= "ecs.c2.xlarge" + "available_resource_creation"= "VSwitch" + "available_disk_category"= "cloud_efficiency" +} +` + +const testAccCheckAlicloudZonesDataSourceFilterIoOptimized = ` +data "alicloud_zones" "foo" { + "available_instance_type"= "ecs.c2.xlarge" + "available_resource_creation"= "IoOptimized" + "available_disk_category"= "cloud" +} +` diff --git a/builtin/providers/alicloud/errors.go b/builtin/providers/alicloud/errors.go new file mode 100644 index 0000000000..f285bf968a --- /dev/null +++ b/builtin/providers/alicloud/errors.go @@ -0,0 +1,30 @@ +package alicloud + +const ( + // common + Notfound = "Not found" + // ecs + InstanceNotfound = "Instance.Notfound" + // disk + DiskIncorrectStatus = "IncorrectDiskStatus" + DiskCreatingSnapshot = "DiskCreatingSnapshot" + InstanceLockedForSecurity = "InstanceLockedForSecurity" + // eip + EipIncorrectStatus = "IncorrectEipStatus" + InstanceIncorrectStatus = "IncorrectInstanceStatus" + HaVipIncorrectStatus = "IncorrectHaVipStatus" + // slb + LoadBalancerNotFound = "InvalidLoadBalancerId.NotFound" + + // security_group + InvalidInstanceIdAlreadyExists = "InvalidInstanceId.AlreadyExists" + InvalidSecurityGroupIdNotFound = "InvalidSecurityGroupId.NotFound" + SgDependencyViolation = "DependencyViolation" + + //Nat gateway + NatGatewayInvalidRegionId = "Invalid.RegionId" + DependencyViolationBandwidthPackages = "DependencyViolation.BandwidthPackages" + + // vswitch + VswitcInvalidRegionId = "InvalidRegionId.NotFound" +) diff --git a/builtin/providers/alicloud/extension_ecs.go b/builtin/providers/alicloud/extension_ecs.go new file mode 100644 index 0000000000..091bd97085 --- /dev/null +++ b/builtin/providers/alicloud/extension_ecs.go @@ -0,0 +1,32 @@ +package alicloud + +type GroupRuleDirection string + +const ( + GroupRuleIngress = GroupRuleDirection("ingress") + GroupRuleEgress = GroupRuleDirection("egress") +) + +type GroupRuleIpProtocol string + +const ( + GroupRuleTcp = GroupRuleIpProtocol("tcp") + GroupRuleUdp = GroupRuleIpProtocol("udp") + GroupRuleIcmp = GroupRuleIpProtocol("icmp") + GroupRuleGre = GroupRuleIpProtocol("gre") + GroupRuleAll = GroupRuleIpProtocol("all") +) + +type GroupRuleNicType string + +const ( + GroupRuleInternet = GroupRuleNicType("internet") + GroupRuleIntranet = GroupRuleNicType("intranet") +) + +type GroupRulePolicy string + +const ( + GroupRulePolicyAccept = GroupRulePolicy("accept") + GroupRulePolicyDrop = GroupRulePolicy("drop") +) diff --git a/builtin/providers/alicloud/extension_nat_gateway.go b/builtin/providers/alicloud/extension_nat_gateway.go new file mode 100644 index 0000000000..3dac446a3f --- /dev/null +++ b/builtin/providers/alicloud/extension_nat_gateway.go @@ -0,0 +1,194 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" +) + +type BandwidthPackageType struct { + IpCount int + Bandwidth int + Zone string +} + +type CreateNatGatewayArgs struct { + RegionId common.Region + VpcId string + Spec string + BandwidthPackage []BandwidthPackageType + Name string + Description string + ClientToken string +} + +type ForwardTableIdType struct { + ForwardTableId []string +} + +type BandwidthPackageIdType struct { + BandwidthPackageId []string +} + +type CreateNatGatewayResponse struct { + common.Response + NatGatewayId string + ForwardTableIds ForwardTableIdType + BandwidthPackageIds BandwidthPackageIdType +} + +// CreateNatGateway creates Virtual Private Cloud +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&createvpc +func CreateNatGateway(client *ecs.Client, args *CreateNatGatewayArgs) (resp *CreateNatGatewayResponse, err error) { + response := CreateNatGatewayResponse{} + err = client.Invoke("CreateNatGateway", args, &response) + if err != nil { + return nil, err + } + return &response, err +} + +type NatGatewaySetType struct { + BusinessStatus string + Description string + BandwidthPackageIds BandwidthPackageIdType + ForwardTableIds ForwardTableIdType + InstanceChargeType string + Name string + NatGatewayId string + RegionId common.Region + Spec string + Status string + VpcId string +} + +type DescribeNatGatewayResponse struct { + common.Response + common.PaginationResult + NatGateways struct { + NatGateway []NatGatewaySetType + } +} + +type DescribeNatGatewaysArgs struct { + RegionId common.Region + NatGatewayId string + VpcId string + common.Pagination +} + +func DescribeNatGateways(client *ecs.Client, args *DescribeNatGatewaysArgs) (natGateways []NatGatewaySetType, + pagination *common.PaginationResult, err error) { + + args.Validate() + response := DescribeNatGatewayResponse{} + + err = client.Invoke("DescribeNatGateways", args, &response) + + if err == nil { + return response.NatGateways.NatGateway, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type ModifyNatGatewayAttributeArgs struct { + RegionId common.Region + NatGatewayId string + Name string + Description string +} + +type ModifyNatGatewayAttributeResponse struct { + common.Response +} + +func ModifyNatGatewayAttribute(client *ecs.Client, args *ModifyNatGatewayAttributeArgs) error { + response := ModifyNatGatewayAttributeResponse{} + return client.Invoke("ModifyNatGatewayAttribute", args, &response) +} + +type ModifyNatGatewaySpecArgs struct { + RegionId common.Region + NatGatewayId string + Spec NatGatewaySpec +} + +func ModifyNatGatewaySpec(client *ecs.Client, args *ModifyNatGatewaySpecArgs) error { + response := ModifyNatGatewayAttributeResponse{} + return client.Invoke("ModifyNatGatewaySpec", args, &response) +} + +type DeleteNatGatewayArgs struct { + RegionId common.Region + NatGatewayId string +} + +type DeleteNatGatewayResponse struct { + common.Response +} + +func DeleteNatGateway(client *ecs.Client, args *DeleteNatGatewayArgs) error { + response := DeleteNatGatewayResponse{} + err := client.Invoke("DeleteNatGateway", args, &response) + return err +} + +type DescribeBandwidthPackagesArgs struct { + RegionId common.Region + BandwidthPackageId string + NatGatewayId string +} + +type DescribeBandwidthPackageType struct { + Bandwidth string + BandwidthPackageId string + IpCount string +} + +type DescribeBandwidthPackagesResponse struct { + common.Response + BandwidthPackages struct { + BandwidthPackage []DescribeBandwidthPackageType + } +} + +func DescribeBandwidthPackages(client *ecs.Client, args *DescribeBandwidthPackagesArgs) ([]DescribeBandwidthPackageType, error) { + response := &DescribeBandwidthPackagesResponse{} + err := client.Invoke("DescribeBandwidthPackages", args, response) + if err != nil { + return nil, err + } + return response.BandwidthPackages.BandwidthPackage, err +} + +type DeleteBandwidthPackageArgs struct { + RegionId common.Region + BandwidthPackageId string +} + +type DeleteBandwidthPackageResponse struct { + common.Response +} + +func DeleteBandwidthPackage(client *ecs.Client, args *DeleteBandwidthPackageArgs) error { + response := DeleteBandwidthPackageResponse{} + err := client.Invoke("DeleteBandwidthPackage", args, &response) + return err +} + +type DescribeSnatTableEntriesArgs struct { + RegionId common.Region +} + +func DescribeSnatTableEntries(client *ecs.Client, args *DescribeSnatTableEntriesArgs) { + +} + +type NatGatewaySpec string + +const ( + NatGatewaySmallSpec = NatGatewaySpec("Small") + NatGatewayMiddleSpec = NatGatewaySpec("Middle") + NatGatewayLargeSpec = NatGatewaySpec("Large") +) diff --git a/builtin/providers/alicloud/extension_slb.go b/builtin/providers/alicloud/extension_slb.go new file mode 100644 index 0000000000..9213f47979 --- /dev/null +++ b/builtin/providers/alicloud/extension_slb.go @@ -0,0 +1,71 @@ +package alicloud + +import ( + "fmt" + "strings" + + "github.com/denverdino/aliyungo/slb" +) + +type Listener struct { + InstancePort int + LoadBalancerPort int + Protocol string + SSLCertificateId string + Bandwidth int +} + +// Takes the result of flatmap.Expand for an array of listeners and +// returns ELB API compatible objects +func expandListeners(configured []interface{}) ([]*Listener, error) { + listeners := make([]*Listener, 0, len(configured)) + + // Loop over our configured listeners and create + // an array of aws-sdk-go compatabile objects + for _, lRaw := range configured { + data := lRaw.(map[string]interface{}) + + ip := data["instance_port"].(int) + lp := data["lb_port"].(int) + l := &Listener{ + InstancePort: ip, + LoadBalancerPort: lp, + Protocol: data["lb_protocol"].(string), + Bandwidth: data["bandwidth"].(int), + } + + if v, ok := data["ssl_certificate_id"]; ok { + l.SSLCertificateId = v.(string) + } + + var valid bool + if l.SSLCertificateId != "" { + // validate the protocol is correct + for _, p := range []string{"https", "ssl"} { + if strings.ToLower(l.Protocol) == p { + valid = true + } + } + } else { + valid = true + } + + if valid { + listeners = append(listeners, l) + } else { + return nil, fmt.Errorf("[ERR] SLB Listener: ssl_certificate_id may be set only when protocol is 'https' or 'ssl'") + } + } + + return listeners, nil +} + +func expandBackendServers(list []interface{}) []slb.BackendServerType { + result := make([]slb.BackendServerType, 0, len(list)) + for _, i := range list { + if i.(string) != "" { + result = append(result, slb.BackendServerType{ServerId: i.(string), Weight: 100}) + } + } + return result +} diff --git a/builtin/providers/alicloud/extension_tags.go b/builtin/providers/alicloud/extension_tags.go new file mode 100644 index 0000000000..5b86ebab74 --- /dev/null +++ b/builtin/providers/alicloud/extension_tags.go @@ -0,0 +1,43 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" +) + +type Tag struct { + Key string + Value string +} + +type AddTagsArgs struct { + ResourceId string + ResourceType ecs.TagResourceType //image, instance, snapshot or disk + RegionId common.Region + Tag []Tag +} + +type RemoveTagsArgs struct { + ResourceId string + ResourceType ecs.TagResourceType //image, instance, snapshot or disk + RegionId common.Region + Tag []Tag +} + +func AddTags(client *ecs.Client, args *AddTagsArgs) error { + response := ecs.AddTagsResponse{} + err := client.Invoke("AddTags", args, &response) + if err != nil { + return err + } + return err +} + +func RemoveTags(client *ecs.Client, args *RemoveTagsArgs) error { + response := ecs.RemoveTagsResponse{} + err := client.Invoke("RemoveTags", args, &response) + if err != nil { + return err + } + return err +} diff --git a/builtin/providers/alicloud/provider.go b/builtin/providers/alicloud/provider.go new file mode 100644 index 0000000000..907f3271d9 --- /dev/null +++ b/builtin/providers/alicloud/provider.go @@ -0,0 +1,88 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/hashicorp/terraform/helper/mutexkv" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +// Provider returns a schema.Provider for alicloud +func Provider() terraform.ResourceProvider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "access_key": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ACCESS_KEY", nil), + Description: descriptions["access_key"], + }, + "secret_key": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SECRET_KEY", nil), + Description: descriptions["secret_key"], + }, + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", "cn-beijing"), + Description: descriptions["region"], + }, + }, + DataSourcesMap: map[string]*schema.Resource{ + + "alicloud_images": dataSourceAlicloudImages(), + "alicloud_regions": dataSourceAlicloudRegions(), + "alicloud_zones": dataSourceAlicloudZones(), + "alicloud_instance_types": dataSourceAlicloudInstanceTypes(), + }, + ResourcesMap: map[string]*schema.Resource{ + "alicloud_instance": resourceAliyunInstance(), + "alicloud_disk": resourceAliyunDisk(), + "alicloud_disk_attachment": resourceAliyunDiskAttachment(), + "alicloud_security_group": resourceAliyunSecurityGroup(), + "alicloud_security_group_rule": resourceAliyunSecurityGroupRule(), + "alicloud_vpc": resourceAliyunVpc(), + "alicloud_nat_gateway": resourceAliyunNatGateway(), + //both subnet and vswith exists,cause compatible old version, and compatible aws habit. + "alicloud_subnet": resourceAliyunSubnet(), + "alicloud_vswitch": resourceAliyunSubnet(), + "alicloud_route_entry": resourceAliyunRouteEntry(), + "alicloud_eip": resourceAliyunEip(), + "alicloud_eip_association": resourceAliyunEipAssociation(), + "alicloud_slb": resourceAliyunSlb(), + "alicloud_slb_attachment": resourceAliyunSlbAttachment(), + }, + + ConfigureFunc: providerConfigure, + } +} + +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + config := Config{ + AccessKey: d.Get("access_key").(string), + SecretKey: d.Get("secret_key").(string), + Region: common.Region(d.Get("region").(string)), + } + + client, err := config.Client() + if err != nil { + return nil, err + } + + return client, nil +} + +// This is a global MutexKV for use within this plugin. +var alicloudMutexKV = mutexkv.NewMutexKV() + +var descriptions map[string]string + +func init() { + descriptions = map[string]string{ + "access_key": "Access key of alicloud", + "secret_key": "Secret key of alicloud", + "region": "Region of alicloud", + } +} diff --git a/builtin/providers/alicloud/provider_test.go b/builtin/providers/alicloud/provider_test.go new file mode 100644 index 0000000000..f0f5e9becf --- /dev/null +++ b/builtin/providers/alicloud/provider_test.go @@ -0,0 +1,59 @@ +package alicloud + +import ( + "log" + "os" + "testing" + + "fmt" + "github.com/hashicorp/terraform/helper/resource" + "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{ + "alicloud": 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("ALICLOUD_ACCESS_KEY"); v == "" { + t.Fatal("ALICLOUD_ACCESS_KEY must be set for acceptance tests") + } + if v := os.Getenv("ALICLOUD_SECRET_KEY"); v == "" { + t.Fatal("ALICLOUD_SECRET_KEY must be set for acceptance tests") + } + if v := os.Getenv("ALICLOUD_REGION"); v == "" { + log.Println("[INFO] Test: Using cn-beijing as test region") + os.Setenv("ALICLOUD_REGION", "cn-beijing") + } +} + +func testAccCheckAlicloudDataSourceID(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Can't find data source: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("data source ID not set") + } + return nil + } +} diff --git a/builtin/providers/alicloud/resource_alicloud_disk.go b/builtin/providers/alicloud/resource_alicloud_disk.go new file mode 100644 index 0000000000..b31c37d937 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_disk.go @@ -0,0 +1,247 @@ +package alicloud + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "log" + "time" +) + +func resourceAliyunDisk() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunDiskCreate, + Read: resourceAliyunDiskRead, + Update: resourceAliyunDiskUpdate, + Delete: resourceAliyunDiskDelete, + + Schema: map[string]*schema.Schema{ + "availability_zone": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateDiskName, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateDiskDescription, + }, + + "category": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateDiskCategory, + Default: "cloud", + }, + + "size": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + }, + + "snapshot_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "status": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceAliyunDiskCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + + conn := client.ecsconn + + availabilityZone, err := client.DescribeZone(d.Get("availability_zone").(string)) + if err != nil { + return err + } + + args := &ecs.CreateDiskArgs{ + RegionId: getRegion(d, meta), + ZoneId: availabilityZone.ZoneId, + } + + if v, ok := d.GetOk("category"); ok && v.(string) != "" { + category := ecs.DiskCategory(v.(string)) + if err := client.DiskAvailable(availabilityZone, category); err != nil { + return err + } + args.DiskCategory = category + } + + if v, ok := d.GetOk("size"); ok { + size := v.(int) + if args.DiskCategory == ecs.DiskCategoryCloud && (size < 5 || size > 2000) { + return fmt.Errorf("the size of cloud disk must between 5 to 2000") + } + + if (args.DiskCategory == ecs.DiskCategoryCloudEfficiency || + args.DiskCategory == ecs.DiskCategoryCloudSSD) && (size < 20 || size > 32768) { + return fmt.Errorf("the size of %s disk must between 20 to 32768", args.DiskCategory) + } + args.Size = size + + d.Set("size", args.Size) + } + + if v, ok := d.GetOk("snapshot_id"); ok && v.(string) != "" { + args.SnapshotId = v.(string) + } + + if args.Size <= 0 && args.SnapshotId == "" { + return fmt.Errorf("One of size or snapshot_id is required when specifying an ECS disk.") + } + + if v, ok := d.GetOk("name"); ok && v.(string) != "" { + args.DiskName = v.(string) + } + + if v, ok := d.GetOk("description"); ok && v.(string) != "" { + args.Description = v.(string) + } + + diskID, err := conn.CreateDisk(args) + if err != nil { + return fmt.Errorf("CreateDisk got a error: %#v", err) + } + + d.SetId(diskID) + + return resourceAliyunDiskUpdate(d, meta) +} + +func resourceAliyunDiskRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + disks, _, err := conn.DescribeDisks(&ecs.DescribeDisksArgs{ + RegionId: getRegion(d, meta), + DiskIds: []string{d.Id()}, + }) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error DescribeDiskAttribute: %#v", err) + } + + log.Printf("[DEBUG] DescribeDiskAttribute for instance: %#v", disks) + + if disks == nil || len(disks) <= 0 { + return fmt.Errorf("No disks found.") + } + + disk := disks[0] + d.Set("availability_zone", disk.ZoneId) + d.Set("category", disk.Category) + d.Set("size", disk.Size) + d.Set("status", disk.Status) + d.Set("name", disk.DiskName) + d.Set("description", disk.Description) + d.Set("snapshot_id", disk.SourceSnapshotId) + + tags, _, err := conn.DescribeTags(&ecs.DescribeTagsArgs{ + RegionId: getRegion(d, meta), + ResourceType: ecs.TagResourceDisk, + ResourceId: d.Id(), + }) + + if err != nil { + log.Printf("[DEBUG] DescribeTags for disk got error: %#v", err) + } + + d.Set("tags", tagsToMap(tags)) + + return nil +} + +func resourceAliyunDiskUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + conn := client.ecsconn + + d.Partial(true) + + if err := setTags(client, ecs.TagResourceDisk, d); err != nil { + log.Printf("[DEBUG] Set tags for instance got error: %#v", err) + return fmt.Errorf("Set tags for instance got error: %#v", err) + } else { + d.SetPartial("tags") + } + attributeUpdate := false + args := &ecs.ModifyDiskAttributeArgs{ + DiskId: d.Id(), + } + + if d.HasChange("name") { + d.SetPartial("name") + val := d.Get("name").(string) + args.DiskName = val + + attributeUpdate = true + } + + if d.HasChange("description") { + d.SetPartial("description") + val := d.Get("description").(string) + args.Description = val + + attributeUpdate = true + } + if attributeUpdate { + if err := conn.ModifyDiskAttribute(args); err != nil { + return err + } + } + + d.Partial(false) + + return resourceAliyunDiskRead(d, meta) +} + +func resourceAliyunDiskDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DeleteDisk(d.Id()) + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == DiskIncorrectStatus || e.ErrorResponse.Code == DiskCreatingSnapshot { + return resource.RetryableError(fmt.Errorf("Disk in use - trying again while it is deleted.")) + } + } + + disks, _, descErr := conn.DescribeDisks(&ecs.DescribeDisksArgs{ + RegionId: getRegion(d, meta), + DiskIds: []string{d.Id()}, + }) + + if descErr != nil { + log.Printf("[ERROR] Delete disk is failed.") + return resource.NonRetryableError(descErr) + } + if disks == nil || len(disks) < 1 { + return nil + } + + return resource.RetryableError(fmt.Errorf("Disk in use - trying again while it is deleted.")) + }) +} diff --git a/builtin/providers/alicloud/resource_alicloud_disk_attachment.go b/builtin/providers/alicloud/resource_alicloud_disk_attachment.go new file mode 100644 index 0000000000..48a2c2c5c3 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_disk_attachment.go @@ -0,0 +1,176 @@ +package alicloud + +import ( + "fmt" + "strings" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "log" + "time" +) + +func resourceAliyunDiskAttachment() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunDiskAttachmentCreate, + Read: resourceAliyunDiskAttachmentRead, + Delete: resourceAliyunDiskAttachmentDelete, + + Schema: map[string]*schema.Schema{ + "instance_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "disk_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "device_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + } +} + +func resourceAliyunDiskAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + + err := diskAttachment(d, meta) + if err != nil { + return err + } + + d.SetId(d.Get("disk_id").(string) + ":" + d.Get("instance_id").(string)) + + return resourceAliyunDiskAttachmentRead(d, meta) +} + +func resourceAliyunDiskAttachmentRead(d *schema.ResourceData, meta interface{}) error { + diskId, instanceId, err := getDiskIDAndInstanceID(d, meta) + if err != nil { + return err + } + + conn := meta.(*AliyunClient).ecsconn + disks, _, err := conn.DescribeDisks(&ecs.DescribeDisksArgs{ + RegionId: getRegion(d, meta), + InstanceId: instanceId, + DiskIds: []string{diskId}, + }) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error DescribeDiskAttribute: %#v", err) + } + + log.Printf("[DEBUG] DescribeDiskAttribute for instance: %#v", disks) + if disks == nil || len(disks) <= 0 { + return fmt.Errorf("No Disks Found.") + } + + disk := disks[0] + d.Set("instance_id", disk.InstanceId) + d.Set("disk_id", disk.DiskId) + d.Set("device_name", disk.Device) + + return nil +} + +func resourceAliyunDiskAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + diskID, instanceID, err := getDiskIDAndInstanceID(d, meta) + if err != nil { + return err + } + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DetachDisk(instanceID, diskID) + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == DiskIncorrectStatus || e.ErrorResponse.Code == InstanceLockedForSecurity { + return resource.RetryableError(fmt.Errorf("Disk in use - trying again while it detaches")) + } + } + + disks, _, descErr := conn.DescribeDisks(&ecs.DescribeDisksArgs{ + RegionId: getRegion(d, meta), + DiskIds: []string{diskID}, + }) + + if descErr != nil { + log.Printf("[ERROR] Disk %s is not detached.", diskID) + return resource.NonRetryableError(err) + } + + for _, disk := range disks { + if disk.Status != ecs.DiskStatusAvailable { + return resource.RetryableError(fmt.Errorf("Disk in use - trying again while it is deleted.")) + } + } + return nil + }) +} + +func getDiskIDAndInstanceID(d *schema.ResourceData, meta interface{}) (string, string, error) { + parts := strings.Split(d.Id(), ":") + + if len(parts) != 2 { + return "", "", fmt.Errorf("invalid resource id") + } + return parts[0], parts[1], nil +} +func diskAttachment(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + diskID := d.Get("disk_id").(string) + instanceID := d.Get("instance_id").(string) + + deviceName := d.Get("device_name").(string) + + args := &ecs.AttachDiskArgs{ + InstanceId: instanceID, + DiskId: diskID, + Device: deviceName, + } + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.AttachDisk(args) + log.Printf("error : %s", err) + + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == DiskIncorrectStatus || e.ErrorResponse.Code == InstanceIncorrectStatus { + return resource.RetryableError(fmt.Errorf("Disk or Instance status is incorrect - trying again while it attaches")) + } + return resource.NonRetryableError(err) + } + + disks, _, descErr := conn.DescribeDisks(&ecs.DescribeDisksArgs{ + RegionId: getRegion(d, meta), + InstanceId: instanceID, + DiskIds: []string{diskID}, + }) + + if descErr != nil { + log.Printf("[ERROR] Disk %s is not attached.", diskID) + return resource.NonRetryableError(err) + } + + if disks == nil || len(disks) <= 0 { + return resource.RetryableError(fmt.Errorf("Disk in attaching - trying again while it is attached.")) + } + + return nil + + }) +} diff --git a/builtin/providers/alicloud/resource_alicloud_disk_attachment_test.go b/builtin/providers/alicloud/resource_alicloud_disk_attachment_test.go new file mode 100644 index 0000000000..a0fe32a0a1 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_disk_attachment_test.go @@ -0,0 +1,154 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "time" +) + +func TestAccAlicloudDiskAttachment(t *testing.T) { + var i ecs.InstanceAttributesType + var v ecs.DiskItemType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_disk_attachment.disk-att", + Providers: testAccProviders, + CheckDestroy: testAccCheckDiskAttachmentDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDiskAttachmentConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.instance", &i), + testAccCheckDiskExists( + "alicloud_disk.disk", &v), + testAccCheckDiskAttachmentExists( + "alicloud_disk_attachment.disk-att", &i, &v), + resource.TestCheckResourceAttr( + "alicloud_disk_attachment.disk-att", + "device_name", + "/dev/xvdb"), + ), + }, + }, + }) + +} + +func testAccCheckDiskAttachmentExists(n string, instance *ecs.InstanceAttributesType, disk *ecs.DiskItemType) 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 Disk ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + + request := &ecs.DescribeDisksArgs{ + RegionId: client.Region, + DiskIds: []string{rs.Primary.Attributes["disk_id"]}, + } + + return resource.Retry(3*time.Minute, func() *resource.RetryError { + response, _, err := conn.DescribeDisks(request) + if response != nil { + for _, d := range response { + if d.Status != ecs.DiskStatusInUse { + return resource.RetryableError(fmt.Errorf("Disk is in attaching - trying again while it attaches")) + } else if d.InstanceId == instance.InstanceId { + // pass + *disk = d + return nil + } + } + } + if err != nil { + return resource.NonRetryableError(err) + } + + return resource.NonRetryableError(fmt.Errorf("Error finding instance/disk")) + }) + } +} + +func testAccCheckDiskAttachmentDestroy(s *terraform.State) error { + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_disk_attachment" { + continue + } + // Try to find the Disk + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + + request := &ecs.DescribeDisksArgs{ + RegionId: client.Region, + DiskIds: []string{rs.Primary.ID}, + } + + response, _, err := conn.DescribeDisks(request) + + for _, disk := range response { + if disk.Status != ecs.DiskStatusAvailable { + return fmt.Errorf("Error ECS Disk Attachment still exist") + } + } + + if err != nil { + // Verify the error is what we want + return err + } + } + + return nil +} + +const testAccDiskAttachmentConfig = ` +resource "alicloud_disk" "disk" { + availability_zone = "cn-beijing-a" + size = "50" + + tags { + Name = "TerraformTest-disk" + } +} + +resource "alicloud_instance" "instance" { + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_type = "ecs.s1.small" + availability_zone = "cn-beijing-a" + security_groups = ["${alicloud_security_group.group.id}"] + instance_name = "hello" + internet_charge_type = "PayByBandwidth" + io_optimized = "none" + + tags { + Name = "TerraformTest-instance" + } +} + +resource "alicloud_disk_attachment" "disk-att" { + disk_id = "${alicloud_disk.disk.id}" + instance_id = "${alicloud_instance.instance.id}" + device_name = "/dev/xvdb" +} + +resource "alicloud_security_group" "group" { + name = "terraform-test-group" + description = "New security group" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_disk_test.go b/builtin/providers/alicloud/resource_alicloud_disk_test.go new file mode 100644 index 0000000000..6cb55bd8ea --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_disk_test.go @@ -0,0 +1,157 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" +) + +func TestAccAlicloudDisk_basic(t *testing.T) { + var v ecs.DiskItemType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_disk.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDiskDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDiskConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckDiskExists( + "alicloud_disk.foo", &v), + resource.TestCheckResourceAttr( + "alicloud_disk.foo", + "category", + "cloud_efficiency"), + resource.TestCheckResourceAttr( + "alicloud_disk.foo", + "size", + "30"), + ), + }, + }, + }) + +} + +func TestAccAlicloudDisk_withTags(t *testing.T) { + var v ecs.DiskItemType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + //module name + IDRefreshName: "alicloud_disk.bar", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDiskDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDiskConfigWithTags, + Check: resource.ComposeTestCheckFunc( + testAccCheckDiskExists("alicloud_disk.bar", &v), + resource.TestCheckResourceAttr( + "alicloud_disk.bar", + "tags.Name", + "TerraformTest"), + ), + }, + }, + }) +} + +func testAccCheckDiskExists(n string, disk *ecs.DiskItemType) 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 Disk ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + + request := &ecs.DescribeDisksArgs{ + RegionId: client.Region, + DiskIds: []string{rs.Primary.ID}, + } + + response, _, err := conn.DescribeDisks(request) + log.Printf("[WARN] disk ids %#v", rs.Primary.ID) + + if err == nil { + if response != nil && len(response) > 0 { + *disk = response[0] + return nil + } + } + return fmt.Errorf("Error finding ECS Disk %#v", rs.Primary.ID) + } +} + +func testAccCheckDiskDestroy(s *terraform.State) error { + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_disk" { + continue + } + + // Try to find the Disk + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + + request := &ecs.DescribeDisksArgs{ + RegionId: client.Region, + DiskIds: []string{rs.Primary.ID}, + } + + response, _, err := conn.DescribeDisks(request) + + if response != nil && len(response) > 0 { + return fmt.Errorf("Error ECS Disk still exist") + } + + if err != nil { + // Verify the error is what we want + return err + } + } + + return nil +} + +const testAccDiskConfig = ` +resource "alicloud_disk" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + name = "New-disk" + description = "Hello ecs disk." + category = "cloud_efficiency" + size = "30" +} +` +const testAccDiskConfigWithTags = ` +resource "alicloud_disk" "bar" { + # cn-beijing + availability_zone = "cn-beijing-b" + size = "10" + tags { + Name = "TerraformTest" + } +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_eip.go b/builtin/providers/alicloud/resource_alicloud_eip.go new file mode 100644 index 0000000000..8a0329eb88 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_eip.go @@ -0,0 +1,156 @@ +package alicloud + +import ( + "strconv" + + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAliyunEip() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunEipCreate, + Read: resourceAliyunEipRead, + Update: resourceAliyunEipUpdate, + Delete: resourceAliyunEipDelete, + + Schema: map[string]*schema.Schema{ + "bandwidth": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 5, + }, + "internet_charge_type": &schema.Schema{ + Type: schema.TypeString, + Default: "PayByBandwidth", + Optional: true, + ForceNew: true, + ValidateFunc: validateInternetChargeType, + }, + + "ip_address": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "status": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "instance": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + } +} + +func resourceAliyunEipCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + args, err := buildAliyunEipArgs(d, meta) + if err != nil { + return err + } + + _, allocationID, err := conn.AllocateEipAddress(args) + if err != nil { + return err + } + + d.SetId(allocationID) + + return resourceAliyunEipRead(d, meta) +} + +func resourceAliyunEipRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + + eip, err := client.DescribeEipAddress(d.Id()) + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return err + } + + bandwidth, _ := strconv.Atoi(eip.Bandwidth) + d.Set("bandwidth", bandwidth) + d.Set("internet_charge_type", eip.InternetChargeType) + d.Set("ip_address", eip.IpAddress) + d.Set("status", eip.Status) + + if eip.InstanceId != "" { + d.Set("instance", eip.InstanceId) + } else { + d.Set("instance", "") + } + + return nil +} + +func resourceAliyunEipUpdate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + d.Partial(true) + + if d.HasChange("bandwidth") { + err := conn.ModifyEipAddressAttribute(d.Id(), d.Get("bandwidth").(int)) + if err != nil { + return err + } + + d.SetPartial("bandwidth") + } + + d.Partial(false) + + return nil +} + +func resourceAliyunEipDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.ReleaseEipAddress(d.Id()) + + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == EipIncorrectStatus { + return resource.RetryableError(fmt.Errorf("EIP in use - trying again while it is deleted.")) + } + } + + args := &ecs.DescribeEipAddressesArgs{ + RegionId: getRegion(d, meta), + AllocationId: d.Id(), + } + + eips, _, descErr := conn.DescribeEipAddresses(args) + if descErr != nil { + return resource.NonRetryableError(descErr) + } else if eips == nil || len(eips) < 1 { + return nil + } + return resource.RetryableError(fmt.Errorf("EIP in use - trying again while it is deleted.")) + }) +} + +func buildAliyunEipArgs(d *schema.ResourceData, meta interface{}) (*ecs.AllocateEipAddressArgs, error) { + + args := &ecs.AllocateEipAddressArgs{ + RegionId: getRegion(d, meta), + Bandwidth: d.Get("bandwidth").(int), + InternetChargeType: common.InternetChargeType(d.Get("internet_charge_type").(string)), + } + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_eip_association.go b/builtin/providers/alicloud/resource_alicloud_eip_association.go new file mode 100644 index 0000000000..a9d419ce15 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_eip_association.go @@ -0,0 +1,131 @@ +package alicloud + +import ( + "fmt" + "strings" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAliyunEipAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunEipAssociationCreate, + Read: resourceAliyunEipAssociationRead, + Delete: resourceAliyunEipAssociationDelete, + + Schema: map[string]*schema.Schema{ + "allocation_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "instance_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + } +} + +func resourceAliyunEipAssociationCreate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + allocationId := d.Get("allocation_id").(string) + instanceId := d.Get("instance_id").(string) + + if err := conn.AssociateEipAddress(allocationId, instanceId); err != nil { + return err + } + + d.SetId(allocationId + ":" + instanceId) + + return resourceAliyunEipAssociationRead(d, meta) +} + +func resourceAliyunEipAssociationRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + + allocationId, instanceId, err := getAllocationIdAndInstanceId(d, meta) + if err != nil { + return err + } + + eip, err := client.DescribeEipAddress(allocationId) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return err + } + + if eip.InstanceId != instanceId { + d.SetId("") + return nil + } + + d.Set("instance_id", eip.InstanceId) + d.Set("allocation_id", allocationId) + return nil +} + +func resourceAliyunEipAssociationDelete(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + allocationId, instanceId, err := getAllocationIdAndInstanceId(d, meta) + if err != nil { + return err + } + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.UnassociateEipAddress(allocationId, instanceId) + + if err != nil { + e, _ := err.(*common.Error) + errCode := e.ErrorResponse.Code + if errCode == InstanceIncorrectStatus || errCode == HaVipIncorrectStatus { + return resource.RetryableError(fmt.Errorf("Eip in use - trying again while make it unassociated.")) + } + } + + args := &ecs.DescribeEipAddressesArgs{ + RegionId: getRegion(d, meta), + AllocationId: allocationId, + } + + eips, _, descErr := conn.DescribeEipAddresses(args) + + if descErr != nil { + return resource.NonRetryableError(descErr) + } else if eips == nil || len(eips) < 1 { + return nil + } + for _, eip := range eips { + if eip.Status != ecs.EipStatusAvailable { + return resource.RetryableError(fmt.Errorf("Eip in use - trying again while make it unassociated.")) + } + } + + return nil + }) +} + +func getAllocationIdAndInstanceId(d *schema.ResourceData, meta interface{}) (string, string, error) { + parts := strings.Split(d.Id(), ":") + + if len(parts) != 2 { + return "", "", fmt.Errorf("invalid resource id") + } + return parts[0], parts[1], nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_eip_association_test.go b/builtin/providers/alicloud/resource_alicloud_eip_association_test.go new file mode 100644 index 0000000000..b039248af8 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_eip_association_test.go @@ -0,0 +1,150 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "time" +) + +func TestAccAlicloudEIPAssociation(t *testing.T) { + var asso ecs.EipAddressSetType + var inst ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_eip_association.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckEIPAssociationDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccEIPAssociationConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.instance", &inst), + testAccCheckEIPExists( + "alicloud_eip.eip", &asso), + testAccCheckEIPAssociationExists( + "alicloud_eip_association.foo", &inst, &asso), + ), + }, + }, + }) + +} + +func testAccCheckEIPAssociationExists(n string, instance *ecs.InstanceAttributesType, eip *ecs.EipAddressSetType) 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 EIP Association ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + return resource.Retry(3*time.Minute, func() *resource.RetryError { + d, err := client.DescribeEipAddress(rs.Primary.Attributes["allocation_id"]) + + if err != nil { + return resource.NonRetryableError(err) + } + + if d != nil { + if d.Status != ecs.EipStatusInUse { + return resource.RetryableError(fmt.Errorf("Eip is in associating - trying again while it associates")) + } else if d.InstanceId == instance.InstanceId { + *eip = *d + return nil + } + } + + return resource.NonRetryableError(fmt.Errorf("EIP Association not found")) + }) + } +} + +func testAccCheckEIPAssociationDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_eip_association" { + continue + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No EIP Association ID is set") + } + + // Try to find the EIP + eips, _, err := client.ecsconn.DescribeEipAddresses(&ecs.DescribeEipAddressesArgs{ + RegionId: client.Region, + AllocationId: rs.Primary.Attributes["allocation_id"], + }) + + for _, eip := range eips { + if eip.Status != ecs.EipStatusAvailable { + return fmt.Errorf("Error EIP Association still exist") + } + } + + // Verify the error is what we want + if err != nil { + return err + } + } + + return nil +} + +const testAccEIPAssociationConfig = ` +resource "alicloud_vpc" "main" { + cidr_block = "10.1.0.0/21" +} + +resource "alicloud_vswitch" "main" { + vpc_id = "${alicloud_vpc.main.id}" + cidr_block = "10.1.1.0/24" + availability_zone = "cn-beijing-a" + depends_on = [ + "alicloud_vpc.main"] +} + +resource "alicloud_instance" "instance" { + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_type = "ecs.s1.small" + availability_zone = "cn-beijing-a" + security_groups = ["${alicloud_security_group.group.id}"] + vswitch_id = "${alicloud_vswitch.main.id}" + instance_name = "hello" + io_optimized = "none" + + tags { + Name = "TerraformTest-instance" + } +} + +resource "alicloud_eip" "eip" { +} + +resource "alicloud_eip_association" "foo" { + allocation_id = "${alicloud_eip.eip.id}" + instance_id = "${alicloud_instance.instance.id}" +} + +resource "alicloud_security_group" "group" { + name = "terraform-test-group" + description = "New security group" + vpc_id = "${alicloud_vpc.main.id}" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_eip_test.go b/builtin/providers/alicloud/resource_alicloud_eip_test.go new file mode 100644 index 0000000000..560f426bab --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_eip_test.go @@ -0,0 +1,131 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" +) + +func TestAccAlicloudEIP_basic(t *testing.T) { + var eip ecs.EipAddressSetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_eip.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckEIPDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccEIPConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckEIPExists( + "alicloud_eip.foo", &eip), + testAccCheckEIPAttributes(&eip), + ), + }, + resource.TestStep{ + Config: testAccEIPConfigTwo, + Check: resource.ComposeTestCheckFunc( + testAccCheckEIPExists( + "alicloud_eip.foo", &eip), + testAccCheckEIPAttributes(&eip), + resource.TestCheckResourceAttr( + "alicloud_eip.foo", + "bandwidth", + "10"), + ), + }, + }, + }) + +} + +func testAccCheckEIPExists(n string, eip *ecs.EipAddressSetType) 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 EIP ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + d, err := client.DescribeEipAddress(rs.Primary.ID) + + log.Printf("[WARN] eip id %#v", rs.Primary.ID) + + if err != nil { + return err + } + + if d == nil || d.IpAddress == "" { + return fmt.Errorf("EIP not found") + } + + *eip = *d + return nil + } +} + +func testAccCheckEIPAttributes(eip *ecs.EipAddressSetType) resource.TestCheckFunc { + return func(s *terraform.State) error { + if eip.IpAddress == "" { + return fmt.Errorf("Empty Ip address") + } + + return nil + } +} + +func testAccCheckEIPDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_eip" { + continue + } + + // Try to find the EIP + conn := client.ecsconn + + args := &ecs.DescribeEipAddressesArgs{ + RegionId: client.Region, + AllocationId: rs.Primary.ID, + } + d, _, err := conn.DescribeEipAddresses(args) + + if d != nil && len(d) > 0 { + return fmt.Errorf("Error EIP still exist") + } + + // Verify the error is what we want + if err != nil { + return err + } + } + + return nil +} + +const testAccEIPConfig = ` +resource "alicloud_eip" "foo" { +} +` + +const testAccEIPConfigTwo = ` +resource "alicloud_eip" "foo" { + bandwidth = "10" + internet_charge_type = "PayByBandwidth" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_instance.go b/builtin/providers/alicloud/resource_alicloud_instance.go new file mode 100644 index 0000000000..aeac4b3af1 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_instance.go @@ -0,0 +1,534 @@ +package alicloud + +import ( + "fmt" + "log" + + "encoding/base64" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func resourceAliyunInstance() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunInstanceCreate, + Read: resourceAliyunInstanceRead, + Update: resourceAliyunInstanceUpdate, + Delete: resourceAliyunInstanceDelete, + + Schema: map[string]*schema.Schema{ + "availability_zone": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "image_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "instance_type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "security_groups": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + + "allocate_public_ip": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "instance_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "ECS-Instance", + ValidateFunc: validateInstanceName, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateInstanceDescription, + }, + + "instance_network_type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "internet_charge_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateInternetChargeType, + }, + "internet_max_bandwidth_in": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "internet_max_bandwidth_out": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ValidateFunc: validateInternetMaxBandWidthOut, + }, + "host_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "password": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "io_optimized": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateIoOptimized, + }, + + "system_disk_category": &schema.Schema{ + Type: schema.TypeString, + Default: "cloud", + Optional: true, + ForceNew: true, + }, + "system_disk_size": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + + //subnet_id and vswitch_id both exists, cause compatible old version, and aws habit. + "subnet_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, //add this schema cause subnet_id not used enter parameter, will different, so will be ForceNew + }, + + "vswitch_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "instance_charge_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateInstanceChargeType, + }, + "period": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + + "public_ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "private_ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "status": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "user_data": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + args, err := buildAliyunInstanceArgs(d, meta) + if err != nil { + return err + } + + instanceID, err := conn.CreateInstance(args) + if err != nil { + return fmt.Errorf("Error creating Aliyun ecs instance: %#v", err) + } + + d.SetId(instanceID) + + d.Set("password", d.Get("password")) + d.Set("system_disk_category", d.Get("system_disk_category")) + + if d.Get("allocate_public_ip").(bool) { + _, err := conn.AllocatePublicIpAddress(d.Id()) + if err != nil { + log.Printf("[DEBUG] AllocatePublicIpAddress for instance got error: %#v", err) + } + } + + // after instance created, its status is pending, + // so we need to wait it become to stopped and then start it + if err := conn.WaitForInstance(d.Id(), ecs.Stopped, defaultTimeout); err != nil { + log.Printf("[DEBUG] WaitForInstance %s got error: %#v", ecs.Stopped, err) + } + + if err := conn.StartInstance(d.Id()); err != nil { + return fmt.Errorf("Start instance got error: %#v", err) + } + + if err := conn.WaitForInstance(d.Id(), ecs.Running, defaultTimeout); err != nil { + log.Printf("[DEBUG] WaitForInstance %s got error: %#v", ecs.Running, err) + } + + return resourceAliyunInstanceUpdate(d, meta) +} + +func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + conn := client.ecsconn + + instance, err := client.QueryInstancesById(d.Id()) + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error DescribeInstanceAttribute: %#v", err) + } + + log.Printf("[DEBUG] DescribeInstanceAttribute for instance: %#v", instance) + + d.Set("instance_name", instance.InstanceName) + d.Set("description", instance.Description) + d.Set("status", instance.Status) + d.Set("availability_zone", instance.ZoneId) + d.Set("host_name", instance.HostName) + d.Set("image_id", instance.ImageId) + d.Set("instance_type", instance.InstanceType) + + // In Classic network, internet_charge_type is valid in any case, and its default value is 'PayByBanwidth'. + // In VPC network, internet_charge_type is valid when instance has public ip, and its default value is 'PayByBanwidth'. + d.Set("internet_charge_type", instance.InternetChargeType) + + if d.Get("allocate_public_ip").(bool) { + d.Set("public_ip", instance.PublicIpAddress.IpAddress[0]) + } + + if ecs.StringOrBool(instance.IoOptimized).Value { + d.Set("io_optimized", "optimized") + } else { + d.Set("io_optimized", "none") + } + + log.Printf("instance.InternetChargeType: %#v", instance.InternetChargeType) + + d.Set("instance_network_type", instance.InstanceNetworkType) + + if d.Get("subnet_id").(string) != "" || d.Get("vswitch_id").(string) != "" { + ipAddress := instance.VpcAttributes.PrivateIpAddress.IpAddress[0] + d.Set("private_ip", ipAddress) + d.Set("subnet_id", instance.VpcAttributes.VSwitchId) + d.Set("vswitch_id", instance.VpcAttributes.VSwitchId) + } else { + ipAddress := strings.Join(ecs.IpAddressSetType(instance.InnerIpAddress).IpAddress, ",") + d.Set("private_ip", ipAddress) + } + + if d.Get("user_data").(string) != "" { + ud, err := conn.DescribeUserdata(&ecs.DescribeUserdataArgs{ + RegionId: getRegion(d, meta), + InstanceId: d.Id(), + }) + + if err != nil { + log.Printf("[ERROR] DescribeUserData for instance got error: %#v", err) + } + d.Set("user_data", userDataHashSum(ud.UserData)) + } + + tags, _, err := conn.DescribeTags(&ecs.DescribeTagsArgs{ + RegionId: getRegion(d, meta), + ResourceType: ecs.TagResourceInstance, + ResourceId: d.Id(), + }) + + if err != nil { + log.Printf("[ERROR] DescribeTags for instance got error: %#v", err) + } + d.Set("tags", tagsToMap(tags)) + + return nil +} + +func resourceAliyunInstanceUpdate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*AliyunClient) + conn := client.ecsconn + + d.Partial(true) + + if err := setTags(client, ecs.TagResourceInstance, d); err != nil { + log.Printf("[DEBUG] Set tags for instance got error: %#v", err) + return fmt.Errorf("Set tags for instance got error: %#v", err) + } else { + d.SetPartial("tags") + } + + attributeUpdate := false + args := &ecs.ModifyInstanceAttributeArgs{ + InstanceId: d.Id(), + } + + if d.HasChange("instance_name") { + log.Printf("[DEBUG] ModifyInstanceAttribute instance_name") + d.SetPartial("instance_name") + args.InstanceName = d.Get("instance_name").(string) + + attributeUpdate = true + } + + if d.HasChange("description") { + log.Printf("[DEBUG] ModifyInstanceAttribute description") + d.SetPartial("description") + args.Description = d.Get("description").(string) + + attributeUpdate = true + } + + if d.HasChange("host_name") { + log.Printf("[DEBUG] ModifyInstanceAttribute host_name") + d.SetPartial("host_name") + args.HostName = d.Get("host_name").(string) + + attributeUpdate = true + } + + passwordUpdate := false + if d.HasChange("password") { + log.Printf("[DEBUG] ModifyInstanceAttribute password") + d.SetPartial("password") + args.Password = d.Get("password").(string) + + attributeUpdate = true + passwordUpdate = true + } + + if attributeUpdate { + if err := conn.ModifyInstanceAttribute(args); err != nil { + return fmt.Errorf("Modify instance attribute got error: %#v", err) + } + } + + if passwordUpdate { + if v, ok := d.GetOk("status"); ok && v.(string) != "" { + if ecs.InstanceStatus(d.Get("status").(string)) == ecs.Running { + log.Printf("[DEBUG] RebootInstance after change password") + if err := conn.RebootInstance(d.Id(), false); err != nil { + return fmt.Errorf("RebootInstance got error: %#v", err) + } + + if err := conn.WaitForInstance(d.Id(), ecs.Running, defaultTimeout); err != nil { + return fmt.Errorf("WaitForInstance got error: %#v", err) + } + } + } + } + + if d.HasChange("security_groups") { + o, n := d.GetChange("security_groups") + os := o.(*schema.Set) + ns := n.(*schema.Set) + + rl := expandStringList(os.Difference(ns).List()) + al := expandStringList(ns.Difference(os).List()) + + if len(al) > 0 { + err := client.JoinSecurityGroups(d.Id(), al) + if err != nil { + return err + } + } + if len(rl) > 0 { + err := client.LeaveSecurityGroups(d.Id(), rl) + if err != nil { + return err + } + } + + d.SetPartial("security_groups") + } + + d.Partial(false) + return resourceAliyunInstanceRead(d, meta) +} + +func resourceAliyunInstanceDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + conn := client.ecsconn + + instance, err := client.QueryInstancesById(d.Id()) + if err != nil { + if notFoundError(err) { + return nil + } + return fmt.Errorf("Error DescribeInstanceAttribute: %#v", err) + } + + if instance.Status != ecs.Stopped { + if err := conn.StopInstance(d.Id(), true); err != nil { + return err + } + + if err := conn.WaitForInstance(d.Id(), ecs.Stopped, defaultTimeout); err != nil { + return err + } + } + + if err := conn.DeleteInstance(d.Id()); err != nil { + return err + } + + return nil +} + +func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateInstanceArgs, error) { + client := meta.(*AliyunClient) + + args := &ecs.CreateInstanceArgs{ + RegionId: getRegion(d, meta), + InstanceType: d.Get("instance_type").(string), + PrivateIpAddress: d.Get("private_ip").(string), + } + + imageID := d.Get("image_id").(string) + + args.ImageId = imageID + + zoneID := d.Get("availability_zone").(string) + + zone, err := client.DescribeZone(zoneID) + if err != nil { + return nil, err + } + + if err := client.ResourceAvailable(zone, ecs.ResourceTypeInstance); err != nil { + return nil, err + } + + args.ZoneId = zoneID + + sgs, ok := d.GetOk("security_groups") + + if ok { + sgList := expandStringList(sgs.(*schema.Set).List()) + sg0 := sgList[0] + // check security group instance exist + _, err := client.DescribeSecurity(sg0) + if err == nil { + args.SecurityGroupId = sg0 + } + + } + + systemDiskCategory := ecs.DiskCategory(d.Get("system_disk_category").(string)) + + if err := client.DiskAvailable(zone, systemDiskCategory); err != nil { + return nil, err + } + + args.SystemDisk = ecs.SystemDiskType{ + Category: systemDiskCategory, + } + + if v := d.Get("instance_name").(string); v != "" { + args.InstanceName = v + } + + if v := d.Get("description").(string); v != "" { + args.Description = v + } + + log.Printf("[DEBUG] internet_charge_type is %s", d.Get("internet_charge_type").(string)) + if v := d.Get("internet_charge_type").(string); v != "" { + args.InternetChargeType = common.InternetChargeType(v) + } + + if v := d.Get("internet_max_bandwidth_out").(int); v != 0 { + args.InternetMaxBandwidthOut = v + } + + if v := d.Get("host_name").(string); v != "" { + args.HostName = v + } + + if v := d.Get("password").(string); v != "" { + args.Password = v + } + + if v := d.Get("io_optimized").(string); v != "" { + args.IoOptimized = ecs.IoOptimized(v) + } + + vswitchValue := d.Get("subnet_id").(string) + if vswitchValue == "" { + vswitchValue = d.Get("vswitch_id").(string) + } + if vswitchValue != "" { + args.VSwitchId = vswitchValue + if d.Get("allocate_public_ip").(bool) && args.InternetMaxBandwidthOut <= 0 { + return nil, fmt.Errorf("Invalid internet_max_bandwidth_out result in allocation public ip failed in the VPC.") + } + } + + if v := d.Get("instance_charge_type").(string); v != "" { + args.InstanceChargeType = common.InstanceChargeType(v) + } + + log.Printf("[DEBUG] period is %d", d.Get("period").(int)) + if v := d.Get("period").(int); v != 0 { + args.Period = v + } else if args.InstanceChargeType == common.PrePaid { + return nil, fmt.Errorf("period is required for instance_charge_type is PrePaid") + } + + if v := d.Get("user_data").(string); v != "" { + args.UserData = v + } + + return args, nil +} + +func userDataHashSum(user_data string) string { + // Check whether the user_data is not Base64 encoded. + // Always calculate hash of base64 decoded value since we + // check against double-encoding when setting it + v, base64DecodeError := base64.StdEncoding.DecodeString(user_data) + if base64DecodeError != nil { + v = []byte(user_data) + } + return string(v) +} diff --git a/builtin/providers/alicloud/resource_alicloud_instance_test.go b/builtin/providers/alicloud/resource_alicloud_instance_test.go new file mode 100644 index 0000000000..2fb232f3cd --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_instance_test.go @@ -0,0 +1,1106 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" + "log" +) + +func TestAccAlicloudInstance_basic(t *testing.T) { + var instance ecs.InstanceAttributesType + + testCheck := func(*terraform.State) error { + log.Printf("[WARN] instances: %#v", instance) + if instance.ZoneId == "" { + return fmt.Errorf("bad availability zone") + } + if len(instance.SecurityGroupIds.SecurityGroupId) == 0 { + return fmt.Errorf("no security group: %#v", instance.SecurityGroupIds.SecurityGroupId) + } + + return nil + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck, + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "internet_charge_type", + "PayByBandwidth"), + ), + }, + + // test for multi steps + resource.TestStep{ + Config: testAccInstanceConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck, + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + ), + }, + }, + }) + +} + +func TestAccAlicloudInstance_vpc(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigVPC, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "system_disk_category", + "cloud_efficiency"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "internet_charge_type", + "PayByTraffic"), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_userData(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigUserData, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "system_disk_category", + "cloud_efficiency"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "internet_charge_type", + "PayByTraffic"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "user_data", + "echo 'net.ipv4.ip_forward=1'>> /etc/sysctl.conf"), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_multipleRegions(t *testing.T) { + var instance ecs.InstanceAttributesType + + // multi provideris + var providers []*schema.Provider + providerFactories := map[string]terraform.ResourceProviderFactory{ + "alicloud": func() (terraform.ResourceProvider, error) { + p := Provider() + providers = append(providers, p.(*schema.Provider)) + return p, nil + }, + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckInstanceDestroyWithProviders(&providers), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigMultipleRegions, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExistsWithProviders( + "alicloud_instance.foo", &instance, &providers), + testAccCheckInstanceExistsWithProviders( + "alicloud_instance.bar", &instance, &providers), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_multiSecurityGroup(t *testing.T) { + var instance ecs.InstanceAttributesType + + testCheck := func(sgCount int) resource.TestCheckFunc { + return func(*terraform.State) error { + if len(instance.SecurityGroupIds.SecurityGroupId) < 0 { + return fmt.Errorf("no security group: %#v", instance.SecurityGroupIds.SecurityGroupId) + } + + if len(instance.SecurityGroupIds.SecurityGroupId) < sgCount { + return fmt.Errorf("less security group: %#v", instance.SecurityGroupIds.SecurityGroupId) + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfig_multiSecurityGroup, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck(2), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + ), + }, + resource.TestStep{ + Config: testAccInstanceConfig_multiSecurityGroup_add, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck(3), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + ), + }, + resource.TestStep{ + Config: testAccInstanceConfig_multiSecurityGroup_remove, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck(1), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + ), + }, + }, + }) + +} + +func TestAccAlicloudInstance_multiSecurityGroupByCount(t *testing.T) { + var instance ecs.InstanceAttributesType + + testCheck := func(sgCount int) resource.TestCheckFunc { + return func(*terraform.State) error { + if len(instance.SecurityGroupIds.SecurityGroupId) < 0 { + return fmt.Errorf("no security group: %#v", instance.SecurityGroupIds.SecurityGroupId) + } + + if len(instance.SecurityGroupIds.SecurityGroupId) < sgCount { + return fmt.Errorf("less security group: %#v", instance.SecurityGroupIds.SecurityGroupId) + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfig_multiSecurityGroupByCount, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck(2), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + ), + }, + }, + }) + +} + +func TestAccAlicloudInstance_NetworkInstanceSecurityGroups(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceNetworkInstanceSecurityGroups, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_tags(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckInstanceConfigTags, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "tags.foo", + "bar"), + ), + }, + + resource.TestStep{ + Config: testAccCheckInstanceConfigTagsUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "tags.foo", + ""), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "tags.bar", + "zzz"), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_update(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckInstanceConfigOrigin, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "instance_foo"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "host_name", + "host-foo"), + ), + }, + + resource.TestStep{ + Config: testAccCheckInstanceConfigOriginUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "instance_bar"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "host_name", + "host-bar"), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_privateIP(t *testing.T) { + var instance ecs.InstanceAttributesType + + testCheckPrivateIP := func() resource.TestCheckFunc { + return func(*terraform.State) error { + privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0] + if privateIP != "172.16.0.229" { + return fmt.Errorf("bad private IP: %s", privateIP) + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigPrivateIP, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + testCheckPrivateIP(), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_associatePublicIPAndPrivateIP(t *testing.T) { + var instance ecs.InstanceAttributesType + + testCheckPrivateIP := func() resource.TestCheckFunc { + return func(*terraform.State) error { + privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0] + if privateIP != "172.16.0.229" { + return fmt.Errorf("bad private IP: %s", privateIP) + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigAssociatePublicIPAndPrivateIP, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + testCheckPrivateIP(), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_vpcRule(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccVpcInstanceWithSecurityRule, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "internet_charge_type", + "PayByBandwidth"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "internet_max_bandwidth_out", + "5"), + ), + }, + }, + }) +} + +func testAccCheckInstanceExists(n string, i *ecs.InstanceAttributesType) resource.TestCheckFunc { + providers := []*schema.Provider{testAccProvider} + return testAccCheckInstanceExistsWithProviders(n, i, &providers) +} + +func testAccCheckInstanceExistsWithProviders(n string, i *ecs.InstanceAttributesType, providers *[]*schema.Provider) 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") + } + for _, provider := range *providers { + // Ignore if Meta is empty, this can happen for validation providers + if provider.Meta() == nil { + continue + } + + client := provider.Meta().(*AliyunClient) + instance, err := client.QueryInstancesById(rs.Primary.ID) + log.Printf("[WARN]get ecs instance %#v", instance) + if err == nil && instance != nil { + *i = *instance + return nil + } + + // Verify the error is what we want + e, _ := err.(*common.Error) + if e.ErrorResponse.Message == InstanceNotfound { + continue + } + if err != nil { + return err + + } + } + + return fmt.Errorf("Instance not found") + } +} + +func testAccCheckInstanceDestroy(s *terraform.State) error { + return testAccCheckInstanceDestroyWithProvider(s, testAccProvider) +} + +func testAccCheckInstanceDestroyWithProviders(providers *[]*schema.Provider) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, provider := range *providers { + if provider.Meta() == nil { + continue + } + if err := testAccCheckInstanceDestroyWithProvider(s, provider); err != nil { + return err + } + } + return nil + } +} + +func testAccCheckInstanceDestroyWithProvider(s *terraform.State, provider *schema.Provider) error { + client := provider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_instance" { + continue + } + + // Try to find the resource + instance, err := client.QueryInstancesById(rs.Primary.ID) + if err == nil { + if instance.Status != "" && instance.Status != "Stopped" { + return fmt.Errorf("Found unstopped instance: %s", instance.InstanceId) + } + } + + // Verify the error is what we want + e, _ := err.(*common.Error) + if e.ErrorResponse.Message == InstanceNotfound { + continue + } + + return err + } + + return nil +} + +const testAccInstanceConfig = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_security_group" "tf_test_bar" { + name = "tf_test_bar" + description = "bar" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + system_disk_category = "cloud_ssd" + + instance_type = "ecs.n1.small" + internet_charge_type = "PayByBandwidth" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + io_optimized = "optimized" + + tags { + foo = "bar" + work = "test" + } +} +` +const testAccInstanceConfigVPC = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + vswitch_id = "${alicloud_vswitch.foo.id}" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + + internet_charge_type = "PayByTraffic" + internet_max_bandwidth_out = 5 + allocate_public_ip = true + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" +} + +` + +const testAccInstanceConfigUserData = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + vswitch_id = "${alicloud_vswitch.foo.id}" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + internet_charge_type = "PayByTraffic" + internet_max_bandwidth_out = 5 + allocate_public_ip = true + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + user_data = "echo 'net.ipv4.ip_forward=1'>> /etc/sysctl.conf" +} +` + +const testAccInstanceConfigMultipleRegions = ` +provider "alicloud" { + alias = "beijing" + region = "cn-beijing" +} + +provider "alicloud" { + alias = "shanghai" + region = "cn-shanghai" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + provider = "alicloud.beijing" + description = "foo" +} + +resource "alicloud_security_group" "tf_test_bar" { + name = "tf_test_bar" + provider = "alicloud.shanghai" + description = "bar" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + provider = "alicloud.beijing" + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + internet_charge_type = "PayByBandwidth" + + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" +} + +resource "alicloud_instance" "bar" { + # cn-shanghai + provider = "alicloud.shanghai" + availability_zone = "cn-shanghai-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + internet_charge_type = "PayByBandwidth" + + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + security_groups = ["${alicloud_security_group.tf_test_bar.id}"] + instance_name = "test_bar" +} +` + +const testAccInstanceConfig_multiSecurityGroup = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_security_group" "tf_test_bar" { + name = "tf_test_bar" + description = "bar" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + instance_type = "ecs.s2.large" + internet_charge_type = "PayByBandwidth" + security_groups = ["${alicloud_security_group.tf_test_foo.id}", "${alicloud_security_group.tf_test_bar.id}"] + instance_name = "test_foo" + io_optimized = "optimized" +}` + +const testAccInstanceConfig_multiSecurityGroup_add = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_security_group" "tf_test_bar" { + name = "tf_test_bar" + description = "bar" +} + +resource "alicloud_security_group" "tf_test_add_sg" { + name = "tf_test_add_sg" + description = "sg" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + instance_type = "ecs.s2.large" + internet_charge_type = "PayByBandwidth" + security_groups = ["${alicloud_security_group.tf_test_foo.id}", "${alicloud_security_group.tf_test_bar.id}", + "${alicloud_security_group.tf_test_add_sg.id}"] + instance_name = "test_foo" + io_optimized = "optimized" +} +` + +const testAccInstanceConfig_multiSecurityGroup_remove = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + instance_type = "ecs.s2.large" + internet_charge_type = "PayByBandwidth" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + io_optimized = "optimized" +} +` + +const testAccInstanceConfig_multiSecurityGroupByCount = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + count = 2 + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + instance_type = "ecs.s2.large" + internet_charge_type = "PayByBandwidth" + security_groups = ["${alicloud_security_group.tf_test_foo.*.id}"] + instance_name = "test_foo" + io_optimized = "none" +} +` + +const testAccInstanceNetworkInstanceSecurityGroups = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + vswitch_id = "${alicloud_vswitch.foo.id}" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + + internet_max_bandwidth_out = 5 + allocate_public_ip = "true" + internet_charge_type = "PayByBandwidth" +} +` +const testAccCheckInstanceConfigTags = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + internet_charge_type = "PayByBandwidth" + system_disk_category = "cloud_efficiency" + + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + + tags { + foo = "bar" + } +} +` + +const testAccCheckInstanceConfigTagsUpdate = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + internet_charge_type = "PayByBandwidth" + system_disk_category = "cloud_efficiency" + + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + + tags { + bar = "zzz" + } +} +` +const testAccCheckInstanceConfigOrigin = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + internet_charge_type = "PayByBandwidth" + system_disk_category = "cloud_efficiency" + + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + instance_name = "instance_foo" + host_name = "host-foo" +} +` + +const testAccCheckInstanceConfigOriginUpdate = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + internet_charge_type = "PayByBandwidth" + system_disk_category = "cloud_efficiency" + + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + instance_name = "instance_bar" + host_name = "host-bar" +} +` + +const testAccInstanceConfigPrivateIP = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/24" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + vswitch_id = "${alicloud_vswitch.foo.id}" + private_ip = "172.16.0.229" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + instance_name = "test_foo" +} +` +const testAccInstanceConfigAssociatePublicIPAndPrivateIP = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/24" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + vswitch_id = "${alicloud_vswitch.foo.id}" + private_ip = "172.16.0.229" + allocate_public_ip = "true" + internet_max_bandwidth_out = 5 + internet_charge_type = "PayByBandwidth" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + instance_name = "test_foo" +} +` +const testAccVpcInstanceWithSecurityRule = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "10.1.0.0/21" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "10.1.1.0/24" + availability_zone = "cn-beijing-c" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_security_group_rule" "ingress" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.tf_test_foo.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-c" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + vswitch_id = "${alicloud_vswitch.foo.id}" + allocate_public_ip = true + + # series II + instance_charge_type = "PostPaid" + instance_type = "ecs.n1.small" + internet_charge_type = "PayByBandwidth" + internet_max_bandwidth_out = 5 + + system_disk_category = "cloud_efficiency" + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_name = "test_foo" + io_optimized = "optimized" +} + +` diff --git a/builtin/providers/alicloud/resource_alicloud_nat_gateway.go b/builtin/providers/alicloud/resource_alicloud_nat_gateway.go new file mode 100644 index 0000000000..51622d86eb --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_nat_gateway.go @@ -0,0 +1,282 @@ +package alicloud + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "log" + "strings" + "time" +) + +func resourceAliyunNatGateway() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunNatGatewayCreate, + Read: resourceAliyunNatGatewayRead, + Update: resourceAliyunNatGatewayUpdate, + Delete: resourceAliyunNatGatewayDelete, + + Schema: map[string]*schema.Schema{ + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "spec": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "bandwidth_package_ids": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "bandwidth_packages": &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip_count": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + "bandwidth": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + "zone": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Required: true, + MaxItems: 4, + }, + }, + } +} + +func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).vpcconn + + args := &CreateNatGatewayArgs{ + RegionId: getRegion(d, meta), + VpcId: d.Get("vpc_id").(string), + Spec: d.Get("spec").(string), + } + + bandwidthPackages := d.Get("bandwidth_packages").([]interface{}) + + bandwidthPackageTypes := []BandwidthPackageType{} + + for _, e := range bandwidthPackages { + pack := e.(map[string]interface{}) + bandwidthPackage := BandwidthPackageType{ + IpCount: pack["ip_count"].(int), + Bandwidth: pack["bandwidth"].(int), + } + if pack["zone"].(string) != "" { + bandwidthPackage.Zone = pack["zone"].(string) + } + + bandwidthPackageTypes = append(bandwidthPackageTypes, bandwidthPackage) + } + + args.BandwidthPackage = bandwidthPackageTypes + + var name string + if v, ok := d.GetOk("name"); ok { + name = v.(string) + } + + args.Name = name + + if v, ok := d.GetOk("description"); ok { + args.Description = v.(string) + } + + resp, err := CreateNatGateway(conn, args) + if err != nil { + return fmt.Errorf("CreateNatGateway got error: %#v", err) + } + + d.SetId(resp.NatGatewayId) + + return resourceAliyunNatGatewayRead(d, meta) +} + +func resourceAliyunNatGatewayRead(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*AliyunClient) + + natGateway, err := client.DescribeNatGateway(d.Id()) + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return err + } + + d.Set("name", natGateway.Name) + d.Set("spec", natGateway.Spec) + d.Set("bandwidth_package_ids", strings.Join(natGateway.BandwidthPackageIds.BandwidthPackageId, ",")) + d.Set("description", natGateway.Description) + d.Set("vpc_id", natGateway.VpcId) + + return nil +} + +func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*AliyunClient) + + natGateway, err := client.DescribeNatGateway(d.Id()) + if err != nil { + return err + } + + d.Partial(true) + attributeUpdate := false + args := &ModifyNatGatewayAttributeArgs{ + RegionId: natGateway.RegionId, + NatGatewayId: natGateway.NatGatewayId, + } + + if d.HasChange("name") { + d.SetPartial("name") + var name string + if v, ok := d.GetOk("name"); ok { + name = v.(string) + } else { + return fmt.Errorf("cann't change name to empty string") + } + args.Name = name + + attributeUpdate = true + } + + if d.HasChange("description") { + d.SetPartial("description") + var description string + if v, ok := d.GetOk("description"); ok { + description = v.(string) + } else { + return fmt.Errorf("can to change description to empty string") + } + + args.Description = description + + attributeUpdate = true + } + + if attributeUpdate { + if err := ModifyNatGatewayAttribute(client.vpcconn, args); err != nil { + return err + } + } + + if d.HasChange("spec") { + d.SetPartial("spec") + var spec NatGatewaySpec + if v, ok := d.GetOk("spec"); ok { + spec = NatGatewaySpec(v.(string)) + } else { + // set default to small spec + spec = NatGatewaySmallSpec + } + + args := &ModifyNatGatewaySpecArgs{ + RegionId: natGateway.RegionId, + NatGatewayId: natGateway.NatGatewayId, + Spec: spec, + } + + err := ModifyNatGatewaySpec(client.vpcconn, args) + if err != nil { + return fmt.Errorf("%#v %#v", err, *args) + } + + } + d.Partial(false) + + return resourceAliyunNatGatewayRead(d, meta) +} + +func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*AliyunClient) + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + + packages, err := DescribeBandwidthPackages(client.vpcconn, &DescribeBandwidthPackagesArgs{ + RegionId: getRegion(d, meta), + NatGatewayId: d.Id(), + }) + if err != nil { + log.Printf("[ERROR] Describe bandwidth package is failed, natGateway Id: %s", d.Id()) + return resource.NonRetryableError(err) + } + + retry := false + for _, pack := range packages { + err = DeleteBandwidthPackage(client.vpcconn, &DeleteBandwidthPackageArgs{ + RegionId: getRegion(d, meta), + BandwidthPackageId: pack.BandwidthPackageId, + }) + + if err != nil { + er, _ := err.(*common.Error) + if er.ErrorResponse.Code == NatGatewayInvalidRegionId { + log.Printf("[ERROR] Delete bandwidth package is failed, bandwidthPackageId: %#v", pack.BandwidthPackageId) + return resource.NonRetryableError(err) + } + retry = true + } + } + + if retry { + return resource.RetryableError(fmt.Errorf("Bandwidth package in use - trying again while it is deleted.")) + } + + args := &DeleteNatGatewayArgs{ + RegionId: client.Region, + NatGatewayId: d.Id(), + } + + err = DeleteNatGateway(client.vpcconn, args) + if err != nil { + er, _ := err.(*common.Error) + if er.ErrorResponse.Code == DependencyViolationBandwidthPackages { + return resource.RetryableError(fmt.Errorf("NatGateway in use - trying again while it is deleted.")) + } + } + + describeArgs := &DescribeNatGatewaysArgs{ + RegionId: client.Region, + NatGatewayId: d.Id(), + } + gw, _, gwErr := DescribeNatGateways(client.vpcconn, describeArgs) + + if gwErr != nil { + log.Printf("[ERROR] Describe NatGateways failed.") + return resource.NonRetryableError(gwErr) + } else if gw == nil || len(gw) < 1 { + return nil + } + + return resource.RetryableError(fmt.Errorf("NatGateway in use - trying again while it is deleted.")) + }) +} diff --git a/builtin/providers/alicloud/resource_alicloud_nat_gateway_test.go b/builtin/providers/alicloud/resource_alicloud_nat_gateway_test.go new file mode 100644 index 0000000000..ad8fba1662 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_nat_gateway_test.go @@ -0,0 +1,241 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "testing" +) + +func TestAccAlicloudNatGateway_basic(t *testing.T) { + var nat NatGatewaySetType + + testCheck := func(*terraform.State) error { + if nat.BusinessStatus != "Normal" { + return fmt.Errorf("abnormal instance status") + } + + if len(nat.BandwidthPackageIds.BandwidthPackageId) == 0 { + return fmt.Errorf("no bandwidth package: %#v", nat.BandwidthPackageIds.BandwidthPackageId) + } + + return nil + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_nat_gateway.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckNatGatewayDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNatGatewayConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckNatGatewayExists( + "alicloud_nat_gateway.foo", &nat), + testCheck, + resource.TestCheckResourceAttr( + "alicloud_nat_gateway.foo", + "spec", + "Small"), + resource.TestCheckResourceAttr( + "alicloud_nat_gateway.foo", + "name", + "test_foo"), + ), + }, + }, + }) + +} + +func TestAccAlicloudNatGateway_spec(t *testing.T) { + var nat NatGatewaySetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_nat_gateway.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckNatGatewayDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNatGatewayConfigSpec, + Check: resource.ComposeTestCheckFunc( + testAccCheckNatGatewayExists( + "alicloud_nat_gateway.foo", &nat), + resource.TestCheckResourceAttr( + "alicloud_nat_gateway.foo", + "spec", + "Middle"), + ), + }, + + resource.TestStep{ + Config: testAccNatGatewayConfigSpecUpgrade, + Check: resource.ComposeTestCheckFunc( + testAccCheckNatGatewayExists( + "alicloud_nat_gateway.foo", &nat), + resource.TestCheckResourceAttr( + "alicloud_nat_gateway.foo", + "spec", + "Large"), + ), + }, + }, + }) + +} + +func testAccCheckNatGatewayExists(n string, nat *NatGatewaySetType) 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 Gateway ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + instance, err := client.DescribeNatGateway(rs.Primary.ID) + + if err != nil { + return err + } + if instance == nil { + return fmt.Errorf("Nat gateway not found") + } + + *nat = *instance + return nil + } +} + +func testAccCheckNatGatewayDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_nat_gateway" { + continue + } + + // Try to find the Nat gateway + instance, err := client.DescribeNatGateway(rs.Primary.ID) + + if instance != nil { + return fmt.Errorf("Nat gateway still exist") + } + + if err != nil { + // Verify the error is what we want + e, _ := err.(*common.Error) + + if !notFoundError(e) { + return err + } + } + + } + + return nil +} + +const testAccNatGatewayConfig = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_nat_gateway" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + spec = "Small" + name = "test_foo" + bandwidth_packages = [{ + ip_count = 1 + bandwidth = 5 + zone = "cn-beijing-b" + }, { + ip_count = 2 + bandwidth = 10 + zone = "cn-beijing-b" + }] + depends_on = [ + "alicloud_vswitch.foo"] +} +` + +const testAccNatGatewayConfigSpec = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_nat_gateway" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + spec = "Middle" + name = "test_foo" + bandwidth_packages = [{ + ip_count = 1 + bandwidth = 5 + zone = "cn-beijing-b" + }, { + ip_count = 2 + bandwidth = 10 + zone = "cn-beijing-b" + }] + depends_on = [ + "alicloud_vswitch.foo"] +} +` + +const testAccNatGatewayConfigSpecUpgrade = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_nat_gateway" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + spec = "Large" + name = "test_foo" + bandwidth_packages = [{ + ip_count = 1 + bandwidth = 5 + zone = "cn-beijing-b" + }, { + ip_count = 2 + bandwidth = 10 + zone = "cn-beijing-b" + }] + depends_on = [ + "alicloud_vswitch.foo"] +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_security_group.go b/builtin/providers/alicloud/resource_alicloud_security_group.go new file mode 100644 index 0000000000..f21ae4b270 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_security_group.go @@ -0,0 +1,169 @@ +package alicloud + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAliyunSecurityGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunSecurityGroupCreate, + Read: resourceAliyunSecurityGroupRead, + Update: resourceAliyunSecurityGroupUpdate, + Delete: resourceAliyunSecurityGroupDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateSecurityGroupName, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateSecurityGroupDescription, + }, + + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceAliyunSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + args, err := buildAliyunSecurityGroupArgs(d, meta) + if err != nil { + return err + } + + securityGroupID, err := conn.CreateSecurityGroup(args) + if err != nil { + return err + } + + d.SetId(securityGroupID) + + return resourceAliyunSecurityGroupRead(d, meta) +} + +func resourceAliyunSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + args := &ecs.DescribeSecurityGroupAttributeArgs{ + SecurityGroupId: d.Id(), + RegionId: getRegion(d, meta), + } + + sg, err := conn.DescribeSecurityGroupAttribute(args) + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error DescribeSecurityGroupAttribute: %#v", err) + } + + d.Set("name", sg.SecurityGroupName) + d.Set("description", sg.Description) + + return nil +} + +func resourceAliyunSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + d.Partial(true) + attributeUpdate := false + args := &ecs.ModifySecurityGroupAttributeArgs{ + SecurityGroupId: d.Id(), + RegionId: getRegion(d, meta), + } + + if d.HasChange("name") { + d.SetPartial("name") + args.SecurityGroupName = d.Get("name").(string) + + attributeUpdate = true + } + + if d.HasChange("description") { + d.SetPartial("description") + args.Description = d.Get("description").(string) + + attributeUpdate = true + } + if attributeUpdate { + if err := conn.ModifySecurityGroupAttribute(args); err != nil { + return err + } + } + + return nil +} + +func resourceAliyunSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DeleteSecurityGroup(getRegion(d, meta), d.Id()) + + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == SgDependencyViolation { + return resource.RetryableError(fmt.Errorf("Security group in use - trying again while it is deleted.")) + } + } + + sg, err := conn.DescribeSecurityGroupAttribute(&ecs.DescribeSecurityGroupAttributeArgs{ + RegionId: getRegion(d, meta), + SecurityGroupId: d.Id(), + }) + + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == InvalidSecurityGroupIdNotFound { + return nil + } + return resource.NonRetryableError(err) + } else if sg == nil { + return nil + } + + return resource.RetryableError(fmt.Errorf("Security group in use - trying again while it is deleted.")) + }) +} + +func buildAliyunSecurityGroupArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateSecurityGroupArgs, error) { + + args := &ecs.CreateSecurityGroupArgs{ + RegionId: getRegion(d, meta), + } + + if v := d.Get("name").(string); v != "" { + args.SecurityGroupName = v + } + + if v := d.Get("description").(string); v != "" { + args.Description = v + } + + if v := d.Get("vpc_id").(string); v != "" { + args.VpcId = v + } + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_security_group_rule.go b/builtin/providers/alicloud/resource_alicloud_security_group_rule.go new file mode 100644 index 0000000000..4627d8e2b6 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_security_group_rule.go @@ -0,0 +1,285 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "log" + "strings" +) + +func resourceAliyunSecurityGroupRule() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunSecurityGroupRuleCreate, + Read: resourceAliyunSecurityGroupRuleRead, + Delete: resourceAliyunSecurityGroupRuleDelete, + + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateSecurityRuleType, + Description: "Type of rule, ingress (inbound) or egress (outbound).", + }, + + "ip_protocol": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateSecurityRuleIpProtocol, + }, + + "nic_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateSecurityRuleNicType, + }, + + "policy": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateSecurityRulePolicy, + }, + + "port_range": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "priority": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ValidateFunc: validateSecurityPriority, + }, + + "security_group_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "cidr_ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "0.0.0.0/0", + }, + + "source_security_group_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "source_group_owner_account": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + ruleType := d.Get("type").(string) + sgId := d.Get("security_group_id").(string) + ptl := d.Get("ip_protocol").(string) + port := d.Get("port_range").(string) + + var autherr error + switch GroupRuleDirection(ruleType) { + case GroupRuleIngress: + args, err := buildAliyunSecurityIngressArgs(d, meta) + if err != nil { + return err + } + autherr = conn.AuthorizeSecurityGroup(args) + case GroupRuleEgress: + args, err := buildAliyunSecurityEgressArgs(d, meta) + if err != nil { + return err + } + autherr = conn.AuthorizeSecurityGroupEgress(args) + default: + return fmt.Errorf("Security Group Rule must be type 'ingress' or type 'egress'") + } + + if autherr != nil { + return fmt.Errorf( + "Error authorizing security group rule type %s: %s", + ruleType, autherr) + } + + d.SetId(sgId + ":" + ruleType + ":" + ptl + ":" + port) + return resourceAliyunSecurityGroupRuleRead(d, meta) +} + +func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + parts := strings.Split(d.Id(), ":") + sgId := parts[0] + types := parts[1] + ip_protocol := parts[2] + port_range := parts[3] + rule, err := client.DescribeSecurityGroupRule(sgId, types, ip_protocol, port_range) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error SecurityGroup rule: %#v", err) + } + log.Printf("[WARN]sg %s, type %s, protocol %s, port %s, rule %#v", sgId, types, ip_protocol, port_range, rule) + d.Set("type", rule.Direction) + d.Set("ip_protocol", strings.ToLower(string(rule.IpProtocol))) + d.Set("nic_type", rule.NicType) + d.Set("policy", strings.ToLower(string(rule.Policy))) + d.Set("port_range", rule.PortRange) + d.Set("priority", rule.Priority) + d.Set("security_group_id", sgId) + //support source and desc by type + if GroupRuleDirection(types) == GroupRuleIngress { + d.Set("cidr_ip", rule.SourceCidrIp) + d.Set("source_security_group_id", rule.SourceGroupId) + d.Set("source_group_owner_account", rule.SourceGroupOwnerAccount) + } else { + d.Set("cidr_ip", rule.DestCidrIp) + d.Set("source_security_group_id", rule.DestGroupId) + d.Set("source_group_owner_account", rule.DestGroupOwnerAccount) + } + + return nil +} + +func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + args, err := buildAliyunSecurityIngressArgs(d, meta) + + if err != nil { + return err + } + revokeArgs := &ecs.RevokeSecurityGroupArgs{ + AuthorizeSecurityGroupArgs: *args, + } + return client.RevokeSecurityGroup(revokeArgs) +} + +func buildAliyunSecurityIngressArgs(d *schema.ResourceData, meta interface{}) (*ecs.AuthorizeSecurityGroupArgs, error) { + conn := meta.(*AliyunClient).ecsconn + + args := &ecs.AuthorizeSecurityGroupArgs{ + RegionId: getRegion(d, meta), + } + + if v := d.Get("ip_protocol").(string); v != "" { + args.IpProtocol = ecs.IpProtocol(v) + } + + if v := d.Get("port_range").(string); v != "" { + args.PortRange = v + } + + if v := d.Get("policy").(string); v != "" { + args.Policy = ecs.PermissionPolicy(v) + } + + if v := d.Get("priority").(int); v != 0 { + args.Priority = v + } + + if v := d.Get("nic_type").(string); v != "" { + args.NicType = ecs.NicType(v) + } + + if v := d.Get("cidr_ip").(string); v != "" { + args.SourceCidrIp = v + } + + if v := d.Get("source_security_group_id").(string); v != "" { + args.SourceGroupId = v + } + + if v := d.Get("source_group_owner_account").(string); v != "" { + args.SourceGroupOwnerAccount = v + } + + sgId := d.Get("security_group_id").(string) + + sgArgs := &ecs.DescribeSecurityGroupAttributeArgs{ + SecurityGroupId: sgId, + RegionId: getRegion(d, meta), + } + + _, err := conn.DescribeSecurityGroupAttribute(sgArgs) + if err != nil { + return nil, fmt.Errorf("Error get security group %s error: %#v", sgId, err) + } + + args.SecurityGroupId = sgId + + return args, nil +} + +func buildAliyunSecurityEgressArgs(d *schema.ResourceData, meta interface{}) (*ecs.AuthorizeSecurityGroupEgressArgs, error) { + conn := meta.(*AliyunClient).ecsconn + + args := &ecs.AuthorizeSecurityGroupEgressArgs{ + RegionId: getRegion(d, meta), + } + + if v := d.Get("ip_protocol").(string); v != "" { + args.IpProtocol = ecs.IpProtocol(v) + } + + if v := d.Get("port_range").(string); v != "" { + args.PortRange = v + } + + if v := d.Get("policy").(string); v != "" { + args.Policy = ecs.PermissionPolicy(v) + } + + if v := d.Get("priority").(int); v != 0 { + args.Priority = v + } + + if v := d.Get("nic_type").(string); v != "" { + args.NicType = ecs.NicType(v) + } + + if v := d.Get("cidr_ip").(string); v != "" { + args.DestCidrIp = v + } + + if v := d.Get("source_security_group_id").(string); v != "" { + args.DestGroupId = v + } + + if v := d.Get("source_group_owner_account").(string); v != "" { + args.DestGroupOwnerAccount = v + } + + sgId := d.Get("security_group_id").(string) + + sgArgs := &ecs.DescribeSecurityGroupAttributeArgs{ + SecurityGroupId: sgId, + RegionId: getRegion(d, meta), + } + + _, err := conn.DescribeSecurityGroupAttribute(sgArgs) + if err != nil { + return nil, fmt.Errorf("Error get security group %s error: %#v", sgId, err) + } + + args.SecurityGroupId = sgId + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_security_group_rule_test.go b/builtin/providers/alicloud/resource_alicloud_security_group_rule_test.go new file mode 100644 index 0000000000..7eb267fcba --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_security_group_rule_test.go @@ -0,0 +1,263 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" + "strings" + "testing" +) + +func TestAccAlicloudSecurityGroupRule_Ingress(t *testing.T) { + var pt ecs.PermissionType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group_rule.ingress", + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupRuleIngress, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "priority", + "1"), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "nic_type", + "internet"), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "ip_protocol", + "tcp"), + ), + }, + }, + }) + +} + +func TestAccAlicloudSecurityGroupRule_Egress(t *testing.T) { + var pt ecs.PermissionType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group_rule.egress", + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupRuleEgress, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.egress", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.egress", + "port_range", + "80/80"), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.egress", + "ip_protocol", + "udp"), + ), + }, + }, + }) + +} + +func TestAccAlicloudSecurityGroupRule_Vpc_Ingress(t *testing.T) { + var pt ecs.PermissionType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group_rule.ingress", + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupRuleVpcIngress, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "port_range", + "1/200"), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "ip_protocol", + "udp"), + ), + }, + }, + }) + +} + +func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) 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 SecurityGroup Rule ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + log.Printf("[WARN]get sg rule %s", rs.Primary.ID) + parts := strings.Split(rs.Primary.ID, ":") + rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3]) + + if err != nil { + return err + } + + if rule == nil { + return fmt.Errorf("SecurityGroup not found") + } + + *m = *rule + return nil + } +} + +func testAccCheckSecurityGroupRuleDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_security_group_rule" { + continue + } + + parts := strings.Split(rs.Primary.ID, ":") + rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3]) + + if rule != nil { + return fmt.Errorf("Error SecurityGroup Rule still exist") + } + + // Verify the error is what we want + if err != nil { + // Verify the error is what we want + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == InvalidSecurityGroupIdNotFound { + continue + } + return err + } + } + + return nil +} + +const testAccSecurityGroupRuleIngress = ` +resource "alicloud_security_group" "foo" { + name = "sg_foo" +} + +resource "alicloud_security_group_rule" "ingress" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "1/200" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "10.159.6.18/12" +} + + +` + +const testAccSecurityGroupRuleEgress = ` +resource "alicloud_security_group" "foo" { + name = "sg_foo" +} + + +resource "alicloud_security_group_rule" "egress" { + type = "egress" + ip_protocol = "udp" + nic_type = "internet" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "10.159.6.18/12" +} + +` + +const testAccSecurityGroupRuleVpcIngress = ` +resource "alicloud_security_group" "foo" { + vpc_id = "${alicloud_vpc.vpc.id}" + name = "sg_foo" +} + +resource "alicloud_vpc" "vpc" { + cidr_block = "10.1.0.0/21" +} + +resource "alicloud_security_group_rule" "ingress" { + type = "ingress" + ip_protocol = "udp" + nic_type = "intranet" + policy = "accept" + port_range = "1/200" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "10.159.6.18/12" +} + +` + +const testAccSecurityGroupRuleMultiIngress = ` +resource "alicloud_security_group" "foo" { + name = "sg_foo" +} + +resource "alicloud_security_group_rule" "ingress1" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "1/200" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "10.159.6.18/12" +} + +resource "alicloud_security_group_rule" "ingress2" { + type = "ingress" + ip_protocol = "gre" + nic_type = "internet" + policy = "accept" + port_range = "-1/-1" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "127.0.1.18/16" +} + +` diff --git a/builtin/providers/alicloud/resource_alicloud_security_group_test.go b/builtin/providers/alicloud/resource_alicloud_security_group_test.go new file mode 100644 index 0000000000..19211bc1fd --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_security_group_test.go @@ -0,0 +1,151 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" +) + +func TestAccAlicloudSecurityGroup_basic(t *testing.T) { + var sg ecs.DescribeSecurityGroupAttributeResponse + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupExists( + "alicloud_security_group.foo", &sg), + resource.TestCheckResourceAttr( + "alicloud_security_group.foo", + "name", + "sg_test"), + ), + }, + }, + }) + +} + +func TestAccAlicloudSecurityGroup_withVpc(t *testing.T) { + var sg ecs.DescribeSecurityGroupAttributeResponse + var vpc ecs.VpcSetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupConfig_withVpc, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupExists( + "alicloud_security_group.foo", &sg), + testAccCheckVpcExists( + "alicloud_vpc.vpc", &vpc), + ), + }, + }, + }) + +} + +func testAccCheckSecurityGroupExists(n string, sg *ecs.DescribeSecurityGroupAttributeResponse) 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 SecurityGroup ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + args := &ecs.DescribeSecurityGroupAttributeArgs{ + RegionId: client.Region, + SecurityGroupId: rs.Primary.ID, + } + d, err := conn.DescribeSecurityGroupAttribute(args) + + log.Printf("[WARN] security group id %#v", rs.Primary.ID) + + if err != nil { + return err + } + + if d == nil { + return fmt.Errorf("SecurityGroup not found") + } + + *sg = *d + return nil + } +} + +func testAccCheckSecurityGroupDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_security_group" { + continue + } + + // Try to find the SecurityGroup + args := &ecs.DescribeSecurityGroupsArgs{ + RegionId: client.Region, + } + + groups, _, err := conn.DescribeSecurityGroups(args) + + for _, sg := range groups { + if sg.SecurityGroupId == rs.Primary.ID { + return fmt.Errorf("Error SecurityGroup still exist") + } + } + + // Verify the error is what we want + if err != nil { + return err + } + } + + return nil +} + +const testAccSecurityGroupConfig = ` +resource "alicloud_security_group" "foo" { + name = "sg_test" +} +` + +const testAccSecurityGroupConfig_withVpc = ` +resource "alicloud_security_group" "foo" { + vpc_id = "${alicloud_vpc.vpc.id}" +} + +resource "alicloud_vpc" "vpc" { + cidr_block = "10.1.0.0/21" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_slb.go b/builtin/providers/alicloud/resource_alicloud_slb.go new file mode 100644 index 0000000000..8bc787b3d6 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_slb.go @@ -0,0 +1,420 @@ +package alicloud + +import ( + "bytes" + "fmt" + "strings" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/slb" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAliyunSlb() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunSlbCreate, + Read: resourceAliyunSlbRead, + Update: resourceAliyunSlbUpdate, + Delete: resourceAliyunSlbDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateSlbName, + Computed: true, + }, + + "internet": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + + "vswitch_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "internet_charge_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "paybytraffic", + ValidateFunc: validateSlbInternetChargeType, + }, + + "bandwidth": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateSlbBandwidth, + Computed: true, + }, + + "listener": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "instance_port": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateInstancePort, + Required: true, + }, + + "lb_port": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateInstancePort, + Required: true, + }, + + "lb_protocol": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateInstanceProtocol, + Required: true, + }, + + "bandwidth": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateSlbListenerBandwidth, + Required: true, + }, + //http + "scheduler": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSlbListenerScheduler, + Optional: true, + Default: "wrr", + }, + + "sticky_session": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSlbListenerStickySession, + Optional: true, + }, + "sticky_session_type": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSlbListenerStickySessionType, + Optional: true, + }, + "cookie": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSlbListenerCookie, + Optional: true, + }, + "PersistenceTimeout": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateSlbListenerPersistenceTimeout, + Optional: true, + Default: 0, + }, + //https + "ssl_certificate_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Set: resourceAliyunSlbListenerHash, + }, + + //deprecated + "instances": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Set: schema.HashString, + }, + + "address": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAliyunSlbCreate(d *schema.ResourceData, meta interface{}) error { + + slbconn := meta.(*AliyunClient).slbconn + + var slbName string + if v, ok := d.GetOk("name"); ok { + slbName = v.(string) + } else { + slbName = resource.PrefixedUniqueId("tf-lb-") + d.Set("name", slbName) + } + + slbArgs := &slb.CreateLoadBalancerArgs{ + RegionId: getRegion(d, meta), + LoadBalancerName: slbName, + } + + if internet, ok := d.GetOk("internet"); ok && internet.(bool) { + slbArgs.AddressType = slb.InternetAddressType + d.Set("internet", true) + } else { + slbArgs.AddressType = slb.IntranetAddressType + d.Set("internet", false) + } + + if v, ok := d.GetOk("internet_charge_type"); ok && v.(string) != "" { + slbArgs.InternetChargeType = slb.InternetChargeType(v.(string)) + } + + if v, ok := d.GetOk("bandwidth"); ok && v.(int) != 0 { + slbArgs.Bandwidth = v.(int) + } + + if v, ok := d.GetOk("vswitch_id"); ok && v.(string) != "" { + slbArgs.VSwitchId = v.(string) + } + slb, err := slbconn.CreateLoadBalancer(slbArgs) + if err != nil { + return err + } + + d.SetId(slb.LoadBalancerId) + + return resourceAliyunSlbUpdate(d, meta) +} + +func resourceAliyunSlbRead(d *schema.ResourceData, meta interface{}) error { + slbconn := meta.(*AliyunClient).slbconn + loadBalancer, err := slbconn.DescribeLoadBalancerAttribute(d.Id()) + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + + return err + } + + d.Set("name", loadBalancer.LoadBalancerName) + + if loadBalancer.AddressType == slb.InternetAddressType { + d.Set("internal", true) + } else { + d.Set("internal", false) + } + d.Set("internet_charge_type", loadBalancer.InternetChargeType) + d.Set("bandwidth", loadBalancer.Bandwidth) + d.Set("vswitch_id", loadBalancer.VSwitchId) + d.Set("address", loadBalancer.Address) + + return nil +} + +func resourceAliyunSlbUpdate(d *schema.ResourceData, meta interface{}) error { + + slbconn := meta.(*AliyunClient).slbconn + + d.Partial(true) + + if d.HasChange("name") { + err := slbconn.SetLoadBalancerName(d.Id(), d.Get("name").(string)) + if err != nil { + return err + } + + d.SetPartial("name") + } + + if d.Get("internet") == true && d.Get("internet_charge_type") == "paybybandwidth" { + //don't intranet web and paybybandwidth, then can modify bandwidth + if d.HasChange("bandwidth") { + args := &slb.ModifyLoadBalancerInternetSpecArgs{ + LoadBalancerId: d.Id(), + Bandwidth: d.Get("bandwidth").(int), + } + err := slbconn.ModifyLoadBalancerInternetSpec(args) + if err != nil { + return err + } + + d.SetPartial("bandwidth") + } + } + + if d.HasChange("listener") { + o, n := d.GetChange("listener") + os := o.(*schema.Set) + ns := n.(*schema.Set) + + remove, _ := expandListeners(os.Difference(ns).List()) + add, _ := expandListeners(ns.Difference(os).List()) + + if len(remove) > 0 { + for _, listener := range remove { + err := slbconn.DeleteLoadBalancerListener(d.Id(), listener.LoadBalancerPort) + if err != nil { + return fmt.Errorf("Failure removing outdated SLB listeners: %#v", err) + } + } + } + + if len(add) > 0 { + for _, listener := range add { + err := createListener(slbconn, d.Id(), listener) + if err != nil { + return fmt.Errorf("Failure add SLB listeners: %#v", err) + } + } + } + + d.SetPartial("listener") + } + + // If we currently have instances, or did have instances, + // we want to figure out what to add and remove from the load + // balancer + if d.HasChange("instances") { + o, n := d.GetChange("instances") + os := o.(*schema.Set) + ns := n.(*schema.Set) + remove := expandBackendServers(os.Difference(ns).List()) + add := expandBackendServers(ns.Difference(os).List()) + + if len(add) > 0 { + _, err := slbconn.AddBackendServers(d.Id(), add) + if err != nil { + return err + } + } + if len(remove) > 0 { + removeBackendServers := make([]string, 0, len(remove)) + for _, e := range remove { + removeBackendServers = append(removeBackendServers, e.ServerId) + } + _, err := slbconn.RemoveBackendServers(d.Id(), removeBackendServers) + if err != nil { + return err + } + } + + d.SetPartial("instances") + } + + d.Partial(false) + + return resourceAliyunSlbRead(d, meta) +} + +func resourceAliyunSlbDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).slbconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DeleteLoadBalancer(d.Id()) + + if err != nil { + return resource.NonRetryableError(err) + } + + loadBalancer, err := conn.DescribeLoadBalancerAttribute(d.Id()) + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == LoadBalancerNotFound { + return nil + } + return resource.NonRetryableError(err) + } + if loadBalancer != nil { + return resource.RetryableError(fmt.Errorf("LoadBalancer in use - trying again while it deleted.")) + } + return nil + }) +} + +func resourceAliyunSlbListenerHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int))) + buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int))) + buf.WriteString(fmt.Sprintf("%s-", + strings.ToLower(m["lb_protocol"].(string)))) + + buf.WriteString(fmt.Sprintf("%d-", m["bandwidth"].(int))) + + if v, ok := m["ssl_certificate_id"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + + return hashcode.String(buf.String()) +} + +func createListener(conn *slb.Client, loadBalancerId string, listener *Listener) error { + if listener.Protocol == strings.ToLower("tcp") { + args := &slb.CreateLoadBalancerTCPListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: listener.LoadBalancerPort, + BackendServerPort: listener.InstancePort, + Bandwidth: listener.Bandwidth, + } + if err := conn.CreateLoadBalancerTCPListener(args); err != nil { + return err + } + } + + if listener.Protocol == strings.ToLower("http") { + args := &slb.CreateLoadBalancerHTTPListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: listener.LoadBalancerPort, + BackendServerPort: listener.InstancePort, + Bandwidth: listener.Bandwidth, + StickySession: slb.OffFlag, + HealthCheck: slb.OffFlag, + } + + if err := conn.CreateLoadBalancerHTTPListener(args); err != nil { + return err + } + } + + if listener.Protocol == strings.ToLower("https") { + args := &slb.CreateLoadBalancerHTTPSListenerArgs{ + + HTTPListenerType: slb.HTTPListenerType{ + LoadBalancerId: loadBalancerId, + ListenerPort: listener.LoadBalancerPort, + BackendServerPort: listener.InstancePort, + Bandwidth: listener.Bandwidth, + StickySession: slb.OffFlag, + HealthCheck: slb.OffFlag, + }, + } + if listener.SSLCertificateId == "" { + return fmt.Errorf("Server Certificated Id cann't be null") + } + + args.ServerCertificateId = listener.SSLCertificateId + + if err := conn.CreateLoadBalancerHTTPSListener(args); err != nil { + return err + } + } + + if listener.Protocol == strings.ToLower("udp") { + args := &slb.CreateLoadBalancerUDPListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: listener.LoadBalancerPort, + BackendServerPort: listener.InstancePort, + Bandwidth: listener.Bandwidth, + } + + if err := conn.CreateLoadBalancerUDPListener(args); err != nil { + return err + } + } + + if err := conn.StartLoadBalancerListener(loadBalancerId, listener.LoadBalancerPort); err != nil { + return err + } + + return nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_slb_attachment.go b/builtin/providers/alicloud/resource_alicloud_slb_attachment.go new file mode 100644 index 0000000000..6a9163c072 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_slb_attachment.go @@ -0,0 +1,144 @@ +package alicloud + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func resourceAliyunSlbAttachment() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunSlbAttachmentCreate, + Read: resourceAliyunSlbAttachmentRead, + Update: resourceAliyunSlbAttachmentUpdate, + Delete: resourceAliyunSlbAttachmentDelete, + + Schema: map[string]*schema.Schema{ + + "slb_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "instances": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + Set: schema.HashString, + }, + + "backend_servers": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + } +} + +func resourceAliyunSlbAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + + slbId := d.Get("slb_id").(string) + + slbconn := meta.(*AliyunClient).slbconn + + loadBalancer, err := slbconn.DescribeLoadBalancerAttribute(slbId) + if err != nil { + if notFoundError(err) { + d.SetId("") + return fmt.Errorf("Special SLB Id not found: %#v", err) + } + + return err + } + + d.SetId(loadBalancer.LoadBalancerId) + + return resourceAliyunSlbAttachmentUpdate(d, meta) +} + +func resourceAliyunSlbAttachmentRead(d *schema.ResourceData, meta interface{}) error { + + slbconn := meta.(*AliyunClient).slbconn + loadBalancer, err := slbconn.DescribeLoadBalancerAttribute(d.Id()) + if err != nil { + if notFoundError(err) { + d.SetId("") + return fmt.Errorf("Read special SLB Id not found: %#v", err) + } + + return err + } + + backendServerType := loadBalancer.BackendServers + servers := backendServerType.BackendServer + instanceIds := make([]string, 0, len(servers)) + if len(servers) > 0 { + for _, e := range servers { + instanceIds = append(instanceIds, e.ServerId) + } + if err != nil { + return err + } + } + + d.Set("slb_id", d.Id()) + d.Set("instances", instanceIds) + d.Set("backend_servers", strings.Join(instanceIds, ",")) + + return nil +} + +func resourceAliyunSlbAttachmentUpdate(d *schema.ResourceData, meta interface{}) error { + + slbconn := meta.(*AliyunClient).slbconn + if d.HasChange("instances") { + o, n := d.GetChange("instances") + os := o.(*schema.Set) + ns := n.(*schema.Set) + remove := expandBackendServers(os.Difference(ns).List()) + add := expandBackendServers(ns.Difference(os).List()) + + if len(add) > 0 { + _, err := slbconn.AddBackendServers(d.Id(), add) + if err != nil { + return err + } + } + if len(remove) > 0 { + removeBackendServers := make([]string, 0, len(remove)) + for _, e := range remove { + removeBackendServers = append(removeBackendServers, e.ServerId) + } + _, err := slbconn.RemoveBackendServers(d.Id(), removeBackendServers) + if err != nil { + return err + } + } + + } + + return resourceAliyunSlbAttachmentRead(d, meta) + +} + +func resourceAliyunSlbAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + + slbconn := meta.(*AliyunClient).slbconn + o := d.Get("instances") + os := o.(*schema.Set) + remove := expandBackendServers(os.List()) + + if len(remove) > 0 { + removeBackendServers := make([]string, 0, len(remove)) + for _, e := range remove { + removeBackendServers = append(removeBackendServers, e.ServerId) + } + _, err := slbconn.RemoveBackendServers(d.Id(), removeBackendServers) + if err != nil { + return err + } + } + + return nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_slb_attachment_test.go b/builtin/providers/alicloud/resource_alicloud_slb_attachment_test.go new file mode 100644 index 0000000000..90a70ead8b --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_slb_attachment_test.go @@ -0,0 +1,110 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/slb" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" + "testing" +) + +func TestAccAlicloudSlbAttachment_basic(t *testing.T) { + var slb slb.LoadBalancerType + + testCheckAttr := func() resource.TestCheckFunc { + return func(*terraform.State) error { + log.Printf("testCheckAttr slb BackendServers is: %#v", slb.BackendServers) + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_slb_attachment.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckSlbDestroy, + Steps: []resource.TestStep{ + //test internet_charge_type is paybybandwidth + resource.TestStep{ + Config: testAccSlbAttachment, + Check: resource.ComposeTestCheckFunc( + testAccCheckSlbExists("alicloud_slb_attachment.foo", &slb), + testCheckAttr(), + testAccCheckAttachment("alicloud_instance.foo", &slb), + ), + }, + }, + }) +} + +func testAccCheckAttachment(n string, slb *slb.LoadBalancerType) 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 ECS ID is set") + } + + ecsInstanceId := rs.Primary.ID + + backendServers := slb.BackendServers.BackendServer + + if len(backendServers) == 0 { + return fmt.Errorf("no SLB backendServer: %#v", backendServers) + } + + log.Printf("slb bacnendservers: %#v", backendServers) + + backendServersInstanceId := backendServers[0].ServerId + + if ecsInstanceId != backendServersInstanceId { + return fmt.Errorf("SLB attachment check invalid: ECS instance %s is not equal SLB backendServer %s", + ecsInstanceId, backendServersInstanceId) + } + return nil + } +} + +const testAccSlbAttachment = ` +resource "alicloud_security_group" "foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + internet_charge_type = "PayByBandwidth" + internet_max_bandwidth_out = "5" + system_disk_category = "cloud_efficiency" + io_optimized = "optimized" + + security_groups = ["${alicloud_security_group.foo.id}"] + instance_name = "test_foo" +} + +resource "alicloud_slb" "foo" { + name = "tf_test_slb_bind" + internet_charge_type = "paybybandwidth" + bandwidth = "5" + internet = "true" +} + +resource "alicloud_slb_attachment" "foo" { + slb_id = "${alicloud_slb.foo.id}" + instances = ["${alicloud_instance.foo.id}"] +} + +` diff --git a/builtin/providers/alicloud/resource_alicloud_slb_test.go b/builtin/providers/alicloud/resource_alicloud_slb_test.go new file mode 100644 index 0000000000..3e68a4c149 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_slb_test.go @@ -0,0 +1,294 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/slb" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" + "testing" +) + +func TestAccAlicloudSlb_basic(t *testing.T) { + var slb slb.LoadBalancerType + + testCheckAttr := func() resource.TestCheckFunc { + return func(*terraform.State) error { + log.Printf("testCheckAttr slb AddressType is: %s", slb.AddressType) + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_slb.bindwidth", + + Providers: testAccProviders, + CheckDestroy: testAccCheckSlbDestroy, + Steps: []resource.TestStep{ + //test internet_charge_type is paybybandwidth + resource.TestStep{ + Config: testAccSlbBindWidth, + Check: resource.ComposeTestCheckFunc( + testAccCheckSlbExists("alicloud_slb.bindwidth", &slb), + testCheckAttr(), + resource.TestCheckResourceAttr( + "alicloud_slb.bindwidth", "internet_charge_type", "paybybandwidth"), + ), + }, + }, + }) +} + +func TestAccAlicloudSlb_traffic(t *testing.T) { + var slb slb.LoadBalancerType + + testCheckAttr := func() resource.TestCheckFunc { + return func(*terraform.State) error { + log.Printf("testCheckAttr slb AddressType is: %s", slb.AddressType) + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_slb.traffic", + Providers: testAccProviders, + CheckDestroy: testAccCheckSlbDestroy, + Steps: []resource.TestStep{ + //test internet_charge_type is paybytraffic + resource.TestStep{ + Config: testAccSlbTraffic, + Check: resource.ComposeTestCheckFunc( + testAccCheckSlbExists("alicloud_slb.traffic", &slb), + testCheckAttr(), + resource.TestCheckResourceAttr( + "alicloud_slb.traffic", "name", "tf_test_slb_classic"), + ), + }, + }, + }) +} + +func TestAccAlicloudSlb_listener(t *testing.T) { + var slb slb.LoadBalancerType + + testListener := func() resource.TestCheckFunc { + return func(*terraform.State) error { + listenerPorts := slb.ListenerPorts.ListenerPort[0] + if listenerPorts != 161 { + return fmt.Errorf("bad loadbalancer listener: %#v", listenerPorts) + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_slb.listener", + Providers: testAccProviders, + CheckDestroy: testAccCheckSlbDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSlbListener, + Check: resource.ComposeTestCheckFunc( + testAccCheckSlbExists("alicloud_slb.listener", &slb), + resource.TestCheckResourceAttr( + "alicloud_slb.listener", "name", "tf_test_slb"), + testAccCheckListenersExists("alicloud_slb.listener", &slb, "http"), + testListener(), + ), + }, + }, + }) +} + +func TestAccAlicloudSlb_vpc(t *testing.T) { + var slb slb.LoadBalancerType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_slb.vpc", + Providers: testAccProviders, + CheckDestroy: testAccCheckSlbDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSlb4Vpc, + Check: resource.ComposeTestCheckFunc( + testAccCheckSlbExists("alicloud_slb.vpc", &slb), + resource.TestCheckResourceAttr( + "alicloud_slb.vpc", "name", "tf_test_slb_vpc"), + ), + }, + }, + }) +} + +func testAccCheckSlbExists(n string, slb *slb.LoadBalancerType) 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 SLB ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + instance, err := client.DescribeLoadBalancerAttribute(rs.Primary.ID) + + if err != nil { + return err + } + if instance == nil { + return fmt.Errorf("SLB not found") + } + + *slb = *instance + return nil + } +} + +func testAccCheckListenersExists(n string, slb *slb.LoadBalancerType, p string) 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 SLB ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + instance, err := client.DescribeLoadBalancerAttribute(rs.Primary.ID) + + if err != nil { + return err + } + if instance == nil { + return fmt.Errorf("SLB not found") + } + + exist := false + for _, listener := range instance.ListenerPortsAndProtocol.ListenerPortAndProtocol { + if listener.ListenerProtocol == p { + exist = true + break + } + } + + if !exist { + return fmt.Errorf("The %s protocol Listener not found.", p) + } + return nil + } +} + +func testAccCheckSlbDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_slb" { + continue + } + + // Try to find the Slb + instance, err := client.DescribeLoadBalancerAttribute(rs.Primary.ID) + + if instance != nil { + return fmt.Errorf("SLB still exist") + } + + if err != nil { + e, _ := err.(*common.Error) + // Verify the error is what we want + if e.ErrorResponse.Code != LoadBalancerNotFound { + return err + } + + } + + } + + return nil +} + +const testAccSlbBindWidth = ` +resource "alicloud_slb" "bindwidth" { + name = "tf_test_slb_bindwidth" + internet_charge_type = "paybybandwidth" + bandwidth = 5 + internet = true +} +` + +const testAccSlbTraffic = ` +resource "alicloud_slb" "traffic" { + name = "tf_test_slb_classic" +} +` + +const testAccSlbListener = ` +resource "alicloud_slb" "listener" { + name = "tf_test_slb" + internet_charge_type = "paybybandwidth" + bandwidth = 5 + internet = true + listener = [ + { + "instance_port" = "2111" + "lb_port" = "21" + "lb_protocol" = "tcp" + "bandwidth" = 1 + },{ + "instance_port" = "8000" + "lb_port" = "80" + "lb_protocol" = "http" + "bandwidth" = 1 + },{ + "instance_port" = "1611" + "lb_port" = "161" + "lb_protocol" = "udp" + "bandwidth" = 1 + }] +} +` + +const testAccSlb4Vpc = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_slb" "vpc" { + name = "tf_test_slb_vpc" + //internet_charge_type = "paybybandwidth" + vswitch_id = "${alicloud_vswitch.foo.id}" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_vpc.go b/builtin/providers/alicloud/resource_alicloud_vpc.go new file mode 100644 index 0000000000..e59e3b53b8 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vpc.go @@ -0,0 +1,190 @@ +package alicloud + +import ( + "fmt" + "strings" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAliyunVpc() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunVpcCreate, + Read: resourceAliyunVpcRead, + Update: resourceAliyunVpcUpdate, + Delete: resourceAliyunVpcDelete, + + Schema: map[string]*schema.Schema{ + "cidr_block": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateCIDRNetworkAddress, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 128 { + errors = append(errors, fmt.Errorf("%s cannot be longer than 128 characters", k)) + } + + if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") { + errors = append(errors, fmt.Errorf("%s cannot starts with http:// or https://", k)) + } + + return + }, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 256 { + errors = append(errors, fmt.Errorf("%s cannot be longer than 256 characters", k)) + + } + return + }, + }, + "router_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "router_table_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAliyunVpcCreate(d *schema.ResourceData, meta interface{}) error { + + args, err := buildAliyunVpcArgs(d, meta) + if err != nil { + return err + } + + ecsconn := meta.(*AliyunClient).ecsconn + + vpc, err := ecsconn.CreateVpc(args) + if err != nil { + return err + } + + d.SetId(vpc.VpcId) + d.Set("router_table_id", vpc.RouteTableId) + + err = ecsconn.WaitForVpcAvailable(args.RegionId, vpc.VpcId, 60) + if err != nil { + return fmt.Errorf("Timeout when WaitForVpcAvailable") + } + + return resourceAliyunVpcRead(d, meta) +} + +func resourceAliyunVpcRead(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*AliyunClient) + + vpc, err := client.DescribeVpc(d.Id()) + if err != nil { + return err + } + + if vpc == nil { + d.SetId("") + return nil + } + + d.Set("cidr_block", vpc.CidrBlock) + d.Set("name", vpc.VpcName) + d.Set("description", vpc.Description) + d.Set("router_id", vpc.VRouterId) + + return nil +} + +func resourceAliyunVpcUpdate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + d.Partial(true) + + attributeUpdate := false + args := &ecs.ModifyVpcAttributeArgs{ + VpcId: d.Id(), + } + + if d.HasChange("name") { + d.SetPartial("name") + args.VpcName = d.Get("name").(string) + + attributeUpdate = true + } + + if d.HasChange("description") { + d.SetPartial("description") + args.Description = d.Get("description").(string) + + attributeUpdate = true + } + + if attributeUpdate { + if err := conn.ModifyVpcAttribute(args); err != nil { + return err + } + } + + d.Partial(false) + + return nil +} + +func resourceAliyunVpcDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DeleteVpc(d.Id()) + + if err != nil { + return resource.RetryableError(fmt.Errorf("Vpc in use - trying again while it is deleted.")) + } + + args := &ecs.DescribeVpcsArgs{ + RegionId: getRegion(d, meta), + VpcId: d.Id(), + } + vpc, _, descErr := conn.DescribeVpcs(args) + if descErr != nil { + return resource.NonRetryableError(err) + } else if vpc == nil || len(vpc) < 1 { + return nil + } + + return resource.RetryableError(fmt.Errorf("Vpc in use - trying again while it is deleted.")) + }) +} + +func buildAliyunVpcArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateVpcArgs, error) { + args := &ecs.CreateVpcArgs{ + RegionId: getRegion(d, meta), + CidrBlock: d.Get("cidr_block").(string), + } + + if v := d.Get("name").(string); v != "" { + args.VpcName = v + } + + if v := d.Get("description").(string); v != "" { + args.Description = v + } + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_vpc_test.go b/builtin/providers/alicloud/resource_alicloud_vpc_test.go new file mode 100644 index 0000000000..b67c650013 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vpc_test.go @@ -0,0 +1,140 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAlicloudVpc_basic(t *testing.T) { + var vpc ecs.VpcSetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_vpc.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckVpcDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccVpcConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcExists("alicloud_vpc.foo", &vpc), + resource.TestCheckResourceAttr( + "alicloud_vpc.foo", "cidr_block", "172.16.0.0/12"), + resource.TestCheckResourceAttrSet( + "alicloud_vpc.foo", "router_id"), + resource.TestCheckResourceAttrSet( + "alicloud_vpc.foo", "router_table_id"), + ), + }, + }, + }) + +} + +func TestAccAlicloudVpc_update(t *testing.T) { + var vpc ecs.VpcSetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVpcDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccVpcConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcExists("alicloud_vpc.foo", &vpc), + resource.TestCheckResourceAttr( + "alicloud_vpc.foo", "cidr_block", "172.16.0.0/12"), + ), + }, + resource.TestStep{ + Config: testAccVpcConfigUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcExists("alicloud_vpc.foo", &vpc), + resource.TestCheckResourceAttr( + "alicloud_vpc.foo", "name", "tf_test_bar"), + ), + }, + }, + }) +} + +func testAccCheckVpcExists(n string, vpc *ecs.VpcSetType) 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 VPC ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + instance, err := client.DescribeVpc(rs.Primary.ID) + + if err != nil { + return err + } + if instance == nil { + return fmt.Errorf("VPC not found") + } + + *vpc = *instance + return nil + } +} + +func testAccCheckVpcDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_vpc" { + continue + } + + // Try to find the VPC + instance, err := client.DescribeVpc(rs.Primary.ID) + + if instance != nil { + return fmt.Errorf("VPCs still exist") + } + + if err != nil { + // Verify the error is what we want + e, _ := err.(*common.Error) + + if e.ErrorResponse.Code != "InvalidVpcID.NotFound" { + return err + } + } + + } + + return nil +} + +const testAccVpcConfig = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} +` + +const testAccVpcConfigUpdate = ` +resource "alicloud_vpc" "foo" { + cidr_block = "172.16.0.0/12" + name = "tf_test_bar" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_vroute_entry.go b/builtin/providers/alicloud/resource_alicloud_vroute_entry.go new file mode 100644 index 0000000000..b71e92ffeb --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vroute_entry.go @@ -0,0 +1,145 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func resourceAliyunRouteEntry() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunRouteEntryCreate, + Read: resourceAliyunRouteEntryRead, + Delete: resourceAliyunRouteEntryDelete, + + Schema: map[string]*schema.Schema{ + "router_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "route_table_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "destination_cidrblock": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "nexthop_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateRouteEntryNextHopType, + }, + "nexthop_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceAliyunRouteEntryCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + rtId := d.Get("route_table_id").(string) + rId := d.Get("router_id").(string) + cidr := d.Get("destination_cidrblock").(string) + nt := d.Get("nexthop_type").(string) + ni := d.Get("nexthop_id").(string) + + args, err := buildAliyunRouteEntryArgs(d, meta) + if err != nil { + return err + } + err = conn.CreateRouteEntry(args) + + if err != nil { + return err + } + // route_table_id:router_id:destination_cidrblock:nexthop_type:nexthop_id + d.SetId(rtId + ":" + rId + ":" + cidr + ":" + nt + ":" + ni) + d.Set("router_id", rId) + + if err := conn.WaitForAllRouteEntriesAvailable(rId, rtId, defaultTimeout); err != nil { + return fmt.Errorf("WaitFor route entry got error: %#v", err) + } + return resourceAliyunRouteEntryRead(d, meta) +} + +func resourceAliyunRouteEntryRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + parts := strings.Split(d.Id(), ":") + rtId := parts[0] + //rId := parts[1] + cidr := parts[2] + nexthop_type := parts[3] + nexthop_id := parts[4] + + en, err := client.QueryRouteEntry(rtId, cidr, nexthop_type, nexthop_id) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error route entry: %#v", err) + } + + d.Set("route_table_id", en.RouteTableId) + d.Set("destination_cidrblock", en.DestinationCidrBlock) + d.Set("nexthop_type", en.NextHopType) + d.Set("nexthop_id", en.InstanceId) + return nil +} + +func resourceAliyunRouteEntryDelete(d *schema.ResourceData, meta interface{}) error { + con := meta.(*AliyunClient).ecsconn + args, err := buildAliyunRouteEntryDeleteArgs(d, meta) + + if err != nil { + return err + } + return con.DeleteRouteEntry(args) +} + +func buildAliyunRouteEntryArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateRouteEntryArgs, error) { + + args := &ecs.CreateRouteEntryArgs{ + RouteTableId: d.Get("route_table_id").(string), + DestinationCidrBlock: d.Get("destination_cidrblock").(string), + } + + if v := d.Get("nexthop_type").(string); v != "" { + args.NextHopType = ecs.NextHopType(v) + } + + if v := d.Get("nexthop_id").(string); v != "" { + args.NextHopId = v + } + + return args, nil +} + +func buildAliyunRouteEntryDeleteArgs(d *schema.ResourceData, meta interface{}) (*ecs.DeleteRouteEntryArgs, error) { + + args := &ecs.DeleteRouteEntryArgs{ + RouteTableId: d.Get("route_table_id").(string), + DestinationCidrBlock: d.Get("destination_cidrblock").(string), + } + + if v := d.Get("destination_cidrblock").(string); v != "" { + args.DestinationCidrBlock = v + } + + if v := d.Get("nexthop_id").(string); v != "" { + args.NextHopId = v + } + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_vroute_entry_test.go b/builtin/providers/alicloud/resource_alicloud_vroute_entry_test.go new file mode 100644 index 0000000000..cbdb59bef1 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vroute_entry_test.go @@ -0,0 +1,183 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "strings" + "testing" +) + +func TestAccAlicloudRouteEntry_Basic(t *testing.T) { + var rt ecs.RouteTableSetType + var rn ecs.RouteEntrySetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_route_entry.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckRouteEntryDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRouteEntryConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRouteTableEntryExists( + "alicloud_route_entry.foo", &rt, &rn), + resource.TestCheckResourceAttrSet( + "alicloud_route_entry.foo", "nexthop_id"), + ), + }, + }, + }) + +} + +func testAccCheckRouteTableExists(rtId string, t *ecs.RouteTableSetType) error { + client := testAccProvider.Meta().(*AliyunClient) + //query route table + rt, terr := client.QueryRouteTableById(rtId) + + if terr != nil { + return terr + } + + if rt == nil { + return fmt.Errorf("Route Table not found") + } + + *t = *rt + return nil +} + +func testAccCheckRouteEntryExists(routeTableId, cidrBlock, nextHopType, nextHopId string, e *ecs.RouteEntrySetType) error { + client := testAccProvider.Meta().(*AliyunClient) + //query route table entry + re, rerr := client.QueryRouteEntry(routeTableId, cidrBlock, nextHopType, nextHopId) + + if rerr != nil { + return rerr + } + + if re == nil { + return fmt.Errorf("Route Table Entry not found") + } + + *e = *re + return nil +} + +func testAccCheckRouteTableEntryExists(n string, t *ecs.RouteTableSetType, e *ecs.RouteEntrySetType) 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 Route Entry ID is set") + } + + parts := strings.Split(rs.Primary.ID, ":") + + //query route table + err := testAccCheckRouteTableExists(parts[0], t) + + if err != nil { + return err + } + //query route table entry + err = testAccCheckRouteEntryExists(parts[0], parts[2], parts[3], parts[4], e) + return err + } +} + +func testAccCheckRouteEntryDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_route_entry" { + continue + } + + parts := strings.Split(rs.Primary.ID, ":") + re, err := client.QueryRouteEntry(parts[0], parts[2], parts[3], parts[4]) + + if re != nil { + return fmt.Errorf("Error Route Entry still exist") + } + + // Verify the error is what we want + if err != nil { + if notFoundError(err) { + return nil + } + return err + } + } + + return nil +} + +const testAccRouteEntryConfig = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "10.1.0.0/21" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "10.1.1.0/24" + availability_zone = "cn-beijing-c" +} + +resource "alicloud_route_entry" "foo" { + router_id = "${alicloud_vpc.foo.router_id}" + route_table_id = "${alicloud_vpc.foo.router_table_id}" + destination_cidrblock = "172.11.1.1/32" + nexthop_type = "Instance" + nexthop_id = "${alicloud_instance.foo.id}" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_security_group_rule" "ingress" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.tf_test_foo.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-c" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + vswitch_id = "${alicloud_vswitch.foo.id}" + allocate_public_ip = true + + # series II + instance_charge_type = "PostPaid" + instance_type = "ecs.n1.small" + internet_charge_type = "PayByTraffic" + internet_max_bandwidth_out = 5 + io_optimized = "optimized" + + system_disk_category = "cloud_efficiency" + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_name = "test_foo" +} + +` diff --git a/builtin/providers/alicloud/resource_alicloud_vswitch.go b/builtin/providers/alicloud/resource_alicloud_vswitch.go new file mode 100644 index 0000000000..74d4c6a888 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vswitch.go @@ -0,0 +1,220 @@ +package alicloud + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "log" + "time" +) + +func resourceAliyunSubnet() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunSwitchCreate, + Read: resourceAliyunSwitchRead, + Update: resourceAliyunSwitchUpdate, + Delete: resourceAliyunSwitchDelete, + + Schema: map[string]*schema.Schema{ + "availability_zone": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "cidr_block": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateSwitchCIDRNetworkAddress, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func resourceAliyunSwitchCreate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + args, err := buildAliyunSwitchArgs(d, meta) + if err != nil { + return err + } + + vswitchID, err := conn.CreateVSwitch(args) + if err != nil { + return fmt.Errorf("Create subnet got a error :%s", err) + } + + d.SetId(vswitchID) + + err = conn.WaitForVSwitchAvailable(args.VpcId, vswitchID, 60) + if err != nil { + return fmt.Errorf("WaitForVSwitchAvailable got a error: %s", err) + } + + return resourceAliyunSwitchRead(d, meta) +} + +func resourceAliyunSwitchRead(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + args := &ecs.DescribeVSwitchesArgs{ + VpcId: d.Get("vpc_id").(string), + VSwitchId: d.Id(), + } + + vswitches, _, err := conn.DescribeVSwitches(args) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return err + } + + if len(vswitches) == 0 { + d.SetId("") + return nil + } + + vswitch := vswitches[0] + + d.Set("availability_zone", vswitch.ZoneId) + d.Set("vpc_id", vswitch.VpcId) + d.Set("cidr_block", vswitch.CidrBlock) + d.Set("name", vswitch.VSwitchName) + d.Set("description", vswitch.Description) + + return nil +} + +func resourceAliyunSwitchUpdate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + d.Partial(true) + + attributeUpdate := false + args := &ecs.ModifyVSwitchAttributeArgs{ + VSwitchId: d.Id(), + } + + if d.HasChange("name") { + d.SetPartial("name") + args.VSwitchName = d.Get("name").(string) + + attributeUpdate = true + } + + if d.HasChange("description") { + d.SetPartial("description") + args.Description = d.Get("description").(string) + + attributeUpdate = true + } + if attributeUpdate { + if err := conn.ModifyVSwitchAttribute(args); err != nil { + return err + } + + } + + d.Partial(false) + + return nil +} + +func resourceAliyunSwitchDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DeleteVSwitch(d.Id()) + + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == VswitcInvalidRegionId { + log.Printf("[ERROR] Delete Switch is failed.") + return resource.NonRetryableError(err) + } + + return resource.RetryableError(fmt.Errorf("Switch in use. -- trying again while it is deleted.")) + } + + vsw, _, vswErr := conn.DescribeVSwitches(&ecs.DescribeVSwitchesArgs{ + VpcId: d.Get("vpc_id").(string), + VSwitchId: d.Id(), + }) + + if vswErr != nil { + return resource.NonRetryableError(vswErr) + } else if vsw == nil || len(vsw) < 1 { + return nil + } + + return resource.RetryableError(fmt.Errorf("Switch in use. -- trying again while it is deleted.")) + }) +} + +func buildAliyunSwitchArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateVSwitchArgs, error) { + + client := meta.(*AliyunClient) + + vpcID := d.Get("vpc_id").(string) + + vpc, err := client.DescribeVpc(vpcID) + if err != nil { + return nil, err + } + + if vpc == nil { + return nil, fmt.Errorf("vpc_id not found") + } + + zoneID := d.Get("availability_zone").(string) + + zone, err := client.DescribeZone(zoneID) + if err != nil { + return nil, err + } + + err = client.ResourceAvailable(zone, ecs.ResourceTypeVSwitch) + if err != nil { + return nil, err + } + + cidrBlock := d.Get("cidr_block").(string) + + args := &ecs.CreateVSwitchArgs{ + VpcId: vpcID, + ZoneId: zoneID, + CidrBlock: cidrBlock, + } + + if v, ok := d.GetOk("name"); ok && v != "" { + args.VSwitchName = v.(string) + } + + if v, ok := d.GetOk("description"); ok && v != "" { + args.Description = v.(string) + } + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_vswitch_test.go b/builtin/providers/alicloud/resource_alicloud_vswitch_test.go new file mode 100644 index 0000000000..bcd70a2cc1 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vswitch_test.go @@ -0,0 +1,105 @@ +package alicloud + +import ( + "testing" + + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAlicloudVswitch_basic(t *testing.T) { + var vsw ecs.VSwitchSetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_vswitch.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckVswitchDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccVswitchConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVswitchExists("alicloud_vswitch.foo", &vsw), + resource.TestCheckResourceAttr( + "alicloud_vswitch.foo", "cidr_block", "172.16.0.0/21"), + ), + }, + }, + }) + +} + +func testAccCheckVswitchExists(n string, vpc *ecs.VSwitchSetType) 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 Vswitch ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + instance, err := client.QueryVswitchById(rs.Primary.Attributes["vpc_id"], rs.Primary.ID) + + if err != nil { + return err + } + if instance == nil { + return fmt.Errorf("Vswitch not found") + } + + *vpc = *instance + return nil + } +} + +func testAccCheckVswitchDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_vswitch" { + continue + } + + // Try to find the Vswitch + instance, err := client.QueryVswitchById(rs.Primary.Attributes["vpc_id"], rs.Primary.ID) + + if instance != nil { + return fmt.Errorf("Vswitch still exist") + } + + if err != nil { + // Verify the error is what we want + e, _ := err.(*common.Error) + + if e.ErrorResponse.Code != "InvalidVswitchID.NotFound" { + return err + } + } + + } + + return nil +} + +const testAccVswitchConfig = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} +` diff --git a/builtin/providers/alicloud/service_alicloud_ecs.go b/builtin/providers/alicloud/service_alicloud_ecs.go new file mode 100644 index 0000000000..2c892ce242 --- /dev/null +++ b/builtin/providers/alicloud/service_alicloud_ecs.go @@ -0,0 +1,208 @@ +package alicloud + +import ( + "encoding/json" + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "strings" +) + +func (client *AliyunClient) DescribeImage(imageId string) (*ecs.ImageType, error) { + + pagination := common.Pagination{ + PageNumber: 1, + } + args := ecs.DescribeImagesArgs{ + Pagination: pagination, + RegionId: client.Region, + Status: ecs.ImageStatusAvailable, + } + + var allImages []ecs.ImageType + + for { + images, _, err := client.ecsconn.DescribeImages(&args) + if err != nil { + break + } + + if len(images) == 0 { + break + } + + allImages = append(allImages, images...) + + args.Pagination.PageNumber++ + } + + if len(allImages) == 0 { + return nil, common.GetClientErrorFromString("Not found") + } + + var image *ecs.ImageType + imageIds := []string{} + for _, im := range allImages { + if im.ImageId == imageId { + image = &im + } + imageIds = append(imageIds, im.ImageId) + } + + if image == nil { + return nil, fmt.Errorf("image_id %s not exists in range %s, all images are %s", imageId, client.Region, imageIds) + } + + return image, nil +} + +// DescribeZone validate zoneId is valid in region +func (client *AliyunClient) DescribeZone(zoneID string) (*ecs.ZoneType, error) { + zones, err := client.ecsconn.DescribeZones(client.Region) + if err != nil { + return nil, fmt.Errorf("error to list zones not found") + } + + var zone *ecs.ZoneType + zoneIds := []string{} + for _, z := range zones { + if z.ZoneId == zoneID { + zone = &ecs.ZoneType{ + ZoneId: z.ZoneId, + LocalName: z.LocalName, + AvailableResourceCreation: z.AvailableResourceCreation, + AvailableDiskCategories: z.AvailableDiskCategories, + } + } + zoneIds = append(zoneIds, z.ZoneId) + } + + if zone == nil { + return nil, fmt.Errorf("availability_zone not exists in range %s, all zones are %s", client.Region, zoneIds) + } + + return zone, nil +} + +func (client *AliyunClient) QueryInstancesByIds(ids []string) (instances []ecs.InstanceAttributesType, err error) { + idsStr, jerr := json.Marshal(ids) + if jerr != nil { + return nil, jerr + } + + args := ecs.DescribeInstancesArgs{ + RegionId: client.Region, + InstanceIds: string(idsStr), + } + + instances, _, errs := client.ecsconn.DescribeInstances(&args) + + if errs != nil { + return nil, errs + } + + return instances, nil +} + +func (client *AliyunClient) QueryInstancesById(id string) (instance *ecs.InstanceAttributesType, err error) { + ids := []string{id} + + instances, errs := client.QueryInstancesByIds(ids) + if errs != nil { + return nil, errs + } + + if len(instances) == 0 { + return nil, common.GetClientErrorFromString(InstanceNotfound) + } + + return &instances[0], nil +} + +// ResourceAvailable check resource available for zone +func (client *AliyunClient) ResourceAvailable(zone *ecs.ZoneType, resourceType ecs.ResourceType) error { + available := false + for _, res := range zone.AvailableResourceCreation.ResourceTypes { + if res == resourceType { + available = true + } + } + if !available { + return fmt.Errorf("%s is not available in %s zone of %s region", resourceType, zone.ZoneId, client.Region) + } + + return nil +} + +func (client *AliyunClient) DiskAvailable(zone *ecs.ZoneType, diskCategory ecs.DiskCategory) error { + available := false + for _, dist := range zone.AvailableDiskCategories.DiskCategories { + if dist == diskCategory { + available = true + } + } + if !available { + return fmt.Errorf("%s is not available in %s zone of %s region", diskCategory, zone.ZoneId, client.Region) + } + return nil +} + +// todo: support syc +func (client *AliyunClient) JoinSecurityGroups(instanceId string, securityGroupIds []string) error { + for _, sid := range securityGroupIds { + err := client.ecsconn.JoinSecurityGroup(instanceId, sid) + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code != InvalidInstanceIdAlreadyExists { + return err + } + } + } + + return nil +} + +func (client *AliyunClient) LeaveSecurityGroups(instanceId string, securityGroupIds []string) error { + for _, sid := range securityGroupIds { + err := client.ecsconn.LeaveSecurityGroup(instanceId, sid) + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code != InvalidSecurityGroupIdNotFound { + return err + } + } + } + + return nil +} + +func (client *AliyunClient) DescribeSecurity(securityGroupId string) (*ecs.DescribeSecurityGroupAttributeResponse, error) { + + args := &ecs.DescribeSecurityGroupAttributeArgs{ + RegionId: client.Region, + SecurityGroupId: securityGroupId, + } + + return client.ecsconn.DescribeSecurityGroupAttribute(args) +} + +func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, types, ip_protocol, port_range string) (*ecs.PermissionType, error) { + + sg, err := client.DescribeSecurity(securityGroupId) + if err != nil { + return nil, err + } + + for _, p := range sg.Permissions.Permission { + if strings.ToLower(string(p.IpProtocol)) == ip_protocol && p.PortRange == port_range { + return &p, nil + } + } + return nil, nil + +} + +func (client *AliyunClient) RevokeSecurityGroup(args *ecs.RevokeSecurityGroupArgs) error { + //todo: handle the specal err + return client.ecsconn.RevokeSecurityGroup(args) +} diff --git a/builtin/providers/alicloud/service_alicloud_slb.go b/builtin/providers/alicloud/service_alicloud_slb.go new file mode 100644 index 0000000000..9fc4730183 --- /dev/null +++ b/builtin/providers/alicloud/service_alicloud_slb.go @@ -0,0 +1,21 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/slb" +) + +func (client *AliyunClient) DescribeLoadBalancerAttribute(slbId string) (*slb.LoadBalancerType, error) { + loadBalancer, err := client.slbconn.DescribeLoadBalancerAttribute(slbId) + if err != nil { + if notFoundError(err) { + return nil, nil + } + return nil, err + } + + if loadBalancer != nil { + return loadBalancer, nil + } + + return nil, nil +} diff --git a/builtin/providers/alicloud/service_alicloud_vpc.go b/builtin/providers/alicloud/service_alicloud_vpc.go new file mode 100644 index 0000000000..102c611653 --- /dev/null +++ b/builtin/providers/alicloud/service_alicloud_vpc.go @@ -0,0 +1,134 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "strings" +) + +func (client *AliyunClient) DescribeEipAddress(allocationId string) (*ecs.EipAddressSetType, error) { + + args := ecs.DescribeEipAddressesArgs{ + RegionId: client.Region, + AllocationId: allocationId, + } + + eips, _, err := client.ecsconn.DescribeEipAddresses(&args) + if err != nil { + return nil, err + } + if len(eips) == 0 { + return nil, common.GetClientErrorFromString("Not found") + } + + return &eips[0], nil +} + +func (client *AliyunClient) DescribeNatGateway(natGatewayId string) (*NatGatewaySetType, error) { + + args := &DescribeNatGatewaysArgs{ + RegionId: client.Region, + NatGatewayId: natGatewayId, + } + + natGateways, _, err := DescribeNatGateways(client.ecsconn, args) + if err != nil { + return nil, err + } + + if len(natGateways) == 0 { + return nil, common.GetClientErrorFromString("Not found") + } + + return &natGateways[0], nil +} + +func (client *AliyunClient) DescribeVpc(vpcId string) (*ecs.VpcSetType, error) { + args := ecs.DescribeVpcsArgs{ + RegionId: client.Region, + VpcId: vpcId, + } + + vpcs, _, err := client.ecsconn.DescribeVpcs(&args) + if err != nil { + if notFoundError(err) { + return nil, nil + } + return nil, err + } + + if len(vpcs) == 0 { + return nil, nil + } + + return &vpcs[0], nil +} + +// describe vswitch by param filters +func (client *AliyunClient) QueryVswitches(args *ecs.DescribeVSwitchesArgs) (vswitches []ecs.VSwitchSetType, err error) { + vsws, _, err := client.ecsconn.DescribeVSwitches(args) + if err != nil { + if notFoundError(err) { + return nil, nil + } + return nil, err + } + + return vsws, nil +} + +func (client *AliyunClient) QueryVswitchById(vpcId, vswitchId string) (vsw *ecs.VSwitchSetType, err error) { + args := &ecs.DescribeVSwitchesArgs{ + VpcId: vpcId, + VSwitchId: vswitchId, + } + vsws, err := client.QueryVswitches(args) + if err != nil { + return nil, err + } + + if len(vsws) == 0 { + return nil, nil + } + + return &vsws[0], nil +} + +func (client *AliyunClient) QueryRouteTables(args *ecs.DescribeRouteTablesArgs) (routeTables []ecs.RouteTableSetType, err error) { + rts, _, err := client.ecsconn.DescribeRouteTables(args) + if err != nil { + return nil, err + } + + return rts, nil +} + +func (client *AliyunClient) QueryRouteTableById(routeTableId string) (rt *ecs.RouteTableSetType, err error) { + args := &ecs.DescribeRouteTablesArgs{ + RouteTableId: routeTableId, + } + rts, err := client.QueryRouteTables(args) + if err != nil { + return nil, err + } + + if len(rts) == 0 { + return nil, &common.Error{ErrorResponse: common.ErrorResponse{Message: Notfound}} + } + + return &rts[0], nil +} + +func (client *AliyunClient) QueryRouteEntry(routeTableId, cidrBlock, nextHopType, nextHopId string) (rn *ecs.RouteEntrySetType, err error) { + rt, errs := client.QueryRouteTableById(routeTableId) + if errs != nil { + return nil, errs + } + + for _, e := range rt.RouteEntrys.RouteEntry { + if strings.ToLower(string(e.DestinationCidrBlock)) == cidrBlock { + return &e, nil + } + } + return nil, nil +} diff --git a/builtin/providers/alicloud/struct_security_groups.go b/builtin/providers/alicloud/struct_security_groups.go new file mode 100644 index 0000000000..678f68f7d9 --- /dev/null +++ b/builtin/providers/alicloud/struct_security_groups.go @@ -0,0 +1,11 @@ +package alicloud + +// 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, v.(string)) + } + return vs +} diff --git a/builtin/providers/alicloud/tags.go b/builtin/providers/alicloud/tags.go new file mode 100644 index 0000000000..a2d906ab97 --- /dev/null +++ b/builtin/providers/alicloud/tags.go @@ -0,0 +1,126 @@ +package alicloud + +import ( + "fmt" + "log" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func String(v string) *string { + return &v +} + +// tagsSchema returns the schema to use for tags. +// +func tagsSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeMap, + //Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + } +} + +// setTags is a helper to set the tags for a resource. It expects the +// tags field to be named "tags" +func setTags(client *AliyunClient, resourceType ecs.TagResourceType, d *schema.ResourceData) error { + + conn := client.ecsconn + + if d.HasChange("tags") { + oraw, nraw := d.GetChange("tags") + o := oraw.(map[string]interface{}) + n := nraw.(map[string]interface{}) + create, remove := diffTags(tagsFromMap(o), tagsFromMap(n)) + + // Set tags + if len(remove) > 0 { + log.Printf("[DEBUG] Removing tags: %#v from %s", remove, d.Id()) + err := RemoveTags(conn, &RemoveTagsArgs{ + RegionId: client.Region, + ResourceId: d.Id(), + ResourceType: resourceType, + Tag: remove, + }) + if err != nil { + return fmt.Errorf("Remove tags got error: %s", err) + } + } + + if len(create) > 0 { + log.Printf("[DEBUG] Creating tags: %s for %s", create, d.Id()) + err := AddTags(conn, &AddTagsArgs{ + RegionId: client.Region, + ResourceId: d.Id(), + ResourceType: resourceType, + Tag: create, + }) + if err != nil { + return fmt.Errorf("Creating tags got error: %s", err) + } + } + } + + return nil +} + +// diffTags takes our tags locally and the ones remotely and returns +// the set of tags that must be created, and the set of tags that must +// be destroyed. +func diffTags(oldTags, newTags []Tag) ([]Tag, []Tag) { + // First, we're creating everything we have + create := make(map[string]interface{}) + for _, t := range newTags { + create[t.Key] = t.Value + } + + // Build the list of what to remove + var remove []Tag + for _, t := range oldTags { + old, ok := create[t.Key] + if !ok || old != t.Value { + // Delete it! + remove = append(remove, t) + } + } + + return tagsFromMap(create), remove +} + +// tagsFromMap returns the tags for the given map of data. +func tagsFromMap(m map[string]interface{}) []Tag { + result := make([]Tag, 0, len(m)) + for k, v := range m { + result = append(result, Tag{ + Key: k, + Value: v.(string), + }) + } + + return result +} + +func tagsToMap(tags []ecs.TagItemType) map[string]string { + result := make(map[string]string) + for _, t := range tags { + result[t.TagKey] = t.TagValue + } + + return result +} + +func tagsToString(tags []ecs.TagItemType) string { + result := make([]string, 0, len(tags)) + + for _, tag := range tags { + ecsTags := ecs.TagItemType{ + TagKey: tag.TagKey, + TagValue: tag.TagValue, + } + result = append(result, ecsTags.TagKey+":"+ecsTags.TagValue) + } + + return strings.Join(result, ",") +} diff --git a/builtin/providers/alicloud/validators.go b/builtin/providers/alicloud/validators.go new file mode 100644 index 0000000000..7eb85ed431 --- /dev/null +++ b/builtin/providers/alicloud/validators.go @@ -0,0 +1,453 @@ +package alicloud + +import ( + "fmt" + "net" + "strconv" + "strings" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/denverdino/aliyungo/slb" + "regexp" +) + +// common +func validateInstancePort(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 1 || value > 65535 { + errors = append(errors, fmt.Errorf( + "%q must be a valid instance port between 1 and 65535", + k)) + return + } + return +} + +func validateInstanceProtocol(v interface{}, k string) (ws []string, errors []error) { + protocal := v.(string) + if !isProtocalValid(protocal) { + errors = append(errors, fmt.Errorf( + "%q is an invalid value. Valid values are either http, https, tcp or udp", + k)) + return + } + return +} + +// ecs +func validateDiskCategory(v interface{}, k string) (ws []string, errors []error) { + category := ecs.DiskCategory(v.(string)) + if category != ecs.DiskCategoryCloud && category != ecs.DiskCategoryCloudEfficiency && category != ecs.DiskCategoryCloudSSD { + errors = append(errors, fmt.Errorf("%s must be one of %s %s %s", k, ecs.DiskCategoryCloud, ecs.DiskCategoryCloudEfficiency, ecs.DiskCategoryCloudSSD)) + } + + return +} + +func validateInstanceName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 128 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 128 characters", k)) + } + + if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") { + errors = append(errors, fmt.Errorf("%s cannot starts with http:// or https://", k)) + } + + return +} + +func validateInstanceDescription(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 256 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k)) + + } + return +} + +func validateDiskName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if value == "" { + return + } + + if len(value) < 2 || len(value) > 128 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 128 characters", k)) + } + + if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") { + errors = append(errors, fmt.Errorf("%s cannot starts with http:// or https://", k)) + } + + return +} + +func validateDiskDescription(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 256 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k)) + + } + return +} + +//security group +func validateSecurityGroupName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 128 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 128 characters", k)) + } + + if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") { + errors = append(errors, fmt.Errorf("%s cannot starts with http:// or https://", k)) + } + + return +} + +func validateSecurityGroupDescription(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 256 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k)) + + } + return +} + +func validateSecurityRuleType(v interface{}, k string) (ws []string, errors []error) { + rt := GroupRuleDirection(v.(string)) + if rt != GroupRuleIngress && rt != GroupRuleEgress { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRuleIngress, GroupRuleEgress)) + } + + return +} + +func validateSecurityRuleIpProtocol(v interface{}, k string) (ws []string, errors []error) { + pt := GroupRuleIpProtocol(v.(string)) + if pt != GroupRuleTcp && pt != GroupRuleUdp && pt != GroupRuleIcmp && pt != GroupRuleGre && pt != GroupRuleAll { + errors = append(errors, fmt.Errorf("%s must be one of %s %s %s %s %s", k, + GroupRuleTcp, GroupRuleUdp, GroupRuleIcmp, GroupRuleGre, GroupRuleAll)) + } + + return +} + +func validateSecurityRuleNicType(v interface{}, k string) (ws []string, errors []error) { + pt := GroupRuleNicType(v.(string)) + if pt != GroupRuleInternet && pt != GroupRuleIntranet { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRuleInternet, GroupRuleIntranet)) + } + + return +} + +func validateSecurityRulePolicy(v interface{}, k string) (ws []string, errors []error) { + pt := GroupRulePolicy(v.(string)) + if pt != GroupRulePolicyAccept && pt != GroupRulePolicyDrop { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRulePolicyAccept, GroupRulePolicyDrop)) + } + + return +} + +func validateSecurityPriority(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 1 || value > 100 { + errors = append(errors, fmt.Errorf( + "%q must be a valid authorization policy priority between 1 and 100", + k)) + return + } + return +} + +// validateCIDRNetworkAddress ensures that the string value is a valid CIDR that +// represents a network address - it adds an error otherwise +func validateCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + _, ipnet, err := net.ParseCIDR(value) + if err != nil { + errors = append(errors, fmt.Errorf( + "%q must contain a valid CIDR, got error parsing: %s", k, err)) + return + } + + if ipnet == nil || value != ipnet.String() { + errors = append(errors, fmt.Errorf( + "%q must contain a valid network CIDR, expected %q, got %q", + k, ipnet, value)) + } + + return +} + +func validateRouteEntryNextHopType(v interface{}, k string) (ws []string, errors []error) { + nht := ecs.NextHopType(v.(string)) + if nht != ecs.NextHopIntance && nht != ecs.NextHopTunnel { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, + ecs.NextHopIntance, ecs.NextHopTunnel)) + } + + return +} + +func validateSwitchCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + _, ipnet, err := net.ParseCIDR(value) + if err != nil { + errors = append(errors, fmt.Errorf( + "%q must contain a valid CIDR, got error parsing: %s", k, err)) + return + } + + if ipnet == nil || value != ipnet.String() { + errors = append(errors, fmt.Errorf( + "%q must contain a valid network CIDR, expected %q, got %q", + k, ipnet, value)) + return + } + + mark, _ := strconv.Atoi(strings.Split(ipnet.String(), "/")[1]) + if mark < 16 || mark > 29 { + errors = append(errors, fmt.Errorf( + "%q must contain a network CIDR which mark between 16 and 29", + k)) + } + + return +} + +// validateIoOptimized ensures that the string value is a valid IoOptimized that +// represents a IoOptimized - it adds an error otherwise +func validateIoOptimized(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + ioOptimized := ecs.IoOptimized(value) + if ioOptimized != ecs.IoOptimizedNone && + ioOptimized != ecs.IoOptimizedOptimized { + errors = append(errors, fmt.Errorf( + "%q must contain a valid IoOptimized, expected %s or %s, got %q", + k, ecs.IoOptimizedNone, ecs.IoOptimizedOptimized, ioOptimized)) + } + } + + return +} + +// validateInstanceNetworkType ensures that the string value is a classic or vpc +func validateInstanceNetworkType(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + network := InstanceNetWork(value) + if network != ClassicNet && + network != VpcNet { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceNetworkType, expected %s or %s, go %q", + k, ClassicNet, VpcNet, network)) + } + } + return +} + +func validateInstanceChargeType(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + chargeType := common.InstanceChargeType(value) + if chargeType != common.PrePaid && + chargeType != common.PostPaid { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceChargeType, expected %s or %s, got %q", + k, common.PrePaid, common.PostPaid, chargeType)) + } + } + + return +} + +func validateInternetChargeType(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + chargeType := common.InternetChargeType(value) + if chargeType != common.PayByBandwidth && + chargeType != common.PayByTraffic { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceChargeType, expected %s or %s, got %q", + k, common.PayByBandwidth, common.PayByTraffic, chargeType)) + } + } + + return +} + +func validateInternetMaxBandWidthOut(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 1 || value > 100 { + errors = append(errors, fmt.Errorf( + "%q must be a valid internet bandwidth out between 1 and 1000", + k)) + return + } + return +} + +// SLB +func validateSlbName(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + if len(value) < 1 || len(value) > 80 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer name characters between 1 and 80", + k)) + return + } + } + + return +} + +func validateSlbInternetChargeType(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + chargeType := common.InternetChargeType(value) + + if chargeType != "paybybandwidth" && + chargeType != "paybytraffic" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceChargeType, expected %s or %s, got %q", + k, "paybybandwidth", "paybytraffic", value)) + } + } + + return +} + +func validateSlbBandwidth(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 1 || value > 1000 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer bandwidth between 1 and 1000", + k)) + return + } + return +} + +func validateSlbListenerBandwidth(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if (value < 1 || value > 1000) && value != -1 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer bandwidth between 1 and 1000 or -1", + k)) + return + } + return +} + +func validateSlbListenerScheduler(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + scheduler := slb.SchedulerType(value) + + if scheduler != "wrr" && scheduler != "wlc" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid SchedulerType, expected %s or %s, got %q", + k, "wrr", "wlc", value)) + } + } + + return +} + +func validateSlbListenerStickySession(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + flag := slb.FlagType(value) + + if flag != "on" && flag != "off" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid StickySession, expected %s or %s, got %q", + k, "on", "off", value)) + } + } + return +} + +func validateSlbListenerStickySessionType(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + flag := slb.StickySessionType(value) + + if flag != "insert" && flag != "server" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid StickySessionType, expected %s or %s, got %q", + k, "insert", "server", value)) + } + } + return +} + +func validateSlbListenerCookie(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + flag := slb.StickySessionType(value) + + if flag != "insert" && flag != "server" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid StickySessionType, expected %s or %s, got %q", + k, "insert", "server", value)) + } + } + return +} + +func validateSlbListenerPersistenceTimeout(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 0 || value > 86400 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer persistence timeout between 0 and 86400", + k)) + return + } + return +} + +//data source validate func +//data_source_alicloud_image +func validateNameRegex(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if _, err := regexp.Compile(value); err != nil { + errors = append(errors, fmt.Errorf( + "%q contains an invalid regular expression: %s", + k, err)) + } + return +} + +func validateImageOwners(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + owners := ecs.ImageOwnerAlias(value) + if owners != ecs.ImageOwnerSystem && + owners != ecs.ImageOwnerSelf && + owners != ecs.ImageOwnerOthers && + owners != ecs.ImageOwnerMarketplace && + owners != ecs.ImageOwnerDefault { + errors = append(errors, fmt.Errorf( + "%q must contain a valid Image owner , expected %s, %s, %s, %s or %s, got %q", + k, ecs.ImageOwnerSystem, ecs.ImageOwnerSelf, ecs.ImageOwnerOthers, ecs.ImageOwnerMarketplace, ecs.ImageOwnerDefault, owners)) + } + } + return +} + +func validateRegion(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + region := common.Region(value) + var valid string + for _, re := range common.ValidRegions { + if region == re { + return + } + valid = valid + ", " + string(re) + } + errors = append(errors, fmt.Errorf( + "%q must contain a valid Region ID , expected %#v, got %q", + k, valid, value)) + + } + return +} diff --git a/builtin/providers/alicloud/validators_test.go b/builtin/providers/alicloud/validators_test.go new file mode 100644 index 0000000000..fa5a8aed00 --- /dev/null +++ b/builtin/providers/alicloud/validators_test.go @@ -0,0 +1,429 @@ +package alicloud + +import "testing" + +func TestValidateInstancePort(t *testing.T) { + validPorts := []int{1, 22, 80, 100, 8088, 65535} + for _, v := range validPorts { + _, errors := validateInstancePort(v, "instance_port") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance port number between 1 and 65535: %q", v, errors) + } + } + + invalidPorts := []int{-10, -1, 0} + for _, v := range invalidPorts { + _, errors := validateInstancePort(v, "instance_port") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance port number", v) + } + } +} + +func TestValidateInstanceProtocol(t *testing.T) { + validProtocals := []string{"http", "tcp", "https", "udp"} + for _, v := range validProtocals { + _, errors := validateInstanceProtocol(v, "instance_protocal") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance protocol: %q", v, errors) + } + } + + invalidProtocals := []string{"HTTP", "abc", "ecmp", "dubbo"} + for _, v := range invalidProtocals { + _, errors := validateInstanceProtocol(v, "instance_protocal") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance protocol", v) + } + } +} + +func TestValidateInstanceDiskCategory(t *testing.T) { + validDiskCategory := []string{"cloud", "cloud_efficiency", "cloud_ssd"} + for _, v := range validDiskCategory { + _, errors := validateDiskCategory(v, "instance_disk_category") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance disk category: %q", v, errors) + } + } + + invalidDiskCategory := []string{"all", "ephemeral", "ephemeral_ssd", "ALL", "efficiency"} + for _, v := range invalidDiskCategory { + _, errors := validateDiskCategory(v, "instance_disk_category") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance disk category", v) + } + } +} + +func TestValidateInstanceName(t *testing.T) { + validInstanceName := []string{"hi", "hi http://", "some word + any word &", "http", "中文"} + for _, v := range validInstanceName { + _, errors := validateInstanceName(v, "instance_name") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance name: %q", v, errors) + } + } + + invalidInstanceName := []string{"y", "http://", "https://", "+"} + for _, v := range invalidInstanceName { + _, errors := validateInstanceName(v, "instance_name") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance name", v) + } + } +} + +func TestValidateInstanceDescription(t *testing.T) { + validInstanceDescription := []string{"hi", "hi http://", "some word + any word &", "http://", "中文"} + for _, v := range validInstanceDescription { + _, errors := validateInstanceDescription(v, "instance_description") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance description: %q", v, errors) + } + } + + invalidvalidInstanceDescription := []string{"y", ""} + for _, v := range invalidvalidInstanceDescription { + _, errors := validateInstanceName(v, "instance_description") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance description", v) + } + } +} + +func TestValidateSecurityGroupName(t *testing.T) { + validSecurityGroupName := []string{"hi", "hi http://", "some word + any word &", "http", "中文", "12345"} + for _, v := range validSecurityGroupName { + _, errors := validateSecurityGroupName(v, "security_group_name") + if len(errors) != 0 { + t.Fatalf("%q should be a valid security group name: %q", v, errors) + } + } + + invalidSecurityGroupName := []string{"y", "http://", "https://", "+"} + for _, v := range invalidSecurityGroupName { + _, errors := validateSecurityGroupName(v, "security_group_name") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid security group name", v) + } + } +} + +func TestValidateSecurityGroupDescription(t *testing.T) { + validSecurityGroupDescription := []string{"hi", "hi http://", "some word + any word &", "http://", "中文"} + for _, v := range validSecurityGroupDescription { + _, errors := validateSecurityGroupDescription(v, "security_group_description") + if len(errors) != 0 { + t.Fatalf("%q should be a valid security group description: %q", v, errors) + } + } + + invalidSecurityGroupDescription := []string{"y", ""} + for _, v := range invalidSecurityGroupDescription { + _, errors := validateSecurityGroupDescription(v, "security_group_description") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid security group description", v) + } + } +} + +func TestValidateSecurityRuleType(t *testing.T) { + validSecurityRuleType := []string{"ingress", "egress"} + for _, v := range validSecurityRuleType { + _, errors := validateSecurityRuleType(v, "security_rule_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid security rule type: %q", v, errors) + } + } + + invalidSecurityRuleType := []string{"y", "gress", "in", "out"} + for _, v := range invalidSecurityRuleType { + _, errors := validateSecurityRuleType(v, "security_rule_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid security rule type", v) + } + } +} + +func TestValidateSecurityRuleIpProtocol(t *testing.T) { + validIpProtocol := []string{"tcp", "udp", "icmp", "gre", "all"} + for _, v := range validIpProtocol { + _, errors := validateSecurityRuleIpProtocol(v, "security_rule_ip_protocol") + if len(errors) != 0 { + t.Fatalf("%q should be a valid ip protocol: %q", v, errors) + } + } + + invalidIpProtocol := []string{"y", "ecmp", "http", "https"} + for _, v := range invalidIpProtocol { + _, errors := validateSecurityRuleIpProtocol(v, "security_rule_ip_protocol") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid ip protocol", v) + } + } +} + +func TestValidateSecurityRuleNicType(t *testing.T) { + validRuleNicType := []string{"intranet", "internet"} + for _, v := range validRuleNicType { + _, errors := validateSecurityRuleNicType(v, "security_rule_nic_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid nic type: %q", v, errors) + } + } + + invalidRuleNicType := []string{"inter", "ecmp", "http", "https"} + for _, v := range invalidRuleNicType { + _, errors := validateSecurityRuleNicType(v, "security_rule_nic_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid nic type", v) + } + } +} + +func TestValidateSecurityRulePolicy(t *testing.T) { + validRulePolicy := []string{"accept", "drop"} + for _, v := range validRulePolicy { + _, errors := validateSecurityRulePolicy(v, "security_rule_policy") + if len(errors) != 0 { + t.Fatalf("%q should be a valid security rule policy: %q", v, errors) + } + } + + invalidRulePolicy := []string{"inter", "ecmp", "http", "https"} + for _, v := range invalidRulePolicy { + _, errors := validateSecurityRulePolicy(v, "security_rule_policy") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid security rule policy", v) + } + } +} + +func TestValidateSecurityRulePriority(t *testing.T) { + validPriority := []int{1, 50, 100} + for _, v := range validPriority { + _, errors := validateSecurityPriority(v, "security_rule_priority") + if len(errors) != 0 { + t.Fatalf("%q should be a valid security rule priority: %q", v, errors) + } + } + + invalidPriority := []int{-1, 0, 101} + for _, v := range invalidPriority { + _, errors := validateSecurityPriority(v, "security_rule_priority") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid security rule priority", v) + } + } +} + +func TestValidateCIDRNetworkAddress(t *testing.T) { + validCIDRNetworkAddress := []string{"192.168.10.0/24", "0.0.0.0/0", "10.121.10.0/24"} + for _, v := range validCIDRNetworkAddress { + _, errors := validateCIDRNetworkAddress(v, "cidr_network_address") + if len(errors) != 0 { + t.Fatalf("%q should be a valid cidr network address: %q", v, errors) + } + } + + invalidCIDRNetworkAddress := []string{"1.2.3.4", "0x38732/21"} + for _, v := range invalidCIDRNetworkAddress { + _, errors := validateCIDRNetworkAddress(v, "cidr_network_address") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid cidr network address", v) + } + } +} + +func TestValidateRouteEntryNextHopType(t *testing.T) { + validNexthopType := []string{"Instance", "Tunnel"} + for _, v := range validNexthopType { + _, errors := validateRouteEntryNextHopType(v, "route_entry_nexthop_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid route entry nexthop type: %q", v, errors) + } + } + + invalidNexthopType := []string{"ri", "vpc"} + for _, v := range invalidNexthopType { + _, errors := validateRouteEntryNextHopType(v, "route_entry_nexthop_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid route entry nexthop type", v) + } + } +} + +func TestValidateSwitchCIDRNetworkAddress(t *testing.T) { + validSwitchCIDRNetworkAddress := []string{"192.168.10.0/24", "0.0.0.0/16", "127.0.0.0/29", "10.121.10.0/24"} + for _, v := range validSwitchCIDRNetworkAddress { + _, errors := validateSwitchCIDRNetworkAddress(v, "switch_cidr_network_address") + if len(errors) != 0 { + t.Fatalf("%q should be a valid switch cidr network address: %q", v, errors) + } + } + + invalidSwitchCIDRNetworkAddress := []string{"1.2.3.4", "0x38732/21", "10.121.10.0/15", "10.121.10.0/30", "256.121.10.0/22"} + for _, v := range invalidSwitchCIDRNetworkAddress { + _, errors := validateSwitchCIDRNetworkAddress(v, "switch_cidr_network_address") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid switch cidr network address", v) + } + } +} + +func TestValidateIoOptimized(t *testing.T) { + validIoOptimized := []string{"", "none", "optimized"} + for _, v := range validIoOptimized { + _, errors := validateIoOptimized(v, "ioOptimized") + if len(errors) != 0 { + t.Fatalf("%q should be a valid IoOptimized value: %q", v, errors) + } + } + + invalidIoOptimized := []string{"true", "ioOptimized"} + for _, v := range invalidIoOptimized { + _, errors := validateIoOptimized(v, "ioOptimized") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid IoOptimized value", v) + } + } +} + +func TestValidateInstanceNetworkType(t *testing.T) { + validInstanceNetworkType := []string{"", "classic", "vpc"} + for _, v := range validInstanceNetworkType { + _, errors := validateInstanceNetworkType(v, "instance_network_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance network type value: %q", v, errors) + } + } + + invalidInstanceNetworkType := []string{"Classic", "vswitch", "123"} + for _, v := range invalidInstanceNetworkType { + _, errors := validateInstanceNetworkType(v, "instance_network_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance network type value", v) + } + } +} + +func TestValidateInstanceChargeType(t *testing.T) { + validInstanceChargeType := []string{"", "PrePaid", "PostPaid"} + for _, v := range validInstanceChargeType { + _, errors := validateInstanceChargeType(v, "instance_charge_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance charge type value: %q", v, errors) + } + } + + invalidInstanceChargeType := []string{"prepay", "yearly", "123"} + for _, v := range invalidInstanceChargeType { + _, errors := validateInstanceChargeType(v, "instance_charge_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance charge type value", v) + } + } +} + +func TestValidateInternetChargeType(t *testing.T) { + validInternetChargeType := []string{"", "PayByBandwidth", "PayByTraffic"} + for _, v := range validInternetChargeType { + _, errors := validateInternetChargeType(v, "internet_charge_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid internet charge type value: %q", v, errors) + } + } + + invalidInternetChargeType := []string{"paybybandwidth", "paybytraffic", "123"} + for _, v := range invalidInternetChargeType { + _, errors := validateInternetChargeType(v, "internet_charge_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid internet charge type value", v) + } + } +} + +func TestValidateInternetMaxBandWidthOut(t *testing.T) { + validInternetMaxBandWidthOut := []int{1, 22, 100} + for _, v := range validInternetMaxBandWidthOut { + _, errors := validateInternetMaxBandWidthOut(v, "internet_max_bandwidth_out") + if len(errors) != 0 { + t.Fatalf("%q should be a valid internet max bandwidth out value: %q", v, errors) + } + } + + invalidInternetMaxBandWidthOut := []int{-2, 0, 101, 123} + for _, v := range invalidInternetMaxBandWidthOut { + _, errors := validateInternetMaxBandWidthOut(v, "internet_max_bandwidth_out") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid internet max bandwidth out value", v) + } + } +} + +func TestValidateSlbName(t *testing.T) { + validSlbName := []string{"h", "http://", "123", "hello, aliyun! "} + for _, v := range validSlbName { + _, errors := validateSlbName(v, "slb_name") + if len(errors) != 0 { + t.Fatalf("%q should be a valid slb name: %q", v, errors) + } + } + + // todo: add invalid case +} + +func TestValidateSlbInternetChargeType(t *testing.T) { + validSlbInternetChargeType := []string{"paybybandwidth", "paybytraffic"} + for _, v := range validSlbInternetChargeType { + _, errors := validateSlbInternetChargeType(v, "slb_internet_charge_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid slb internet charge type value: %q", v, errors) + } + } + + invalidSlbInternetChargeType := []string{"PayByBandwidth", "PayByTraffic"} + for _, v := range invalidSlbInternetChargeType { + _, errors := validateSlbInternetChargeType(v, "slb_internet_charge_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid slb internet charge type value", v) + } + } +} + +func TestValidateSlbBandwidth(t *testing.T) { + validSlbBandwidth := []int{1, 22, 1000} + for _, v := range validSlbBandwidth { + _, errors := validateSlbBandwidth(v, "slb_bandwidth") + if len(errors) != 0 { + t.Fatalf("%q should be a valid slb bandwidth value: %q", v, errors) + } + } + + invalidSlbBandwidth := []int{-2, 0, 1001} + for _, v := range invalidSlbBandwidth { + _, errors := validateSlbBandwidth(v, "slb_bandwidth") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid slb bandwidth value", v) + } + } +} + +func TestValidateSlbListenerBandwidth(t *testing.T) { + validSlbListenerBandwidth := []int{-1, 1, 22, 1000} + for _, v := range validSlbListenerBandwidth { + _, errors := validateSlbListenerBandwidth(v, "slb_bandwidth") + if len(errors) != 0 { + t.Fatalf("%q should be a valid slb listener bandwidth value: %q", v, errors) + } + } + + invalidSlbListenerBandwidth := []int{-2, 0, -10, 1001} + for _, v := range invalidSlbListenerBandwidth { + _, errors := validateSlbListenerBandwidth(v, "slb_bandwidth") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid slb listener bandwidth value", v) + } + } +} diff --git a/examples/alicloud-ecs-image/README.md b/examples/alicloud-ecs-image/README.md new file mode 100644 index 0000000000..a6a76d8e5a --- /dev/null +++ b/examples/alicloud-ecs-image/README.md @@ -0,0 +1,27 @@ +### ECS Example + +The example gains image info and use it to launche ECS instance, disk, and attached the disk on ECS. the count parameter in variables.tf can let you gain specify image and use it to create specify number ECS instances. + +### Get up and running + +* Planning phase + + terraform plan + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + .... + +* Apply phase + + terraform apply + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + .... + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-image/main.tf b/examples/alicloud-ecs-image/main.tf new file mode 100644 index 0000000000..743e6b9e1d --- /dev/null +++ b/examples/alicloud-ecs-image/main.tf @@ -0,0 +1,55 @@ +data "alicloud_images" "ecs_image" { + most_recent = "${var.most_recent}" + owners = "${var.image_owners}" + name_regex = "${var.name_regex}" +} + +resource "alicloud_security_group" "group" { + name = "${var.short_name}" + description = "New security group" +} + + +resource "alicloud_disk" "disk" { + availability_zone = "${var.availability_zones}" + category = "${var.disk_category}" + size = "${var.disk_size}" + count = "${var.count}" +} + +resource "alicloud_instance" "instance" { + instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + image_id = "${data.alicloud_images.ecs_image.images.0.id}" + instance_type = "${var.ecs_type}" + count = "${var.count}" + availability_zone = "${var.availability_zones}" + security_groups = ["${alicloud_security_group.group.*.id}"] + + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = "${var.internet_max_bandwidth_out}" + + io_optimized = "${var.io_optimized}" + + password = "${var.ecs_password}" + + allocate_public_ip = "${var.allocate_public_ip}" + + instance_charge_type = "PostPaid" + system_disk_category = "cloud_efficiency" + + + tags { + role = "${var.role}" + dc = "${var.datacenter}" + } + +} + +resource "alicloud_disk_attachment" "instance-attachment" { + count = "${var.count}" + disk_id = "${element(alicloud_disk.disk.*.id, count.index)}" + instance_id = "${element(alicloud_instance.instance.*.id, count.index)}" + device_name = "${var.device_name}" +} + diff --git a/examples/alicloud-ecs-image/outputs.tf b/examples/alicloud-ecs-image/outputs.tf new file mode 100644 index 0000000000..104f2389fc --- /dev/null +++ b/examples/alicloud-ecs-image/outputs.tf @@ -0,0 +1,15 @@ +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} + +output "ecs_public_ip" { + value = "${join(",", alicloud_instance.instance.*.public_ip)}" +} + +output "tags" { + value = "${jsonencode(alicloud_instance.instance.tags)}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-image/variables.tf b/examples/alicloud-ecs-image/variables.tf new file mode 100644 index 0000000000..35007e8c80 --- /dev/null +++ b/examples/alicloud-ecs-image/variables.tf @@ -0,0 +1,57 @@ +variable "count" { + default = "1" +} +variable "count_format" { + default = "%02d" +} +variable "most_recent" { + default = true +} + +variable "image_owners" { + default = "" +} + +variable "name_regex" { + default = "^centos_6\\w{1,5}[64].*" +} + +variable "role" { + default = "work" +} +variable "datacenter" { + default = "beijing" +} +variable "short_name" { + default = "hi" +} +variable "ecs_type" { + default = "ecs.n1.small" +} +variable "ecs_password" { + default = "Test12345" +} +variable "availability_zones" { + default = "cn-beijing-b" +} +variable "allocate_public_ip" { + default = true +} +variable "internet_charge_type" { + default = "PayByTraffic" +} +variable "internet_max_bandwidth_out" { + default = 5 +} +variable "io_optimized" { + default = "optimized" +} +variable "disk_category" { + default = "cloud_ssd" +} +variable "disk_size" { + default = "40" +} +variable "device_name" { + default = "/dev/xvdb" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-slb/README.md b/examples/alicloud-ecs-slb/README.md new file mode 100644 index 0000000000..db4b4631e3 --- /dev/null +++ b/examples/alicloud-ecs-slb/README.md @@ -0,0 +1,18 @@ +### ECS With SLB Example + +The example launches ECS, disk, and attached the disk on ECS. It also creates an SLB, and addition the ECS to backendServer. The variables.tf can let you create specify parameter instances, such as image_id, ecs_type etc. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-slb/main.tf b/examples/alicloud-ecs-slb/main.tf new file mode 100644 index 0000000000..fad5c77682 --- /dev/null +++ b/examples/alicloud-ecs-slb/main.tf @@ -0,0 +1,53 @@ +resource "alicloud_security_group" "group" { + name = "${var.short_name}" + description = "New security group" +} + +resource "alicloud_instance" "instance" { + instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + image_id = "${var.image_id}" + instance_type = "${var.ecs_type}" + count = "${var.count}" + availability_zone = "${var.availability_zones}" + security_groups = ["${alicloud_security_group.group.*.id}"] + + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = "${var.internet_max_bandwidth_out}" + + io_optimized = "${var.io_optimized}" + + password = "${var.ecs_password}" + + allocate_public_ip = "${var.allocate_public_ip}" + + instance_charge_type = "PostPaid" + system_disk_category = "cloud_efficiency" + + + tags { + role = "${var.role}" + dc = "${var.datacenter}" + } + +} + +resource "alicloud_slb" "instance" { + name = "${var.slb_name}" + internet_charge_type = "${var.slb_internet_charge_type}" + internet = "${var.internet}" + + listener = [ + { + "instance_port" = "2111" + "lb_port" = "21" + "lb_protocol" = "tcp" + "bandwidth" = "5" + }] +} + + +resource "alicloud_slb_attachment" "default" { + slb_id = "${alicloud_slb.instance.id}" + instances = ["${alicloud_instance.instance.*.id}"] +} \ No newline at end of file diff --git a/examples/alicloud-ecs-slb/outputs.tf b/examples/alicloud-ecs-slb/outputs.tf new file mode 100644 index 0000000000..9c7d3800ee --- /dev/null +++ b/examples/alicloud-ecs-slb/outputs.tf @@ -0,0 +1,20 @@ +output "slb_id" { + value = "${alicloud_slb.instance.id}" +} + +output "slbname" { + value = "${alicloud_slb.instance.name}" +} + +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} + +output "slb_backendserver" { + value = "${alicloud_slb_attachment.default.backend_servers}" +} + diff --git a/examples/alicloud-ecs-slb/variables.tf b/examples/alicloud-ecs-slb/variables.tf new file mode 100644 index 0000000000..20896631a2 --- /dev/null +++ b/examples/alicloud-ecs-slb/variables.tf @@ -0,0 +1,58 @@ +variable "count" { + default = "1" +} +variable "count_format" { + default = "%02d" +} +variable "image_id" { + default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" +} + +variable "role" { + default = "worder" +} +variable "datacenter" { + default = "beijing" +} +variable "short_name" { + default = "hi" +} +variable "ecs_type" { + default = "ecs.n1.small" +} +variable "ecs_password" { + default = "Test12345" +} +variable "availability_zones" { + default = "cn-beijing-b" +} +variable "ssh_username" { + default = "root" +} + +variable "allocate_public_ip" { + default = true +} + +variable "internet_charge_type" { + default = "PayByTraffic" +} + +variable "slb_internet_charge_type" { + default = "paybytraffic" +} +variable "internet_max_bandwidth_out" { + default = 5 +} + +variable "io_optimized" { + default = "optimized" +} + +variable "slb_name" { + default = "slb_worder" +} + +variable "internet" { + default = true +} diff --git a/examples/alicloud-ecs-special-sg/README.md b/examples/alicloud-ecs-special-sg/README.md new file mode 100644 index 0000000000..6e9e482a9f --- /dev/null +++ b/examples/alicloud-ecs-special-sg/README.md @@ -0,0 +1,20 @@ +### ECS With special SLB and SecurityGroup Example + +The example launches 6 ECS and create it on special SLB and securityGroup. +Also additional first and second instance to the SLB backend server. +The variables.tf can let you create specify parameter instances, such as image_id, ecs_type etc. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-special-sg/main.tf b/examples/alicloud-ecs-special-sg/main.tf new file mode 100644 index 0000000000..ba92486a6d --- /dev/null +++ b/examples/alicloud-ecs-special-sg/main.tf @@ -0,0 +1,39 @@ +provider "alicloud" { + alias = "bj" + region = "cn-beijing" +} + +resource "alicloud_instance" "instance" { + provider = "alicloud.bj" + instance_name = "website-${format(var.count_format, count.index+1)}" + host_name = "website-${format(var.count_format, count.index+1)}" + image_id = "centos7u2_64_40G_cloudinit_20160728.raw" + instance_type = "ecs.s2.large" + count = "6" + availability_zone = "cn-beijing-b" + security_groups = "${var.security_groups}" + + internet_charge_type = "PayByBandwidth" + + io_optimized = "none" + + password = "${var.ecs_password}" + + allocate_public_ip = "false" + + instance_charge_type = "PostPaid" + system_disk_category = "cloud" + + + tags { + env = "prod" + product = "website" + dc = "beijing" + } + +} + +resource "alicloud_slb_attachment" "foo" { + slb_id = "${var.slb_id}" + instances = ["${alicloud_instance.instance.0.id}", "${alicloud_instance.instance.1.id}"] +} \ No newline at end of file diff --git a/examples/alicloud-ecs-special-sg/outputs.tf b/examples/alicloud-ecs-special-sg/outputs.tf new file mode 100644 index 0000000000..33cb4a7e8f --- /dev/null +++ b/examples/alicloud-ecs-special-sg/outputs.tf @@ -0,0 +1,7 @@ +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} diff --git a/examples/alicloud-ecs-special-sg/variables.tf b/examples/alicloud-ecs-special-sg/variables.tf new file mode 100644 index 0000000000..694b8000fd --- /dev/null +++ b/examples/alicloud-ecs-special-sg/variables.tf @@ -0,0 +1,17 @@ +variable "count" { + default = "6" +} +variable "count_format" { + default = "%02d" +} + +variable "security_groups" { + type = "list" + default = ["sg-2zecd09tw30jo1c7ekdi"] +} +variable "ecs_password" { + default = "Test12345" +} +variable "slb_id"{ + default = "lb-2zel5fjqk1qgmwud7t3xb" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-userdata/README.md b/examples/alicloud-ecs-userdata/README.md new file mode 100644 index 0000000000..f58f16c0d9 --- /dev/null +++ b/examples/alicloud-ecs-userdata/README.md @@ -0,0 +1,17 @@ +### ECS with UserData Example + +Pass shell scripts to Ecs Instance by user_data parameter. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-userdata/main.tf b/examples/alicloud-ecs-userdata/main.tf new file mode 100644 index 0000000000..99376325f3 --- /dev/null +++ b/examples/alicloud-ecs-userdata/main.tf @@ -0,0 +1,37 @@ + +resource "alicloud_vpc" "default" { + name = "tf-vpc" + cidr_block = "${var.vpc_cidr}" +} + +resource "alicloud_vswitch" "vsw" { + vpc_id = "${alicloud_vpc.default.id}" + cidr_block = "${var.vswitch_cidr}" + availability_zone = "${var.zone}" +} + +resource "alicloud_security_group" "sg" { + name = "tf-sg" + description = "sg" + vpc_id = "${alicloud_vpc.default.id}" +} + +resource "alicloud_instance" "website" { + # cn-beijing + availability_zone = "${var.zone}" + vswitch_id = "${alicloud_vswitch.vsw.id}" + image_id = "${var.image}" + + # series II + instance_type = "${var.ecs_type}" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + + internet_charge_type = "PayByTraffic" + internet_max_bandwidth_out = 5 + allocate_public_ip = true + security_groups = ["${alicloud_security_group.sg.id}"] + instance_name = "test_foo" + + user_data = "${file("userdata.sh")}" +} diff --git a/examples/alicloud-ecs-userdata/outputs.tf b/examples/alicloud-ecs-userdata/outputs.tf new file mode 100644 index 0000000000..7115e9247f --- /dev/null +++ b/examples/alicloud-ecs-userdata/outputs.tf @@ -0,0 +1,7 @@ +output "hostname" { + value = "${alicloud_instance.website.instance_name}" +} + +output "ecs_id" { + value = "${alicloud_instance.website.id}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-userdata/userdata.sh b/examples/alicloud-ecs-userdata/userdata.sh new file mode 100644 index 0000000000..dba9b88914 --- /dev/null +++ b/examples/alicloud-ecs-userdata/userdata.sh @@ -0,0 +1,6 @@ +#!/bin/bash -v +apt-get update -y +apt-get install -y nginx > /tmp/nginx.log + +cd / +mkdir -p alicloud/go \ No newline at end of file diff --git a/examples/alicloud-ecs-userdata/variables.tf b/examples/alicloud-ecs-userdata/variables.tf new file mode 100644 index 0000000000..d8809ad838 --- /dev/null +++ b/examples/alicloud-ecs-userdata/variables.tf @@ -0,0 +1,19 @@ +variable "vpc_cidr" { + default = "172.16.0.0/12" +} + +variable "vswitch_cidr" { + default = "172.16.0.0/21" +} + +variable "zone" { + default = "cn-beijing-b" +} + +variable "image" { + default = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" +} + +variable "ecs_type" { + default = "ecs.n1.medium" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc-cluster/README.md b/examples/alicloud-ecs-vpc-cluster/README.md new file mode 100644 index 0000000000..62fec8cc0b --- /dev/null +++ b/examples/alicloud-ecs-vpc-cluster/README.md @@ -0,0 +1,19 @@ +### VPC Cluster Example + +The example launches VPC cluster, include VPC, VSwitch, Nategateway, ECS, SecurityGroups. the example used the "module" to create instances. The variables.tf can let you create specify parameter instances, such as image_id, ecs_type, count etc. + +### Get up and running + +* Planning phase + + terraform plan + + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc-cluster/main.tf b/examples/alicloud-ecs-vpc-cluster/main.tf new file mode 100644 index 0000000000..0ec8bf8a00 --- /dev/null +++ b/examples/alicloud-ecs-vpc-cluster/main.tf @@ -0,0 +1,62 @@ +provider "alicloud" { + region = "${var.region}" +} + +module "vpc" { + availability_zones = "${var.availability_zones}" + source = "../alicloud-vpc" + short_name = "${var.short_name}" + region = "${var.region}" +} + +module "security-groups" { + source = "../alicloud-vpc-cluster-sg" + short_name = "${var.short_name}" + vpc_id = "${module.vpc.vpc_id}" +} + +module "control-nodes" { + source = "../alicloud-ecs-vpc" + count = "${var.control_count}" + role = "control" + datacenter = "${var.datacenter}" + ecs_type = "${var.control_ecs_type}" + ecs_password = "${var.ecs_password}" + disk_size = "${var.control_disk_size}" + ssh_username = "${var.ssh_username}" + short_name = "${var.short_name}" + availability_zones = "${module.vpc.availability_zones}" + security_groups = ["${module.security-groups.control_security_group}"] + vswitch_id = "${module.vpc.vswitch_ids}" + internet_charge_type = "${var.internet_charge_type}" +} + +module "edge-nodes" { + source = "../alicloud-ecs-vpc" + count = "${var.edge_count}" + role = "edge" + datacenter = "${var.datacenter}" + ecs_type = "${var.edge_ecs_type}" + ecs_password = "${var.ecs_password}" + ssh_username = "${var.ssh_username}" + short_name = "${var.short_name}" + availability_zones = "${module.vpc.availability_zones}" + security_groups = ["${module.security-groups.worker_security_group}"] + vswitch_id = "${module.vpc.vswitch_ids}" + internet_charge_type = "${var.internet_charge_type}" +} + +module "worker-nodes" { + source = "../alicloud-ecs-vpc" + count = "${var.worker_count}" + role = "worker" + datacenter = "${var.datacenter}" + ecs_type = "${var.worker_ecs_type}" + ecs_password = "${var.ecs_password}" + ssh_username = "${var.ssh_username}" + short_name = "${var.short_name}" + availability_zones = "${module.vpc.availability_zones}" + security_groups = ["${module.security-groups.worker_security_group}"] + vswitch_id = "${module.vpc.vswitch_ids}" + internet_charge_type = "${var.internet_charge_type}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc-cluster/variables.tf b/examples/alicloud-ecs-vpc-cluster/variables.tf new file mode 100644 index 0000000000..7af6118624 --- /dev/null +++ b/examples/alicloud-ecs-vpc-cluster/variables.tf @@ -0,0 +1,59 @@ +variable "ecs_password" { + default = "Test12345" +} + +variable "control_count" { + default = "3" +} +variable "control_count_format" { + default = "%02d" +} +variable "control_ecs_type" { + default = "ecs.n1.medium" +} +variable "control_disk_size" { + default = "100" +} + +variable "edge_count" { + default = "2" +} +variable "edge_count_format" { + default = "%02d" +} +variable "edge_ecs_type" { + default = "ecs.n1.small" +} + +variable "worker_count" { + default = "1" +} +variable "worker_count_format" { + default = "%03d" +} +variable "worker_ecs_type" { + default = "ecs.n1.small" +} + +variable "short_name" { + default = "ali" +} +variable "ssh_username" { + default = "root" +} + +variable "region" { + default = "cn-beijing" +} + +variable "availability_zones" { + default = "cn-beijing-c" +} + +variable "internet_charge_type" { + default = "" +} + +variable "datacenter" { + default = "beijing" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc/README.md b/examples/alicloud-ecs-vpc/README.md new file mode 100644 index 0000000000..bfc92c494c --- /dev/null +++ b/examples/alicloud-ecs-vpc/README.md @@ -0,0 +1,31 @@ +### ECS In VPC Example + +The example launches ECS in VPC, vswitch_id parameter is the vswitch id from VPC. It also create disk, and attached the disk on ECS. The variables.tf can let you create specify parameter instances, such as image_id, ecs_type, count etc. + +### Get up and running + +* Planning phase + + terraform plan + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + var.vswitch_id + Enter a value: {vswitch_id} + .... + +* Apply phase + + terraform apply + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + var.vswitch_id + Enter a value: {vswitch_id} + .... + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc/main.tf b/examples/alicloud-ecs-vpc/main.tf new file mode 100644 index 0000000000..a4283ccd99 --- /dev/null +++ b/examples/alicloud-ecs-vpc/main.tf @@ -0,0 +1,45 @@ +resource "alicloud_disk" "disk" { + availability_zone = "${var.availability_zones}" + category = "${var.disk_category}" + size = "${var.disk_size}" + count = "${var.count}" +} + +resource "alicloud_instance" "instance" { + instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + image_id = "${var.image_id}" + instance_type = "${var.ecs_type}" + count = "${var.count}" + availability_zone = "${var.availability_zones}" + security_groups = ["${var.security_groups}"] + vswitch_id = "${var.vswitch_id}" + + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = "${var.internet_max_bandwidth_out}" + + io_optimized = "${var.io_optimized}" + + allocate_public_ip = "${var.allocate_public_ip}" + + password = "${var.ecs_password}" + + instance_charge_type = "${var.instance_charge_type}" + system_disk_category = "${var.system_disk_category}" + + + tags { + role = "${var.role}" + dc = "${var.datacenter}" + } + +} + +resource "alicloud_disk_attachment" "instance-attachment" { + count = "${var.count}" + disk_id = "${element(alicloud_disk.disk.*.id, count.index)}" + instance_id = "${element(alicloud_instance.instance.*.id, count.index)}" + device_name = "${var.device_name}" +} + + diff --git a/examples/alicloud-ecs-vpc/outputs.tf b/examples/alicloud-ecs-vpc/outputs.tf new file mode 100644 index 0000000000..d1b7c8a19d --- /dev/null +++ b/examples/alicloud-ecs-vpc/outputs.tf @@ -0,0 +1,7 @@ +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc/variables.tf b/examples/alicloud-ecs-vpc/variables.tf new file mode 100644 index 0000000000..67664e4255 --- /dev/null +++ b/examples/alicloud-ecs-vpc/variables.tf @@ -0,0 +1,67 @@ +variable "count" { + default = "1" +} +variable "count_format" { + default = "%02d" +} +variable "image_id" { + default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" +} + +variable "role" { +} +variable "datacenter" { +} +variable "short_name" { + default = "hi" +} +variable "ecs_type" { +} +variable "ecs_password" { +} +variable "availability_zones" { +} +variable "security_groups" { + type = "list" +} +variable "ssh_username" { + default = "root" +} + +//if instance_charge_type is "PrePaid", then must be set period, the value is 1 to 30, unit is month +variable "instance_charge_type" { + default = "PostPaid" +} + +variable "system_disk_category" { + default = "cloud_efficiency" +} + +variable "internet_charge_type" { + default = "PayByTraffic" +} +variable "internet_max_bandwidth_out" { + default = 5 +} + +variable "io_optimized" { + default = "optimized" +} + +variable "allocate_public_ip" { + default = true +} + +variable "disk_category" { + default = "cloud_ssd" +} +variable "disk_size" { + default = "40" +} +variable "device_name" { + default = "/dev/xvdb" +} + +variable "vswitch_id" { + default = "" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-zone-type/README.md b/examples/alicloud-ecs-zone-type/README.md new file mode 100644 index 0000000000..6f0b462218 --- /dev/null +++ b/examples/alicloud-ecs-zone-type/README.md @@ -0,0 +1,19 @@ +### Ecs Instance Type Data Source Example + +The example launches Ecs instance type Data Resource. Then set ecs parameter instance_type refer to the Data Resource config above. + +### Get up and running + +* Planning phase + + terraform plan + + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-zone-type/main.tf b/examples/alicloud-ecs-zone-type/main.tf new file mode 100644 index 0000000000..c3c21bc9ae --- /dev/null +++ b/examples/alicloud-ecs-zone-type/main.tf @@ -0,0 +1,42 @@ +data "alicloud_instance_types" "1c2g" { + cpu_core_count = 1 + memory_size = 2 + instance_type_family = "ecs.n1" +} + +data "alicloud_zones" "default" { + "available_instance_type"= "${data.alicloud_instance_types.4c8g.instance_types.0.id}" + "available_disk_category"= "${var.disk_category}" +} + +resource "alicloud_security_group" "group" { + name = "${var.short_name}" + description = "New security group" +} + +resource "alicloud_instance" "instance" { + instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + image_id = "${var.image_id}" + instance_type = "${data.alicloud_instance_types.1c2g.instance_types.0.id}" + count = "${var.count}" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" + security_groups = ["${alicloud_security_group.group.*.id}"] + + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = "${var.internet_max_bandwidth_out}" + + io_optimized = "${var.io_optimized}" + + password = "${var.ecs_password}" + + instance_charge_type = "PostPaid" + system_disk_category = "${var.disk_category}" + + + tags { + role = "${var.role}" + dc = "${var.datacenter}" + } + +} diff --git a/examples/alicloud-ecs-zone-type/outputs.tf b/examples/alicloud-ecs-zone-type/outputs.tf new file mode 100644 index 0000000000..104f2389fc --- /dev/null +++ b/examples/alicloud-ecs-zone-type/outputs.tf @@ -0,0 +1,15 @@ +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} + +output "ecs_public_ip" { + value = "${join(",", alicloud_instance.instance.*.public_ip)}" +} + +output "tags" { + value = "${jsonencode(alicloud_instance.instance.tags)}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-zone-type/variables.tf b/examples/alicloud-ecs-zone-type/variables.tf new file mode 100644 index 0000000000..1ba1cb5cc5 --- /dev/null +++ b/examples/alicloud-ecs-zone-type/variables.tf @@ -0,0 +1,35 @@ +variable "count" { + default = "1" +} +variable "count_format" { + default = "%02d" +} + +variable "image_id" { + default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" +} + +variable "disk_category" { + default = "cloud_ssd" +} +variable "role" { + default = "work" +} +variable "datacenter" { + default = "beijing" +} +variable "short_name" { + default = "hi" +} +variable "ecs_password" { + default = "Test12345" +} +variable "internet_charge_type" { + default = "PayByTraffic" +} +variable "internet_max_bandwidth_out" { + default = 5 +} +variable "io_optimized" { + default = "optimized" +} \ No newline at end of file diff --git a/examples/alicloud-ecs/README.md b/examples/alicloud-ecs/README.md new file mode 100644 index 0000000000..88d2887a34 --- /dev/null +++ b/examples/alicloud-ecs/README.md @@ -0,0 +1,27 @@ +### ECS Example + +The example launches ECS instance, disk, and attached the disk on ECS. the count parameter in variables.tf can let you create specify number ECS instances. + +### Get up and running + +* Planning phase + + terraform plan + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + .... + +* Apply phase + + terraform apply + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + .... + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs/main.tf b/examples/alicloud-ecs/main.tf new file mode 100644 index 0000000000..a6d39a059e --- /dev/null +++ b/examples/alicloud-ecs/main.tf @@ -0,0 +1,49 @@ +resource "alicloud_security_group" "group" { + name = "${var.short_name}" + description = "New security group" +} + + +resource "alicloud_disk" "disk" { + availability_zone = "${var.availability_zones}" + category = "${var.disk_category}" + size = "${var.disk_size}" + count = "${var.count}" +} + +resource "alicloud_instance" "instance" { + instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + image_id = "${var.image_id}" + instance_type = "${var.ecs_type}" + count = "${var.count}" + availability_zone = "${var.availability_zones}" + security_groups = ["${alicloud_security_group.group.*.id}"] + + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = "${var.internet_max_bandwidth_out}" + + password = "${var.ecs_password}" + + allocate_public_ip = "${var.allocate_public_ip}" + + io_optimized = "${var.io_optimized}" + + instance_charge_type = "PostPaid" + system_disk_category = "cloud_efficiency" + + + tags { + role = "${var.role}" + dc = "${var.datacenter}" + } + +} + +resource "alicloud_disk_attachment" "instance-attachment" { + count = "${var.count}" + disk_id = "${element(alicloud_disk.disk.*.id, count.index)}" + instance_id = "${element(alicloud_instance.instance.*.id, count.index)}" + device_name = "${var.device_name}" +} + diff --git a/examples/alicloud-ecs/outputs.tf b/examples/alicloud-ecs/outputs.tf new file mode 100644 index 0000000000..104f2389fc --- /dev/null +++ b/examples/alicloud-ecs/outputs.tf @@ -0,0 +1,15 @@ +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} + +output "ecs_public_ip" { + value = "${join(",", alicloud_instance.instance.*.public_ip)}" +} + +output "tags" { + value = "${jsonencode(alicloud_instance.instance.tags)}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs/variables.tf b/examples/alicloud-ecs/variables.tf new file mode 100644 index 0000000000..c663f8dd3e --- /dev/null +++ b/examples/alicloud-ecs/variables.tf @@ -0,0 +1,51 @@ +variable "count" { + default = "1" +} +variable "count_format" { + default = "%02d" +} +variable "image_id" { + default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" +} + +variable "role" { + default = "work" +} +variable "datacenter" { + default = "beijing" +} +variable "short_name" { + default = "hi" +} +variable "ecs_type" { + default = "ecs.n1.small" +} +variable "ecs_password" { + default = "Test12345" +} +variable "availability_zones" { + default = "cn-beijing-b" +} +variable "allocate_public_ip" { + default = true +} +variable "internet_charge_type" { + default = "PayByTraffic" +} +variable "internet_max_bandwidth_out" { + default = 5 +} + +variable "io_optimized" { + default = "optimized" +} + +variable "disk_category" { + default = "cloud_ssd" +} +variable "disk_size" { + default = "40" +} +variable "device_name" { + default = "/dev/xvdb" +} \ No newline at end of file diff --git a/examples/alicloud-security-group-rule/main.tf b/examples/alicloud-security-group-rule/main.tf new file mode 100644 index 0000000000..706ee08630 --- /dev/null +++ b/examples/alicloud-security-group-rule/main.tf @@ -0,0 +1,14 @@ +resource "alicloud_security_group" "default" { + name = "${var.security_group_name}" +} + +resource "alicloud_security_group_rule" "allow_all_tcp" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "${var.nic_type}" + policy = "accept" + port_range = "1/65535" + priority = 1 + security_group_id = "${alicloud_security_group.default.id}" + cidr_ip = "0.0.0.0/0" +} \ No newline at end of file diff --git a/examples/alicloud-security-group-rule/outputs.tf b/examples/alicloud-security-group-rule/outputs.tf new file mode 100644 index 0000000000..79ab88b6db --- /dev/null +++ b/examples/alicloud-security-group-rule/outputs.tf @@ -0,0 +1,15 @@ +output "rule_id" { + value = "${alicloud_security_group_rule.allow_all_tcp.id}" +} + +output "rule_type" { + value = "${alicloud_security_group_rule.allow_all_tcp.type}" +} + +output "port_range" { + value = "${alicloud_security_group_rule.allow_all_tcp.port_range}" +} + +output "ip_protocol" { + value = "${alicloud_security_group_rule.allow_all_tcp.ip_protocol}" +} \ No newline at end of file diff --git a/examples/alicloud-security-group-rule/variables.tf b/examples/alicloud-security-group-rule/variables.tf new file mode 100644 index 0000000000..91aa57d4b6 --- /dev/null +++ b/examples/alicloud-security-group-rule/variables.tf @@ -0,0 +1,7 @@ +variable "security_group_name" { + default = "default-sg" +} + +variable "nic_type" { + default = "internet" +} \ No newline at end of file diff --git a/examples/alicloud-security-group/README.md b/examples/alicloud-security-group/README.md new file mode 100644 index 0000000000..b54db7ad25 --- /dev/null +++ b/examples/alicloud-security-group/README.md @@ -0,0 +1,19 @@ +### SecurityGroup With Vpc Example + +The example create SecurityGroup for specify VPC. + +### Get up and running + +* Planning phase + + terraform plan + + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-security-group/main.tf b/examples/alicloud-security-group/main.tf new file mode 100644 index 0000000000..afdf0577f2 --- /dev/null +++ b/examples/alicloud-security-group/main.tf @@ -0,0 +1,5 @@ +resource "alicloud_security_group" "group" { + name = "${var.short_name}" + description = "New security group" + vpc_id = "${var.vpc_id}" +} diff --git a/examples/alicloud-security-group/outputs.tf b/examples/alicloud-security-group/outputs.tf new file mode 100644 index 0000000000..bd67a462f4 --- /dev/null +++ b/examples/alicloud-security-group/outputs.tf @@ -0,0 +1,3 @@ +output "security_group" { + value = "${alicloud_security_group.group.id}" +} \ No newline at end of file diff --git a/examples/alicloud-security-group/variables.tf b/examples/alicloud-security-group/variables.tf new file mode 100644 index 0000000000..e37d4e0d23 --- /dev/null +++ b/examples/alicloud-security-group/variables.tf @@ -0,0 +1,4 @@ +variable "short_name" { +} +variable "vpc_id" { +} diff --git a/examples/alicloud-slb-vpc/README.md b/examples/alicloud-slb-vpc/README.md new file mode 100644 index 0000000000..809ed9589c --- /dev/null +++ b/examples/alicloud-slb-vpc/README.md @@ -0,0 +1,19 @@ +### SLB With VPC Example + +The example create SLB in special VPC, The variables.tf can let you create specify parameter instances, such as vpc_id, vswitch_id. + +### Get up and running + +* Planning phase + + terraform plan + + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-slb-vpc/main.tf b/examples/alicloud-slb-vpc/main.tf new file mode 100644 index 0000000000..c54d16d005 --- /dev/null +++ b/examples/alicloud-slb-vpc/main.tf @@ -0,0 +1,27 @@ +resource "alicloud_vpc" "main" { + name = "${var.long_name}" + cidr_block = "${var.vpc_cidr}" +} + +resource "alicloud_vswitch" "main" { + vpc_id = "${alicloud_vpc.main.id}" + count = "${length(split(",", var.availability_zones))}" + cidr_block = "${lookup(var.cidr_blocks, "az${count.index}")}" + availability_zone = "${element(split(",", var.availability_zones), count.index)}" + depends_on = [ + "alicloud_vpc.main"] +} + +resource "alicloud_slb" "instance" { + name = "${var.name}" + vswitch_id = "${alicloud_vswitch.main.id}" + internet_charge_type = "${var.internet_charge_type}" + listener = [ + { + "instance_port" = "2111" + "lb_port" = "21" + "lb_protocol" = "tcp" + "bandwidth" = "5" + }] +} + diff --git a/examples/alicloud-slb-vpc/outputs.tf b/examples/alicloud-slb-vpc/outputs.tf new file mode 100644 index 0000000000..d8e1cc93e8 --- /dev/null +++ b/examples/alicloud-slb-vpc/outputs.tf @@ -0,0 +1,7 @@ +output "slb_id" { + value = "${alicloud_slb.instance.id}" +} + +output "slbname" { + value = "${alicloud_slb.instance.name}" +} \ No newline at end of file diff --git a/examples/alicloud-slb-vpc/variables.tf b/examples/alicloud-slb-vpc/variables.tf new file mode 100644 index 0000000000..7e9f9d390e --- /dev/null +++ b/examples/alicloud-slb-vpc/variables.tf @@ -0,0 +1,30 @@ +variable "availability_zones" { + default = "cn-beijing-c" +} + +variable "name" { + default = "slb_alicloud" +} + +variable "cidr_blocks" { + type = "map" + default = { + az0 = "10.1.1.0/24" + az1 = "10.1.2.0/24" + az2 = "10.1.3.0/24" + } +} + +variable "internet_charge_type" { + default = "paybytraffic" +} + +variable "long_name" { + default = "alicloud" +} +variable "vpc_cidr" { + default = "10.1.0.0/21" +} +variable "region" { + default = "cn-beijing" +} \ No newline at end of file diff --git a/examples/alicloud-slb/README.md b/examples/alicloud-slb/README.md new file mode 100644 index 0000000000..370476f13d Binary files /dev/null and b/examples/alicloud-slb/README.md differ diff --git a/examples/alicloud-slb/main.tf b/examples/alicloud-slb/main.tf new file mode 100644 index 0000000000..67f5e0cf60 --- /dev/null +++ b/examples/alicloud-slb/main.tf @@ -0,0 +1,25 @@ +resource "alicloud_slb" "instance" { + name = "${var.slb_name}" + internet_charge_type = "${var.internet_charge_type}" + internet = "${var.internet}" + + listener = [ + { + "instance_port" = "2111" + "lb_port" = "21" + "lb_protocol" = "tcp" + "bandwidth" = "5" + }, + { + "instance_port" = "8000" + "lb_port" = "80" + "lb_protocol" = "http" + "bandwidth" = "5" + }, + { + "instance_port" = "1611" + "lb_port" = "161" + "lb_protocol" = "udp" + "bandwidth" = "5" + }] +} diff --git a/examples/alicloud-slb/outputs.tf b/examples/alicloud-slb/outputs.tf new file mode 100644 index 0000000000..d8e1cc93e8 --- /dev/null +++ b/examples/alicloud-slb/outputs.tf @@ -0,0 +1,7 @@ +output "slb_id" { + value = "${alicloud_slb.instance.id}" +} + +output "slbname" { + value = "${alicloud_slb.instance.name}" +} \ No newline at end of file diff --git a/examples/alicloud-slb/variables.tf b/examples/alicloud-slb/variables.tf new file mode 100644 index 0000000000..7a741b295c --- /dev/null +++ b/examples/alicloud-slb/variables.tf @@ -0,0 +1,11 @@ +variable "slb_name" { + default = "slb_worder" +} + +variable "internet_charge_type" { + default = "paybytraffic" +} + +variable "internet" { + default = true +} diff --git a/examples/alicloud-vpc-cluster-sg/README.md b/examples/alicloud-vpc-cluster-sg/README.md new file mode 100644 index 0000000000..5ef00e5f63 --- /dev/null +++ b/examples/alicloud-vpc-cluster-sg/README.md @@ -0,0 +1,19 @@ +### SecurityGroups With Vpc Example + +The example create SecurityGroups for specify VPC Clusters. + +### Get up and running + +* Planning phase + + terraform plan + + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-vpc-cluster-sg/main.tf b/examples/alicloud-vpc-cluster-sg/main.tf new file mode 100644 index 0000000000..1e6575aeb9 --- /dev/null +++ b/examples/alicloud-vpc-cluster-sg/main.tf @@ -0,0 +1,23 @@ +resource "alicloud_security_group" "default" { + name = "${var.short_name}-default" + description = "Default security group for VPC" + vpc_id = "${var.vpc_id}" +} + +resource "alicloud_security_group" "control" { + name = "${var.short_name}-control" + description = "Allow inboud traffic for control nodes" + vpc_id = "${var.vpc_id}" +} + +resource "alicloud_security_group" "edge" { + name = "${var.short_name}-edge" + description = "Allow inboud traffic for edge routing" + vpc_id = "${var.vpc_id}" +} + +resource "alicloud_security_group" "worker" { + name = "${var.short_name}-worker" + description = "Allow inboud traffic for worker nodes" + vpc_id = "${var.vpc_id}" +} diff --git a/examples/alicloud-vpc-cluster-sg/outputs.tf b/examples/alicloud-vpc-cluster-sg/outputs.tf new file mode 100644 index 0000000000..f2e6408069 --- /dev/null +++ b/examples/alicloud-vpc-cluster-sg/outputs.tf @@ -0,0 +1,15 @@ +output "default_security_group" { + value = "${alicloud_security_group.default.id}" +} + +output "edge_security_group" { + value = "${alicloud_security_group.edge.id}" +} + +output "control_security_group" { + value = "${alicloud_security_group.control.id}" +} + +output "worker_security_group" { + value = "${alicloud_security_group.worker.id}" +} diff --git a/examples/alicloud-vpc-cluster-sg/variables.tf b/examples/alicloud-vpc-cluster-sg/variables.tf new file mode 100644 index 0000000000..e37d4e0d23 --- /dev/null +++ b/examples/alicloud-vpc-cluster-sg/variables.tf @@ -0,0 +1,4 @@ +variable "short_name" { +} +variable "vpc_id" { +} diff --git a/examples/alicloud-vpc-multi-region/README.md b/examples/alicloud-vpc-multi-region/README.md new file mode 100644 index 0000000000..e45fe46661 --- /dev/null +++ b/examples/alicloud-vpc-multi-region/README.md @@ -0,0 +1,18 @@ +### VPC Example + +The example will create VPC in multi region use "alias" characters. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-vpc-multi-region/main.tf b/examples/alicloud-vpc-multi-region/main.tf new file mode 100644 index 0000000000..a3f946cc45 --- /dev/null +++ b/examples/alicloud-vpc-multi-region/main.tf @@ -0,0 +1,21 @@ +provider "alicloud" { + alias = "bj" + region = "${var.region1}" +} + +provider "alicloud" { + alias = "hz" + region = "${var.region2}" +} + +resource "alicloud_vpc" "work" { + provider = "alicloud.hz" + name = "${var.long_name}" + cidr_block = "${var.vpc_cidr}" +} + +resource "alicloud_vpc" "control" { + provider = "alicloud.bj" + name = "${var.long_name}" + cidr_block = "${var.vpc_cidr}" +} diff --git a/examples/alicloud-vpc-multi-region/outputs.tf b/examples/alicloud-vpc-multi-region/outputs.tf new file mode 100644 index 0000000000..d46e24927a --- /dev/null +++ b/examples/alicloud-vpc-multi-region/outputs.tf @@ -0,0 +1,7 @@ +output "vpc_work_id" { + value = "${alicloud_vpc.work.id}" +} + +output "vpc_control_id" { + value = "${alicloud_vpc.control.id}" +} \ No newline at end of file diff --git a/examples/alicloud-vpc-multi-region/variables.tf b/examples/alicloud-vpc-multi-region/variables.tf new file mode 100644 index 0000000000..c896ae47a0 --- /dev/null +++ b/examples/alicloud-vpc-multi-region/variables.tf @@ -0,0 +1,12 @@ +variable "long_name" { + default = "alicloud" +} +variable "vpc_cidr" { + default = "10.1.0.0/21" +} +variable "region1" { + default = "cn-beijing" +} +variable "region2" { + default = "cn-hangzhou" +} \ No newline at end of file diff --git a/examples/alicloud-vpc-route-entry/main.tf b/examples/alicloud-vpc-route-entry/main.tf new file mode 100644 index 0000000000..9f6876b29b --- /dev/null +++ b/examples/alicloud-vpc-route-entry/main.tf @@ -0,0 +1,55 @@ +resource "alicloud_vpc" "default" { + name = "tf_vpc" + cidr_block = "${var.vpc_cidr}" +} + +resource "alicloud_vswitch" "default" { + vpc_id = "${alicloud_vpc.default.id}" + cidr_block = "${var.vswitch_cidr}" + availability_zone = "${var.zone_id}" +} + +resource "alicloud_route_entry" "default" { + router_id = "${alicloud_vpc.default.router_id}" + route_table_id = "${alicloud_vpc.default.router_table_id}" + destination_cidrblock = "${var.entry_cidr}" + nexthop_type = "Instance" + nexthop_id = "${alicloud_instance.snat.id}" +} + +resource "alicloud_security_group" "sg" { + name = "tf_sg" + description = "tf_sg" + vpc_id = "${alicloud_vpc.default.id}" +} + +resource "alicloud_security_group_rule" "ssh" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "${var.rule_policy}" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.sg.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_instance" "snat" { + # cn-beijing + availability_zone = "${var.zone_id}" + security_groups = ["${alicloud_security_group.sg.id}"] + + vswitch_id = "${alicloud_vswitch.default.id}" + allocate_public_ip = true + + # series II + instance_charge_type = "PostPaid" + instance_type = "${var.instance_type}" + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = 5 + io_optimized = "${var.io_optimized}" + + system_disk_category = "cloud_efficiency" + image_id = "${var.image_id}" + instance_name = "tf_snat" +} \ No newline at end of file diff --git a/examples/alicloud-vpc-route-entry/ouputs.tf b/examples/alicloud-vpc-route-entry/ouputs.tf new file mode 100644 index 0000000000..37e60e250c --- /dev/null +++ b/examples/alicloud-vpc-route-entry/ouputs.tf @@ -0,0 +1,15 @@ +output "route_table_id" { + value = "${alicloud_route_entry.default.route_table_id}" +} + +output "router_id" { + value = "${alicloud_route_entry.default.router_id}" +} + +output "nexthop_type" { + value = "${alicloud_route_entry.default.nexthop_type}" +} + +output "nexthop_id" { + value = "${alicloud_route_entry.default.nexthop_id}" +} \ No newline at end of file diff --git a/examples/alicloud-vpc-route-entry/variables.tf b/examples/alicloud-vpc-route-entry/variables.tf new file mode 100644 index 0000000000..4708e266f5 --- /dev/null +++ b/examples/alicloud-vpc-route-entry/variables.tf @@ -0,0 +1,28 @@ + +variable "vpc_cidr" { + default = "10.1.0.0/21" +} +variable "vswitch_cidr" { + default = "10.1.1.0/24" +} +variable "zone_id" { + default = "cn-beijing-c" +} +variable "entry_cidr" { + default = "172.11.1.1/32" +} +variable "rule_policy" { + default = "accept" +} +variable "instance_type" { + default = "ecs.n1.small" +} +variable "image_id" { + default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" +} +variable "internet_charge_type" { + default = "PayByTraffic" +} +variable "io_optimized" { + default = "optimized" +} \ No newline at end of file diff --git a/examples/alicloud-vpc/README.md b/examples/alicloud-vpc/README.md new file mode 100644 index 0000000000..392e0abc8d --- /dev/null +++ b/examples/alicloud-vpc/README.md @@ -0,0 +1,18 @@ +### VPC Example + +The example create VPC, VSwitch, Natgateway. The variables.tf can let you create specify parameter instances, such as availability_zone, cidr_block etc. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-vpc/main.tf b/examples/alicloud-vpc/main.tf new file mode 100644 index 0000000000..78d58b17cc --- /dev/null +++ b/examples/alicloud-vpc/main.tf @@ -0,0 +1,28 @@ +resource "alicloud_vpc" "main" { + name = "${var.long_name}" + cidr_block = "${var.vpc_cidr}" +} + +resource "alicloud_vswitch" "main" { + vpc_id = "${alicloud_vpc.main.id}" + count = "${length(split(",", var.availability_zones))}" + cidr_block = "${lookup(var.cidr_blocks, "az${count.index}")}" + availability_zone = "${var.availability_zones}" + depends_on = [ + "alicloud_vpc.main"] +} + +resource "alicloud_nat_gateway" "main" { + vpc_id = "${alicloud_vpc.main.id}" + spec = "Small" + bandwidth_packages = [ + { + ip_count = 1 + bandwidth = 5 + zone = "${var.availability_zones}" + } + ] + depends_on = [ + "alicloud_vswitch.main"] +} + diff --git a/examples/alicloud-vpc/outputs.tf b/examples/alicloud-vpc/outputs.tf new file mode 100644 index 0000000000..06809a16a8 --- /dev/null +++ b/examples/alicloud-vpc/outputs.tf @@ -0,0 +1,11 @@ +output "vpc_id" { + value = "${alicloud_vpc.main.id}" +} + +output "vswitch_ids" { + value = "${join(",", alicloud_vswitch.main.*.id)}" +} + +output "availability_zones" { + value = "${join(",",alicloud_vswitch.main.*.availability_zone)}" +} diff --git a/examples/alicloud-vpc/variables.tf b/examples/alicloud-vpc/variables.tf new file mode 100644 index 0000000000..2b30cde057 --- /dev/null +++ b/examples/alicloud-vpc/variables.tf @@ -0,0 +1,25 @@ +variable "availability_zones" { + default = "cn-beijing-c" +} + +variable "cidr_blocks" { + type = "map" + default = { + az0 = "10.1.1.0/24" + az1 = "10.1.2.0/24" + az2 = "10.1.3.0/24" + } +} + +variable "long_name" { + default = "alicloud" +} +variable "short_name" { + default = "ali" +} +variable "vpc_cidr" { + default = "10.1.0.0/21" +} +variable "region" { + default = "cn-beijing" +} \ No newline at end of file diff --git a/vendor/github.com/denverdino/aliyungo/LICENSE.txt b/vendor/github.com/denverdino/aliyungo/LICENSE.txt new file mode 100644 index 0000000000..9182971332 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/LICENSE.txt @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015-2015 Li Yi (denverdino@gmail.com). + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/denverdino/aliyungo/common/client.go b/vendor/github.com/denverdino/aliyungo/common/client.go new file mode 100755 index 0000000000..1fa43afaeb --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/client.go @@ -0,0 +1,225 @@ +package common + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "log" + "net/http" + "net/url" + "time" + "strings" + + "github.com/denverdino/aliyungo/util" +) + +// A Client represents a client of ECS services +type Client struct { + AccessKeyId string //Access Key Id + AccessKeySecret string //Access Key Secret + debug bool + httpClient *http.Client + endpoint string + version string +} + +// NewClient creates a new instance of ECS client +func (client *Client) Init(endpoint, version, accessKeyId, accessKeySecret string) { + client.AccessKeyId = accessKeyId + client.AccessKeySecret = accessKeySecret + "&" + client.debug = false + client.httpClient = &http.Client{} + client.endpoint = endpoint + client.version = version +} + +// SetEndpoint sets custom endpoint +func (client *Client) SetEndpoint(endpoint string) { + client.endpoint = endpoint +} + +// SetEndpoint sets custom version +func (client *Client) SetVersion(version string) { + client.version = version +} + +// SetAccessKeyId sets new AccessKeyId +func (client *Client) SetAccessKeyId(id string) { + client.AccessKeyId = id +} + +// SetAccessKeySecret sets new AccessKeySecret +func (client *Client) SetAccessKeySecret(secret string) { + client.AccessKeySecret = secret + "&" +} + +// SetDebug sets debug mode to log the request/response message +func (client *Client) SetDebug(debug bool) { + client.debug = debug +} + +// Invoke sends the raw HTTP request for ECS services +func (client *Client) Invoke(action string, args interface{}, response interface{}) error { + + request := Request{} + request.init(client.version, action, client.AccessKeyId) + + query := util.ConvertToQueryValues(request) + util.SetQueryValues(args, &query) + + // Sign request + signature := util.CreateSignatureForRequest(ECSRequestMethod, &query, client.AccessKeySecret) + + // Generate the request URL + requestURL := client.endpoint + "?" + query.Encode() + "&Signature=" + url.QueryEscape(signature) + + httpReq, err := http.NewRequest(ECSRequestMethod, requestURL, nil) + + if err != nil { + return GetClientError(err) + } + + // TODO move to util and add build val flag + httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version) + + t0 := time.Now() + httpResp, err := client.httpClient.Do(httpReq) + t1 := time.Now() + if err != nil { + return GetClientError(err) + } + statusCode := httpResp.StatusCode + + if client.debug { + log.Printf("Invoke %s %s %d (%v)", ECSRequestMethod, requestURL, statusCode, t1.Sub(t0)) + } + + defer httpResp.Body.Close() + body, err := ioutil.ReadAll(httpResp.Body) + + if err != nil { + return GetClientError(err) + } + + if client.debug { + var prettyJSON bytes.Buffer + err = json.Indent(&prettyJSON, body, "", " ") + log.Println(string(prettyJSON.Bytes())) + } + + if statusCode >= 400 && statusCode <= 599 { + errorResponse := ErrorResponse{} + err = json.Unmarshal(body, &errorResponse) + ecsError := &Error{ + ErrorResponse: errorResponse, + StatusCode: statusCode, + } + return ecsError + } + + err = json.Unmarshal(body, response) + //log.Printf("%++v", response) + if err != nil { + return GetClientError(err) + } + + return nil +} + +// Invoke sends the raw HTTP request for ECS services +//改进了一下上面那个方法,可以使用各种Http方法 +func (client *Client) InvokeByAnyMethod(method, action string, args interface{}, response interface{}) error { + + request := Request{} + request.init(client.version, action, client.AccessKeyId) + + data := util.ConvertToQueryValues(request) + util.SetQueryValues(args, &data) + + // Sign request + signature := util.CreateSignatureForRequest(method, &data, client.AccessKeySecret) + + data.Add("Signature", signature) + + // Generate the request URL + var ( + httpReq *http.Request + err error + ) + if method == http.MethodGet { + requestURL := client.endpoint + "?" + data.Encode() + httpReq, err = http.NewRequest(method, requestURL, nil) + } else { + httpReq, err = http.NewRequest(method, client.endpoint, strings.NewReader(data.Encode())) + httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") + } + + if err != nil { + return GetClientError(err) + } + + // TODO move to util and add build val flag + httpReq.Header.Set("X-SDK-Client", `AliyunGO/` + Version) + + t0 := time.Now() + httpResp, err := client.httpClient.Do(httpReq) + t1 := time.Now() + if err != nil { + return GetClientError(err) + } + statusCode := httpResp.StatusCode + + if client.debug { + log.Printf("Invoke %s %s %d (%v) %v", ECSRequestMethod, client.endpoint, statusCode, t1.Sub(t0), data.Encode()) + } + + defer httpResp.Body.Close() + body, err := ioutil.ReadAll(httpResp.Body) + + if err != nil { + return GetClientError(err) + } + + if client.debug { + var prettyJSON bytes.Buffer + err = json.Indent(&prettyJSON, body, "", " ") + log.Println(string(prettyJSON.Bytes())) + } + + if statusCode >= 400 && statusCode <= 599 { + errorResponse := ErrorResponse{} + err = json.Unmarshal(body, &errorResponse) + ecsError := &Error{ + ErrorResponse: errorResponse, + StatusCode: statusCode, + } + return ecsError + } + + err = json.Unmarshal(body, response) + //log.Printf("%++v", response) + if err != nil { + return GetClientError(err) + } + + return nil +} + +// GenerateClientToken generates the Client Token with random string +func (client *Client) GenerateClientToken() string { + return util.CreateRandomString() +} + +func GetClientErrorFromString(str string) error { + return &Error{ + ErrorResponse: ErrorResponse{ + Code: "AliyunGoClientFailure", + Message: str, + }, + StatusCode: -1, + } +} + +func GetClientError(err error) error { + return GetClientErrorFromString(err.Error()) +} diff --git a/vendor/github.com/denverdino/aliyungo/common/regions.go b/vendor/github.com/denverdino/aliyungo/common/regions.go new file mode 100644 index 0000000000..781a727bc1 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/regions.go @@ -0,0 +1,29 @@ +package common + +// Region represents ECS region +type Region string + +// Constants of region definition +const ( + Hangzhou = Region("cn-hangzhou") + Qingdao = Region("cn-qingdao") + Beijing = Region("cn-beijing") + Hongkong = Region("cn-hongkong") + Shenzhen = Region("cn-shenzhen") + USWest1 = Region("us-west-1") + USEast1 = Region("us-east-1") + APSouthEast1 = Region("ap-southeast-1") + Shanghai = Region("cn-shanghai") + MEEast1 = Region("me-east-1") + APNorthEast1 = Region("ap-northeast-1") + APSouthEast2 = Region("ap-southeast-2") + EUCentral1 = Region("eu-central-1") +) + +var ValidRegions = []Region{ + Hangzhou, Qingdao, Beijing, Shenzhen, Hongkong, Shanghai, + USWest1, USEast1, + APNorthEast1, APSouthEast1, APSouthEast2, + MEEast1, + EUCentral1, +} diff --git a/vendor/github.com/denverdino/aliyungo/common/request.go b/vendor/github.com/denverdino/aliyungo/common/request.go new file mode 100644 index 0000000000..2a883f19b2 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/request.go @@ -0,0 +1,101 @@ +package common + +import ( + "fmt" + "log" + "time" + + "github.com/denverdino/aliyungo/util" +) + +// Constants for Aliyun API requests +const ( + SignatureVersion = "1.0" + SignatureMethod = "HMAC-SHA1" + JSONResponseFormat = "JSON" + XMLResponseFormat = "XML" + ECSRequestMethod = "GET" +) + +type Request struct { + Format string + Version string + AccessKeyId string + Signature string + SignatureMethod string + Timestamp util.ISO6801Time + SignatureVersion string + SignatureNonce string + ResourceOwnerAccount string + Action string +} + +func (request *Request) init(version string, action string, AccessKeyId string) { + request.Format = JSONResponseFormat + request.Timestamp = util.NewISO6801Time(time.Now().UTC()) + request.Version = version + request.SignatureVersion = SignatureVersion + request.SignatureMethod = SignatureMethod + request.SignatureNonce = util.CreateRandomString() + request.Action = action + request.AccessKeyId = AccessKeyId +} + +type Response struct { + RequestId string +} + +type ErrorResponse struct { + Response + HostId string + Code string + Message string +} + +// An Error represents a custom error for Aliyun API failure response +type Error struct { + ErrorResponse + StatusCode int //Status Code of HTTP Response +} + +func (e *Error) Error() string { + return fmt.Sprintf("Aliyun API Error: RequestId: %s Status Code: %d Code: %s Message: %s", e.RequestId, e.StatusCode, e.Code, e.Message) +} + +type Pagination struct { + PageNumber int + PageSize int +} + +func (p *Pagination) SetPageSize(size int) { + p.PageSize = size +} + +func (p *Pagination) Validate() { + if p.PageNumber < 0 { + log.Printf("Invalid PageNumber: %d", p.PageNumber) + p.PageNumber = 1 + } + if p.PageSize < 0 { + log.Printf("Invalid PageSize: %d", p.PageSize) + p.PageSize = 10 + } else if p.PageSize > 50 { + log.Printf("Invalid PageSize: %d", p.PageSize) + p.PageSize = 50 + } +} + +// A PaginationResponse represents a response with pagination information +type PaginationResult struct { + TotalCount int + PageNumber int + PageSize int +} + +// NextPage gets the next page of the result set +func (r *PaginationResult) NextPage() *Pagination { + if r.PageNumber*r.PageSize >= r.TotalCount { + return nil + } + return &Pagination{PageNumber: r.PageNumber + 1, PageSize: r.PageSize} +} diff --git a/vendor/github.com/denverdino/aliyungo/common/types.go b/vendor/github.com/denverdino/aliyungo/common/types.go new file mode 100644 index 0000000000..c562aedfc2 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/types.go @@ -0,0 +1,15 @@ +package common + +type InternetChargeType string + +const ( + PayByBandwidth = InternetChargeType("PayByBandwidth") + PayByTraffic = InternetChargeType("PayByTraffic") +) + +type InstanceChargeType string + +const ( + PrePaid = InstanceChargeType("PrePaid") + PostPaid = InstanceChargeType("PostPaid") +) diff --git a/vendor/github.com/denverdino/aliyungo/common/version.go b/vendor/github.com/denverdino/aliyungo/common/version.go new file mode 100644 index 0000000000..7cb3d3aff0 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/version.go @@ -0,0 +1,3 @@ +package common + +const Version = "0.1" diff --git a/vendor/github.com/denverdino/aliyungo/ecs/client.go b/vendor/github.com/denverdino/aliyungo/ecs/client.go new file mode 100644 index 0000000000..063c0738c0 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/client.go @@ -0,0 +1,37 @@ +package ecs + +import ( + "github.com/denverdino/aliyungo/common" + "os" +) + +// Interval for checking status in WaitForXXX method +const DefaultWaitForInterval = 5 + +// Default timeout value for WaitForXXX method +const DefaultTimeout = 60 + +type Client struct { + common.Client +} + +const ( + // ECSDefaultEndpoint is the default API endpoint of ECS services + ECSDefaultEndpoint = "https://ecs-cn-hangzhou.aliyuncs.com" + ECSAPIVersion = "2014-05-26" +) + +// NewClient creates a new instance of ECS client +func NewClient(accessKeyId, accessKeySecret string) *Client { + endpoint := os.Getenv("ECS_ENDPOINT") + if endpoint == "" { + endpoint = ECSDefaultEndpoint + } + return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret) +} + +func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client { + client := &Client{} + client.Init(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret) + return client +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/disks.go b/vendor/github.com/denverdino/aliyungo/ecs/disks.go new file mode 100644 index 0000000000..f1d1e93412 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/disks.go @@ -0,0 +1,330 @@ +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +// Types of disks +type DiskType string + +const ( + DiskTypeAll = DiskType("all") //Default + DiskTypeAllSystem = DiskType("system") + DiskTypeAllData = DiskType("data") +) + +// Categories of disks +type DiskCategory string + +const ( + DiskCategoryAll = DiskCategory("all") //Default + DiskCategoryCloud = DiskCategory("cloud") + DiskCategoryEphemeral = DiskCategory("ephemeral") + DiskCategoryEphemeralSSD = DiskCategory("ephemeral_ssd") + DiskCategoryCloudEfficiency = DiskCategory("cloud_efficiency") + DiskCategoryCloudSSD = DiskCategory("cloud_ssd") +) + +// Status of disks +type DiskStatus string + +const ( + DiskStatusInUse = DiskStatus("In_use") + DiskStatusAvailable = DiskStatus("Available") + DiskStatusAttaching = DiskStatus("Attaching") + DiskStatusDetaching = DiskStatus("Detaching") + DiskStatusCreating = DiskStatus("Creating") + DiskStatusReIniting = DiskStatus("ReIniting") + DiskStatusAll = DiskStatus("All") //Default +) + +// Charge type of disks +type DiskChargeType string + +const ( + PrePaid = DiskChargeType("PrePaid") + PostPaid = DiskChargeType("PostPaid") +) + +// A DescribeDisksArgs defines the arguments to describe disks +type DescribeDisksArgs struct { + RegionId common.Region + ZoneId string + DiskIds []string + InstanceId string + DiskType DiskType //enum for all(default) | system | data + Category DiskCategory //enum for all(default) | cloud | ephemeral + Status DiskStatus //enum for In_use | Available | Attaching | Detaching | Creating | ReIniting | All(default) + SnapshotId string + Name string + Portable *bool //optional + DeleteWithInstance *bool //optional + DeleteAutoSnapshot *bool //optional + EnableAutoSnapshot *bool //optional + DiskChargeType DiskChargeType + Tag map[string]string + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&diskitemtype +type DiskItemType struct { + DiskId string + RegionId common.Region + ZoneId string + DiskName string + Description string + Type DiskType + Category DiskCategory + Size int + ImageId string + SourceSnapshotId string + ProductCode string + Portable bool + Status DiskStatus + OperationLocks OperationLocksType + InstanceId string + Device string + DeleteWithInstance bool + DeleteAutoSnapshot bool + EnableAutoSnapshot bool + CreationTime util.ISO6801Time + AttachedTime util.ISO6801Time + DetachedTime util.ISO6801Time + DiskChargeType DiskChargeType +} + +type DescribeDisksResponse struct { + common.Response + common.PaginationResult + RegionId common.Region + Disks struct { + Disk []DiskItemType + } +} + +// DescribeDisks describes Disks +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&describedisks +func (client *Client) DescribeDisks(args *DescribeDisksArgs) (disks []DiskItemType, pagination *common.PaginationResult, err error) { + response := DescribeDisksResponse{} + + err = client.Invoke("DescribeDisks", args, &response) + + if err != nil { + return nil, nil, err + } + + return response.Disks.Disk, &response.PaginationResult, err +} + +type CreateDiskArgs struct { + RegionId common.Region + ZoneId string + DiskName string + Description string + DiskCategory DiskCategory + Size int + SnapshotId string + ClientToken string +} + +type CreateDisksResponse struct { + common.Response + DiskId string +} + +// CreateDisk creates a new disk +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&createdisk +func (client *Client) CreateDisk(args *CreateDiskArgs) (diskId string, err error) { + response := CreateDisksResponse{} + err = client.Invoke("CreateDisk", args, &response) + if err != nil { + return "", err + } + return response.DiskId, err +} + +type DeleteDiskArgs struct { + DiskId string +} + +type DeleteDiskResponse struct { + common.Response +} + +// DeleteDisk deletes disk +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&deletedisk +func (client *Client) DeleteDisk(diskId string) error { + args := DeleteDiskArgs{ + DiskId: diskId, + } + response := DeleteDiskResponse{} + err := client.Invoke("DeleteDisk", &args, &response) + return err +} + +type ReInitDiskArgs struct { + DiskId string +} + +type ReInitDiskResponse struct { + common.Response +} + +// ReInitDisk reinitizes disk +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&reinitdisk +func (client *Client) ReInitDisk(diskId string) error { + args := ReInitDiskArgs{ + DiskId: diskId, + } + response := ReInitDiskResponse{} + err := client.Invoke("ReInitDisk", &args, &response) + return err +} + +type AttachDiskArgs struct { + InstanceId string + DiskId string + Device string + DeleteWithInstance bool +} + +type AttachDiskResponse struct { + common.Response +} + +// AttachDisk attaches disk to instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&attachdisk +func (client *Client) AttachDisk(args *AttachDiskArgs) error { + response := AttachDiskResponse{} + err := client.Invoke("AttachDisk", args, &response) + return err +} + +type DetachDiskArgs struct { + InstanceId string + DiskId string +} + +type DetachDiskResponse struct { + common.Response +} + +// DetachDisk detaches disk from instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&detachdisk +func (client *Client) DetachDisk(instanceId string, diskId string) error { + args := DetachDiskArgs{ + InstanceId: instanceId, + DiskId: diskId, + } + response := DetachDiskResponse{} + err := client.Invoke("DetachDisk", &args, &response) + return err +} + +type ResetDiskArgs struct { + DiskId string + SnapshotId string +} + +type ResetDiskResponse struct { + common.Response +} + +// ResetDisk resets disk to original status +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&resetdisk +func (client *Client) ResetDisk(diskId string, snapshotId string) error { + args := ResetDiskArgs{ + SnapshotId: snapshotId, + DiskId: diskId, + } + response := ResetDiskResponse{} + err := client.Invoke("ResetDisk", &args, &response) + return err +} + +type ModifyDiskAttributeArgs struct { + DiskId string + DiskName string + Description string + DeleteWithInstance *bool + DeleteAutoSnapshot *bool + EnableAutoSnapshot *bool +} + +type ModifyDiskAttributeResponse struct { + common.Response +} + +// ModifyDiskAttribute modifies disk attribute +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&modifydiskattribute +func (client *Client) ModifyDiskAttribute(args *ModifyDiskAttributeArgs) error { + response := ModifyDiskAttributeResponse{} + err := client.Invoke("ModifyDiskAttribute", args, &response) + return err +} + +type ReplaceSystemDiskArgs struct { + InstanceId string + ImageId string + SystemDisk SystemDiskType + ClientToken string +} + +type ReplaceSystemDiskResponse struct { + common.Response + DiskId string +} + +// ReplaceSystemDisk replace system disk +// +// You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/disk/replacesystemdisk.html +func (client *Client) ReplaceSystemDisk(args *ReplaceSystemDiskArgs) (diskId string, err error) { + response := ReplaceSystemDiskResponse{} + err = client.Invoke("ReplaceSystemDisk", args, &response) + if err != nil { + return "", err + } + return response.DiskId, nil +} + +// WaitForDisk waits for disk to given status +func (client *Client) WaitForDisk(regionId common.Region, diskId string, status DiskStatus, timeout int) error { + if timeout <= 0 { + timeout = DefaultTimeout + } + args := DescribeDisksArgs{ + RegionId: regionId, + DiskIds: []string{diskId}, + } + + for { + disks, _, err := client.DescribeDisks(&args) + if err != nil { + return err + } + if disks == nil || len(disks) == 0 { + return common.GetClientErrorFromString("Not found") + } + if disks[0].Status == status { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/images.go b/vendor/github.com/denverdino/aliyungo/ecs/images.go new file mode 100644 index 0000000000..c623caf9c3 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/images.go @@ -0,0 +1,280 @@ +package ecs + +import ( + "net/url" + "strconv" + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +// ImageOwnerAlias represents image owner +type ImageOwnerAlias string + +// Constants of image owner +const ( + ImageOwnerSystem = ImageOwnerAlias("system") + ImageOwnerSelf = ImageOwnerAlias("self") + ImageOwnerOthers = ImageOwnerAlias("others") + ImageOwnerMarketplace = ImageOwnerAlias("marketplace") + ImageOwnerDefault = ImageOwnerAlias("") //Return the values for system, self, and others +) + +type ImageStatus string + +const ( + ImageStatusAvailable = ImageStatus("Available") + ImageStatusUnAvailable = ImageStatus("UnAvailable") + ImageStatusCreating = ImageStatus("Creating") + ImageStatusCreateFailed = ImageStatus("CreateFailed") +) + +type ImageUsage string + +const ( + ImageUsageInstance = ImageUsage("instance") + ImageUsageNone = ImageUsage("none") +) + +// DescribeImagesArgs repsents arguements to describe images +type DescribeImagesArgs struct { + RegionId common.Region + ImageId string + SnapshotId string + ImageName string + Status ImageStatus + ImageOwnerAlias ImageOwnerAlias + common.Pagination +} + +type DescribeImagesResponse struct { + common.Response + common.PaginationResult + + RegionId common.Region + Images struct { + Image []ImageType + } +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&diskdevicemapping +type DiskDeviceMapping struct { + SnapshotId string + //Why Size Field is string-type. + Size string + Device string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&imagetype +type ImageType struct { + ImageId string + ImageVersion string + Architecture string + ImageName string + Description string + Size int + ImageOwnerAlias string + OSName string + OSType string + Platform string + DiskDeviceMappings struct { + DiskDeviceMapping []DiskDeviceMapping + } + ProductCode string + IsSubscribed bool + IsSelfShared string + IsCopied bool + IsSupportIoOptimized bool + Progress string + Usage ImageUsage + Status ImageStatus + CreationTime util.ISO6801Time +} + +// DescribeImages describes images +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/image&describeimages +func (client *Client) DescribeImages(args *DescribeImagesArgs) (images []ImageType, pagination *common.PaginationResult, err error) { + + args.Validate() + response := DescribeImagesResponse{} + err = client.Invoke("DescribeImages", args, &response) + if err != nil { + return nil, nil, err + } + return response.Images.Image, &response.PaginationResult, nil +} + +// CreateImageArgs repsents arguements to create image +type CreateImageArgs struct { + RegionId common.Region + SnapshotId string + ImageName string + ImageVersion string + Description string + ClientToken string +} + +type CreateImageResponse struct { + common.Response + + ImageId string +} + +// CreateImage creates a new image +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/image&createimage +func (client *Client) CreateImage(args *CreateImageArgs) (imageId string, err error) { + response := &CreateImageResponse{} + err = client.Invoke("CreateImage", args, &response) + if err != nil { + return "", err + } + return response.ImageId, nil +} + +type DeleteImageArgs struct { + RegionId common.Region + ImageId string +} + +type DeleteImageResponse struct { + common.Response +} + +// DeleteImage deletes Image +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/image&deleteimage +func (client *Client) DeleteImage(regionId common.Region, imageId string) error { + args := DeleteImageArgs{ + RegionId: regionId, + ImageId: imageId, + } + + response := &DeleteImageResponse{} + return client.Invoke("DeleteImage", &args, &response) +} + +// ModifyImageSharePermission repsents arguements to share image +type ModifyImageSharePermissionArgs struct { + RegionId common.Region + ImageId string + AddAccount []string + RemoveAccount []string +} + +// You can read doc at http://help.aliyun.com/document_detail/ecs/open-api/image/modifyimagesharepermission.html +func (client *Client) ModifyImageSharePermission(args *ModifyImageSharePermissionArgs) error { + req := url.Values{} + req.Add("RegionId", string(args.RegionId)) + req.Add("ImageId", args.ImageId) + + for i, item := range args.AddAccount { + req.Add("AddAccount."+strconv.Itoa(i+1), item) + } + for i, item := range args.RemoveAccount { + req.Add("RemoveAccount."+strconv.Itoa(i+1), item) + } + + return client.Invoke("ModifyImageSharePermission", req, &common.Response{}) +} + +type AccountType struct { + AliyunId string +} +type ImageSharePermissionResponse struct { + common.Response + ImageId string + RegionId string + Accounts struct { + Account []AccountType + } + TotalCount int + PageNumber int + PageSize int +} + +func (client *Client) DescribeImageSharePermission(args *ModifyImageSharePermissionArgs) (*ImageSharePermissionResponse, error) { + response := ImageSharePermissionResponse{} + err := client.Invoke("DescribeImageSharePermission", args, &response) + return &response, err +} + +type CopyImageArgs struct { + RegionId common.Region + ImageId string + DestinationRegionId common.Region + DestinationImageName string + DestinationDescription string + ClientToken string +} + +type CopyImageResponse struct { + common.Response + ImageId string +} + +// You can read doc at https://help.aliyun.com/document_detail/25538.html +func (client *Client) CopyImage(args *CopyImageArgs) (string, error) { + response := &CopyImageResponse{} + err := client.Invoke("CopyImage", args, &response) + if err != nil { + return "", err + } + return response.ImageId, nil +} + +// Default timeout value for WaitForImageReady method +const ImageDefaultTimeout = 120 + +//Wait Image ready +func (client *Client) WaitForImageReady(regionId common.Region, imageId string, timeout int) error { + if timeout <= 0 { + timeout = ImageDefaultTimeout + } + for { + args := DescribeImagesArgs{ + RegionId: regionId, + ImageId: imageId, + Status: ImageStatusCreating, + } + + images, _, err := client.DescribeImages(&args) + if err != nil { + return err + } + if images == nil || len(images) == 0 { + args.Status = ImageStatusAvailable + images, _, er := client.DescribeImages(&args) + if er == nil && len(images) == 1 { + break + } else { + return common.GetClientErrorFromString("Not found") + } + } + if images[0].Progress == "100%" { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} + +type CancelCopyImageRequest struct { + regionId common.Region + ImageId string +} + +// You can read doc at https://help.aliyun.com/document_detail/25539.html +func (client *Client) CancelCopyImage(regionId common.Region, imageId string) error { + response := &common.Response{} + err := client.Invoke("CancelCopyImage", &CancelCopyImageRequest{regionId, imageId}, &response) + return err +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/instance_types.go b/vendor/github.com/denverdino/aliyungo/ecs/instance_types.go new file mode 100644 index 0000000000..1e3e94bb94 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/instance_types.go @@ -0,0 +1,51 @@ +package ecs + +import "github.com/denverdino/aliyungo/common" + +type DescribeInstanceTypesArgs struct { + InstanceTypeFamily string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancetypeitemtype +type InstanceTypeItemType struct { + InstanceTypeId string + CpuCoreCount int + MemorySize float64 + InstanceTypeFamily string +} + +type DescribeInstanceTypesResponse struct { + common.Response + InstanceTypes struct { + InstanceType []InstanceTypeItemType + } +} + +// DescribeInstanceTypes describes all instance types +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/other&describeinstancetypes +func (client *Client) DescribeInstanceTypes() (instanceTypes []InstanceTypeItemType, err error) { + response := DescribeInstanceTypesResponse{} + + err = client.Invoke("DescribeInstanceTypes", &DescribeInstanceTypesArgs{}, &response) + + if err != nil { + return []InstanceTypeItemType{}, err + } + return response.InstanceTypes.InstanceType, nil + +} + +// support user args +func (client *Client) DescribeInstanceTypesNew(args *DescribeInstanceTypesArgs) (instanceTypes []InstanceTypeItemType, err error) { + response := DescribeInstanceTypesResponse{} + + err = client.Invoke("DescribeInstanceTypes", args, &response) + + if err != nil { + return []InstanceTypeItemType{}, err + } + return response.InstanceTypes.InstanceType, nil + +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/instances.go b/vendor/github.com/denverdino/aliyungo/ecs/instances.go new file mode 100644 index 0000000000..cce16dff48 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/instances.go @@ -0,0 +1,540 @@ +package ecs + +import ( + "encoding/base64" + "encoding/json" + "strconv" + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +// InstanceStatus represents instance status +type InstanceStatus string + +// Constants of InstanceStatus +const ( + Creating = InstanceStatus("Creating") + Running = InstanceStatus("Running") + Starting = InstanceStatus("Starting") + + Stopped = InstanceStatus("Stopped") + Stopping = InstanceStatus("Stopping") +) + +type LockReason string + +const ( + LockReasonFinancial = LockReason("financial") + LockReasonSecurity = LockReason("security") +) + +type LockReasonType struct { + LockReason LockReason +} + +type DescribeUserdataArgs struct { + RegionId common.Region + InstanceId string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancestatusitemtype +type DescribeUserdataItemType struct { + UserData string + InstanceId string + RegionId string +} + +type DescribeUserdataResponse struct { + common.Response + DescribeUserdataItemType +} + +// DescribeInstanceStatus describes instance status +// +// You can read doc at https://intl.aliyun.com/help/doc-detail/49227.htm +func (client *Client) DescribeUserdata(args *DescribeUserdataArgs) (userData *DescribeUserdataItemType, err error) { + response := DescribeUserdataResponse{} + + err = client.Invoke("DescribeUserdata", args, &response) + + if err == nil { + return &response.DescribeUserdataItemType, nil + } + + return nil, err +} + +type DescribeInstanceStatusArgs struct { + RegionId common.Region + ZoneId string + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancestatusitemtype +type InstanceStatusItemType struct { + InstanceId string + Status InstanceStatus +} + +type DescribeInstanceStatusResponse struct { + common.Response + common.PaginationResult + InstanceStatuses struct { + InstanceStatus []InstanceStatusItemType + } +} + +// DescribeInstanceStatus describes instance status +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancestatus +func (client *Client) DescribeInstanceStatus(args *DescribeInstanceStatusArgs) (instanceStatuses []InstanceStatusItemType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeInstanceStatusResponse{} + + err = client.Invoke("DescribeInstanceStatus", args, &response) + + if err == nil { + return response.InstanceStatuses.InstanceStatus, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type StopInstanceArgs struct { + InstanceId string + ForceStop bool +} + +type StopInstanceResponse struct { + common.Response +} + +// StopInstance stops instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&stopinstance +func (client *Client) StopInstance(instanceId string, forceStop bool) error { + args := StopInstanceArgs{ + InstanceId: instanceId, + ForceStop: forceStop, + } + response := StopInstanceResponse{} + err := client.Invoke("StopInstance", &args, &response) + return err +} + +type StartInstanceArgs struct { + InstanceId string +} + +type StartInstanceResponse struct { + common.Response +} + +// StartInstance starts instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&startinstance +func (client *Client) StartInstance(instanceId string) error { + args := StartInstanceArgs{InstanceId: instanceId} + response := StartInstanceResponse{} + err := client.Invoke("StartInstance", &args, &response) + return err +} + +type RebootInstanceArgs struct { + InstanceId string + ForceStop bool +} + +type RebootInstanceResponse struct { + common.Response +} + +// RebootInstance reboot instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&rebootinstance +func (client *Client) RebootInstance(instanceId string, forceStop bool) error { + request := RebootInstanceArgs{ + InstanceId: instanceId, + ForceStop: forceStop, + } + response := RebootInstanceResponse{} + err := client.Invoke("RebootInstance", &request, &response) + return err +} + +type DescribeInstanceAttributeArgs struct { + InstanceId string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&operationlockstype +type OperationLocksType struct { + LockReason []LockReasonType //enum for financial, security +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&securitygroupidsettype +type SecurityGroupIdSetType struct { + SecurityGroupId string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&ipaddresssettype +type IpAddressSetType struct { + IpAddress []string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vpcattributestype +type VpcAttributesType struct { + VpcId string + VSwitchId string + PrivateIpAddress IpAddressSetType + NatIpAddress string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddressassociatetype +type EipAddressAssociateType struct { + AllocationId string + IpAddress string + Bandwidth int + InternetChargeType common.InternetChargeType +} + +// Experimental feature +type SpotStrategyType string + +// Constants of SpotStrategyType +const ( + NoSpot = SpotStrategyType("NoSpot") + SpotWithPriceLimit = SpotStrategyType("SpotWithPriceLimit") +) + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instanceattributestype +type InstanceAttributesType struct { + InstanceId string + InstanceName string + Description string + ImageId string + RegionId common.Region + ZoneId string + CPU int + Memory int + ClusterId string + InstanceType string + InstanceTypeFamily string + HostName string + SerialNumber string + Status InstanceStatus + OperationLocks OperationLocksType + SecurityGroupIds struct { + SecurityGroupId []string + } + PublicIpAddress IpAddressSetType + InnerIpAddress IpAddressSetType + InstanceNetworkType string //enum Classic | Vpc + InternetMaxBandwidthIn int + InternetMaxBandwidthOut int + InternetChargeType common.InternetChargeType + CreationTime util.ISO6801Time //time.Time + VpcAttributes VpcAttributesType + EipAddress EipAddressAssociateType + IoOptimized StringOrBool + InstanceChargeType common.InstanceChargeType + ExpiredTime util.ISO6801Time + Tags struct { + Tag []TagItemType + } + SpotStrategy SpotStrategyType +} + +type DescribeInstanceAttributeResponse struct { + common.Response + InstanceAttributesType +} + +// DescribeInstanceAttribute describes instance attribute +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstanceattribute +func (client *Client) DescribeInstanceAttribute(instanceId string) (instance *InstanceAttributesType, err error) { + args := DescribeInstanceAttributeArgs{InstanceId: instanceId} + + response := DescribeInstanceAttributeResponse{} + err = client.Invoke("DescribeInstanceAttribute", &args, &response) + if err != nil { + return nil, err + } + return &response.InstanceAttributesType, err +} + +type ModifyInstanceAttributeArgs struct { + InstanceId string + InstanceName string + Description string + Password string + HostName string +} + +type ModifyInstanceAttributeResponse struct { + common.Response +} + +//ModifyInstanceAttribute modify instance attrbute +// +// You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/modifyinstanceattribute.html +func (client *Client) ModifyInstanceAttribute(args *ModifyInstanceAttributeArgs) error { + response := ModifyInstanceAttributeResponse{} + err := client.Invoke("ModifyInstanceAttribute", args, &response) + return err +} + +// Default timeout value for WaitForInstance method +const InstanceDefaultTimeout = 120 + +// WaitForInstance waits for instance to given status +func (client *Client) WaitForInstance(instanceId string, status InstanceStatus, timeout int) error { + if timeout <= 0 { + timeout = InstanceDefaultTimeout + } + for { + instance, err := client.DescribeInstanceAttribute(instanceId) + if err != nil { + return err + } + if instance.Status == status { + //TODO + //Sleep one more time for timing issues + time.Sleep(DefaultWaitForInterval * time.Second) + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + + } + return nil +} + +type DescribeInstanceVncUrlArgs struct { + RegionId common.Region + InstanceId string +} + +type DescribeInstanceVncUrlResponse struct { + common.Response + VncUrl string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancevncurl +func (client *Client) DescribeInstanceVncUrl(args *DescribeInstanceVncUrlArgs) (string, error) { + response := DescribeInstanceVncUrlResponse{} + + err := client.Invoke("DescribeInstanceVncUrl", args, &response) + + if err == nil { + return response.VncUrl, nil + } + + return "", err +} + +type DescribeInstancesArgs struct { + RegionId common.Region + VpcId string + VSwitchId string + ZoneId string + InstanceIds string + InstanceNetworkType string + InstanceName string + Status InstanceStatus + PrivateIpAddresses string + InnerIpAddresses string + PublicIpAddresses string + SecurityGroupId string + Tag map[string]string + InstanceType string + SpotStrategy SpotStrategyType + common.Pagination +} + +type DescribeInstancesResponse struct { + common.Response + common.PaginationResult + Instances struct { + Instance []InstanceAttributesType + } +} + +// DescribeInstances describes instances +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstances +func (client *Client) DescribeInstances(args *DescribeInstancesArgs) (instances []InstanceAttributesType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeInstancesResponse{} + + err = client.Invoke("DescribeInstances", args, &response) + + if err == nil { + return response.Instances.Instance, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type DeleteInstanceArgs struct { + InstanceId string +} + +type DeleteInstanceResponse struct { + common.Response +} + +// DeleteInstance deletes instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&deleteinstance +func (client *Client) DeleteInstance(instanceId string) error { + args := DeleteInstanceArgs{InstanceId: instanceId} + response := DeleteInstanceResponse{} + err := client.Invoke("DeleteInstance", &args, &response) + return err +} + +type DataDiskType struct { + Size int + Category DiskCategory //Enum cloud, ephemeral, ephemeral_ssd + SnapshotId string + DiskName string + Description string + Device string + DeleteWithInstance bool +} + +type SystemDiskType struct { + Size int + Category DiskCategory //Enum cloud, ephemeral, ephemeral_ssd + DiskName string + Description string +} + +type IoOptimized string + +type StringOrBool struct { + Value bool +} + +// UnmarshalJSON implements the json.Unmarshaller interface. +func (io *StringOrBool) UnmarshalJSON(value []byte) error { + if value[0] == '"' { + var str string + err := json.Unmarshal(value, &str) + if err == nil { + io.Value = (str == "true" || str == "optimized") + } + return err + } + var boolVal bool + err := json.Unmarshal(value, &boolVal) + if err == nil { + io.Value = boolVal + } + return err +} + +func (io StringOrBool) Bool() bool { + return io.Value +} + +func (io StringOrBool) String() string { + return strconv.FormatBool(io.Value) +} + +var ( + IoOptimizedNone = IoOptimized("none") + IoOptimizedOptimized = IoOptimized("optimized") +) + +type CreateInstanceArgs struct { + RegionId common.Region + ZoneId string + ImageId string + InstanceType string + SecurityGroupId string + InstanceName string + Description string + InternetChargeType common.InternetChargeType + InternetMaxBandwidthIn int + InternetMaxBandwidthOut int + HostName string + Password string + IoOptimized IoOptimized + SystemDisk SystemDiskType + DataDisk []DataDiskType + VSwitchId string + PrivateIpAddress string + ClientToken string + InstanceChargeType common.InstanceChargeType + Period int + UserData string + AutoRenew bool + AutoRenewPeriod int + SpotStrategy SpotStrategyType +} + +type CreateInstanceResponse struct { + common.Response + InstanceId string +} + +// CreateInstance creates instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&createinstance +func (client *Client) CreateInstance(args *CreateInstanceArgs) (instanceId string, err error) { + if args.UserData != "" { + // Encode to base64 string + args.UserData = base64.StdEncoding.EncodeToString([]byte(args.UserData)) + } + response := CreateInstanceResponse{} + err = client.Invoke("CreateInstance", args, &response) + if err != nil { + return "", err + } + return response.InstanceId, err +} + +type SecurityGroupArgs struct { + InstanceId string + SecurityGroupId string +} + +type SecurityGroupResponse struct { + common.Response +} + +//JoinSecurityGroup +// +//You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/joinsecuritygroup.html +func (client *Client) JoinSecurityGroup(instanceId string, securityGroupId string) error { + args := SecurityGroupArgs{InstanceId: instanceId, SecurityGroupId: securityGroupId} + response := SecurityGroupResponse{} + err := client.Invoke("JoinSecurityGroup", &args, &response) + return err +} + +//LeaveSecurityGroup +// +//You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/leavesecuritygroup.html +func (client *Client) LeaveSecurityGroup(instanceId string, securityGroupId string) error { + args := SecurityGroupArgs{InstanceId: instanceId, SecurityGroupId: securityGroupId} + response := SecurityGroupResponse{} + err := client.Invoke("LeaveSecurityGroup", &args, &response) + return err +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/monitoring.go b/vendor/github.com/denverdino/aliyungo/ecs/monitoring.go new file mode 100644 index 0000000000..6123d28b71 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/monitoring.go @@ -0,0 +1,136 @@ +package ecs + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type DescribeInstanceMonitorDataArgs struct { + InstanceId string + StartTime util.ISO6801Time + EndTime util.ISO6801Time + Period int //Default 60s +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancemonitordatatype +type InstanceMonitorDataType struct { + InstanceId string + CPU int + IntranetRX int + IntranetTX int + IntranetBandwidth int + InternetRX int + InternetTX int + InternetBandwidth int + IOPSRead int + IOPSWrite int + BPSRead int + BPSWrite int + TimeStamp util.ISO6801Time +} + +type DescribeInstanceMonitorDataResponse struct { + common.Response + MonitorData struct { + InstanceMonitorData []InstanceMonitorDataType + } +} + +// DescribeInstanceMonitorData describes instance monitoring data +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/monitor&describeinstancemonitordata +func (client *Client) DescribeInstanceMonitorData(args *DescribeInstanceMonitorDataArgs) (monitorData []InstanceMonitorDataType, err error) { + if args.Period == 0 { + args.Period = 60 + } + response := DescribeInstanceMonitorDataResponse{} + err = client.Invoke("DescribeInstanceMonitorData", args, &response) + if err != nil { + return nil, err + } + return response.MonitorData.InstanceMonitorData, err +} + +type DescribeEipMonitorDataArgs struct { + AllocationId string + StartTime util.ISO6801Time + EndTime util.ISO6801Time + Period int //Default 60s +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipmonitordatatype +type EipMonitorDataType struct { + EipRX int + EipTX int + EipFlow int + EipBandwidth int + EipPackets int + TimeStamp util.ISO6801Time +} + +type DescribeEipMonitorDataResponse struct { + common.Response + EipMonitorDatas struct { + EipMonitorData []EipMonitorDataType + } +} + +// DescribeEipMonitorData describes EIP monitoring data +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/monitor&describeeipmonitordata +func (client *Client) DescribeEipMonitorData(args *DescribeEipMonitorDataArgs) (monitorData []EipMonitorDataType, err error) { + if args.Period == 0 { + args.Period = 60 + } + response := DescribeEipMonitorDataResponse{} + err = client.Invoke("DescribeEipMonitorData", args, &response) + if err != nil { + return nil, err + } + return response.EipMonitorDatas.EipMonitorData, err +} + +type DescribeDiskMonitorDataArgs struct { + DiskId string + StartTime util.ISO6801Time + EndTime util.ISO6801Time + Period int //Default 60s +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&diskmonitordatatype +type DiskMonitorDataType struct { + DiskId string + IOPSRead int + IOPSWrite int + IOPSTotal int + BPSRead int + BPSWrite int + BPSTotal int + TimeStamp util.ISO6801Time +} + +type DescribeDiskMonitorDataResponse struct { + common.Response + TotalCount int + MonitorData struct { + DiskMonitorData []DiskMonitorDataType + } +} + +// DescribeDiskMonitorData describes disk monitoring data +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/monitor&describediskmonitordata +func (client *Client) DescribeDiskMonitorData(args *DescribeDiskMonitorDataArgs) (monitorData []DiskMonitorDataType, totalCount int, err error) { + if args.Period == 0 { + args.Period = 60 + } + response := DescribeDiskMonitorDataResponse{} + err = client.Invoke("DescribeDiskMonitorData", args, &response) + if err != nil { + return nil, 0, err + } + return response.MonitorData.DiskMonitorData, response.TotalCount, err +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/networks.go b/vendor/github.com/denverdino/aliyungo/ecs/networks.go new file mode 100644 index 0000000000..fecc7af1a0 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/networks.go @@ -0,0 +1,249 @@ +// API on Network + +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type AllocatePublicIpAddressArgs struct { + InstanceId string +} + +type AllocatePublicIpAddressResponse struct { + common.Response + + IpAddress string +} + +// AllocatePublicIpAddress allocates Public Ip Address +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&allocatepublicipaddress +func (client *Client) AllocatePublicIpAddress(instanceId string) (ipAddress string, err error) { + args := AllocatePublicIpAddressArgs{ + InstanceId: instanceId, + } + response := AllocatePublicIpAddressResponse{} + err = client.Invoke("AllocatePublicIpAddress", &args, &response) + if err != nil { + return "", err + } + return response.IpAddress, nil +} + +type ModifyInstanceNetworkSpec struct { + InstanceId string + InternetMaxBandwidthOut *int + InternetMaxBandwidthIn *int +} + +type ModifyInstanceNetworkSpecResponse struct { + common.Response +} + +// ModifyInstanceNetworkSpec modifies instance network spec +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&modifyinstancenetworkspec +func (client *Client) ModifyInstanceNetworkSpec(args *ModifyInstanceNetworkSpec) error { + + response := ModifyInstanceNetworkSpecResponse{} + return client.Invoke("ModifyInstanceNetworkSpec", args, &response) +} + +type AllocateEipAddressArgs struct { + RegionId common.Region + Bandwidth int + InternetChargeType common.InternetChargeType + ClientToken string +} + +type AllocateEipAddressResponse struct { + common.Response + EipAddress string + AllocationId string +} + +// AllocateEipAddress allocates Eip Address +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&allocateeipaddress +func (client *Client) AllocateEipAddress(args *AllocateEipAddressArgs) (EipAddress string, AllocationId string, err error) { + if args.Bandwidth == 0 { + args.Bandwidth = 5 + } + response := AllocateEipAddressResponse{} + err = client.Invoke("AllocateEipAddress", args, &response) + if err != nil { + return "", "", err + } + return response.EipAddress, response.AllocationId, nil +} + +type AssociateEipAddressArgs struct { + AllocationId string + InstanceId string +} + +type AssociateEipAddressResponse struct { + common.Response +} + +// AssociateEipAddress associates EIP address to VM instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&associateeipaddress +func (client *Client) AssociateEipAddress(allocationId string, instanceId string) error { + args := AssociateEipAddressArgs{ + AllocationId: allocationId, + InstanceId: instanceId, + } + response := ModifyInstanceNetworkSpecResponse{} + return client.Invoke("AssociateEipAddress", &args, &response) +} + +// Status of disks +type EipStatus string + +const ( + EipStatusAssociating = EipStatus("Associating") + EipStatusUnassociating = EipStatus("Unassociating") + EipStatusInUse = EipStatus("InUse") + EipStatusAvailable = EipStatus("Available") +) + +type DescribeEipAddressesArgs struct { + RegionId common.Region + Status EipStatus //enum Associating | Unassociating | InUse | Available + EipAddress string + AllocationId string + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddresssettype +type EipAddressSetType struct { + RegionId common.Region + IpAddress string + AllocationId string + Status EipStatus + InstanceId string + Bandwidth string // Why string + InternetChargeType common.InternetChargeType + OperationLocks OperationLocksType + AllocationTime util.ISO6801Time +} + +type DescribeEipAddressesResponse struct { + common.Response + common.PaginationResult + EipAddresses struct { + EipAddress []EipAddressSetType + } +} + +// DescribeInstanceStatus describes instance status +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&describeeipaddresses +func (client *Client) DescribeEipAddresses(args *DescribeEipAddressesArgs) (eipAddresses []EipAddressSetType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeEipAddressesResponse{} + + err = client.Invoke("DescribeEipAddresses", args, &response) + + if err == nil { + return response.EipAddresses.EipAddress, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type ModifyEipAddressAttributeArgs struct { + AllocationId string + Bandwidth int +} + +type ModifyEipAddressAttributeResponse struct { + common.Response +} + +// ModifyEipAddressAttribute Modifies EIP attribute +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&modifyeipaddressattribute +func (client *Client) ModifyEipAddressAttribute(allocationId string, bandwidth int) error { + args := ModifyEipAddressAttributeArgs{ + AllocationId: allocationId, + Bandwidth: bandwidth, + } + response := ModifyEipAddressAttributeResponse{} + return client.Invoke("ModifyEipAddressAttribute", &args, &response) +} + +type UnallocateEipAddressArgs struct { + AllocationId string + InstanceId string +} + +type UnallocateEipAddressResponse struct { + common.Response +} + +// UnassociateEipAddress unallocates Eip Address from instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&unassociateeipaddress +func (client *Client) UnassociateEipAddress(allocationId string, instanceId string) error { + args := UnallocateEipAddressArgs{ + AllocationId: allocationId, + InstanceId: instanceId, + } + response := UnallocateEipAddressResponse{} + return client.Invoke("UnassociateEipAddress", &args, &response) +} + +type ReleaseEipAddressArgs struct { + AllocationId string +} + +type ReleaseEipAddressResponse struct { + common.Response +} + +// ReleaseEipAddress releases Eip address +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&releaseeipaddress +func (client *Client) ReleaseEipAddress(allocationId string) error { + args := ReleaseEipAddressArgs{ + AllocationId: allocationId, + } + response := ReleaseEipAddressResponse{} + return client.Invoke("ReleaseEipAddress", &args, &response) +} + +// WaitForVSwitchAvailable waits for VSwitch to given status +func (client *Client) WaitForEip(regionId common.Region, allocationId string, status EipStatus, timeout int) error { + if timeout <= 0 { + timeout = DefaultTimeout + } + args := DescribeEipAddressesArgs{ + RegionId: regionId, + AllocationId: allocationId, + } + for { + eips, _, err := client.DescribeEipAddresses(&args) + if err != nil { + return err + } + if len(eips) == 0 { + return common.GetClientErrorFromString("Not found") + } + if eips[0].Status == status { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/regions.go b/vendor/github.com/denverdino/aliyungo/ecs/regions.go new file mode 100644 index 0000000000..d8ed72e8d3 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/regions.go @@ -0,0 +1,34 @@ +package ecs + +import "github.com/denverdino/aliyungo/common" + +type DescribeRegionsArgs struct { +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype®iontype +type RegionType struct { + RegionId common.Region + LocalName string +} + +type DescribeRegionsResponse struct { + common.Response + Regions struct { + Region []RegionType + } +} + +// DescribeRegions describes regions +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/region&describeregions +func (client *Client) DescribeRegions() (regions []RegionType, err error) { + response := DescribeRegionsResponse{} + + err = client.Invoke("DescribeRegions", &DescribeRegionsArgs{}, &response) + + if err != nil { + return []RegionType{}, err + } + return response.Regions.Region, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go b/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go new file mode 100644 index 0000000000..01f43127c7 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go @@ -0,0 +1,176 @@ +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type DescribeRouteTablesArgs struct { + VRouterId string + RouteTableId string + common.Pagination +} + +type RouteTableType string + +const ( + RouteTableSystem = RouteTableType("System") + RouteTableCustom = RouteTableType("Custom") +) + +type RouteEntryStatus string + +const ( + RouteEntryStatusPending = RouteEntryStatus("Pending") + RouteEntryStatusAvailable = RouteEntryStatus("Available") + RouteEntryStatusModifying = RouteEntryStatus("Modifying") +) + +type NextHopListType struct { + NextHopList struct { + NextHopItem []NextHopItemType + } +} + +type NextHopItemType struct { + NextHopType string + NextHopId string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&routeentrysettype +type RouteEntrySetType struct { + RouteTableId string + DestinationCidrBlock string + Type RouteTableType + NextHopType string + NextHopId string + NextHopList NextHopListType + InstanceId string + Status RouteEntryStatus // enum Pending | Available | Modifying +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&routetablesettype +type RouteTableSetType struct { + VRouterId string + RouteTableId string + RouteEntrys struct { + RouteEntry []RouteEntrySetType + } + RouteTableType RouteTableType + CreationTime util.ISO6801Time +} + +type DescribeRouteTablesResponse struct { + common.Response + common.PaginationResult + RouteTables struct { + RouteTable []RouteTableSetType + } +} + +// DescribeRouteTables describes Virtual Routers +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/routertable&describeroutetables +func (client *Client) DescribeRouteTables(args *DescribeRouteTablesArgs) (routeTables []RouteTableSetType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeRouteTablesResponse{} + + err = client.Invoke("DescribeRouteTables", args, &response) + + if err == nil { + return response.RouteTables.RouteTable, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type NextHopType string + +const ( + NextHopIntance = NextHopType("Instance") //Default + NextHopTunnel = NextHopType("Tunnel") +) + +type CreateRouteEntryArgs struct { + RouteTableId string + DestinationCidrBlock string + NextHopType NextHopType + NextHopId string + ClientToken string +} + +type CreateRouteEntryResponse struct { + common.Response +} + +// CreateRouteEntry creates route entry +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/routertable&createrouteentry +func (client *Client) CreateRouteEntry(args *CreateRouteEntryArgs) error { + response := CreateRouteEntryResponse{} + return client.Invoke("CreateRouteEntry", args, &response) +} + +type DeleteRouteEntryArgs struct { + RouteTableId string + DestinationCidrBlock string + NextHopId string +} + +type DeleteRouteEntryResponse struct { + common.Response +} + +// DeleteRouteEntry deletes route entry +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/routertable&deleterouteentry +func (client *Client) DeleteRouteEntry(args *DeleteRouteEntryArgs) error { + response := DeleteRouteEntryResponse{} + return client.Invoke("DeleteRouteEntry", args, &response) +} + +// WaitForAllRouteEntriesAvailable waits for all route entries to Available status +func (client *Client) WaitForAllRouteEntriesAvailable(vrouterId string, routeTableId string, timeout int) error { + if timeout <= 0 { + timeout = DefaultTimeout + } + args := DescribeRouteTablesArgs{ + VRouterId: vrouterId, + RouteTableId: routeTableId, + } + for { + + routeTables, _, err := client.DescribeRouteTables(&args) + + if err != nil { + return err + } + if len(routeTables) == 0 { + return common.GetClientErrorFromString("Not found") + } + success := true + + loop: + for _, routeTable := range routeTables { + for _, routeEntry := range routeTable.RouteEntrys.RouteEntry { + if routeEntry.Status != RouteEntryStatusAvailable { + success = false + break loop + } + } + } + if success { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go new file mode 100644 index 0000000000..ef057393f6 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go @@ -0,0 +1,272 @@ +package ecs + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type NicType string + +const ( + NicTypeInternet = NicType("internet") + NicTypeIntranet = NicType("intranet") +) + +type IpProtocol string + +const ( + IpProtocolAll = IpProtocol("all") + IpProtocolTCP = IpProtocol("tcp") + IpProtocolUDP = IpProtocol("udp") + IpProtocolICMP = IpProtocol("icmp") + IpProtocolGRE = IpProtocol("gre") +) + +type PermissionPolicy string + +const ( + PermissionPolicyAccept = PermissionPolicy("accept") + PermissionPolicyDrop = PermissionPolicy("drop") +) + +type DescribeSecurityGroupAttributeArgs struct { + SecurityGroupId string + RegionId common.Region + NicType NicType //enum for internet (default) |intranet +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&permissiontype +type PermissionType struct { + IpProtocol IpProtocol + PortRange string + SourceCidrIp string + SourceGroupId string + SourceGroupOwnerAccount string + DestCidrIp string + DestGroupId string + DestGroupOwnerAccount string + Policy PermissionPolicy + NicType NicType + Priority int + Direction string + Description string +} + +type DescribeSecurityGroupAttributeResponse struct { + common.Response + + SecurityGroupId string + SecurityGroupName string + RegionId common.Region + Description string + Permissions struct { + Permission []PermissionType + } + VpcId string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&describesecuritygroupattribute +func (client *Client) DescribeSecurityGroupAttribute(args *DescribeSecurityGroupAttributeArgs) (response *DescribeSecurityGroupAttributeResponse, err error) { + response = &DescribeSecurityGroupAttributeResponse{} + err = client.Invoke("DescribeSecurityGroupAttribute", args, response) + if err != nil { + return nil, err + } + return response, nil +} + +type DescribeSecurityGroupsArgs struct { + RegionId common.Region + VpcId string + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&securitygroupitemtype +type SecurityGroupItemType struct { + SecurityGroupId string + SecurityGroupName string + Description string + VpcId string + CreationTime util.ISO6801Time +} + +type DescribeSecurityGroupsResponse struct { + common.Response + common.PaginationResult + + RegionId common.Region + SecurityGroups struct { + SecurityGroup []SecurityGroupItemType + } +} + +// DescribeSecurityGroups describes security groups +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&describesecuritygroups +func (client *Client) DescribeSecurityGroups(args *DescribeSecurityGroupsArgs) (securityGroupItems []SecurityGroupItemType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeSecurityGroupsResponse{} + + err = client.Invoke("DescribeSecurityGroups", args, &response) + + if err != nil { + return nil, nil, err + } + + return response.SecurityGroups.SecurityGroup, &response.PaginationResult, nil +} + +type CreateSecurityGroupArgs struct { + RegionId common.Region + SecurityGroupName string + Description string + VpcId string + ClientToken string +} + +type CreateSecurityGroupResponse struct { + common.Response + + SecurityGroupId string +} + +// CreateSecurityGroup creates security group +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&createsecuritygroup +func (client *Client) CreateSecurityGroup(args *CreateSecurityGroupArgs) (securityGroupId string, err error) { + response := CreateSecurityGroupResponse{} + err = client.Invoke("CreateSecurityGroup", args, &response) + if err != nil { + return "", err + } + return response.SecurityGroupId, err +} + +type DeleteSecurityGroupArgs struct { + RegionId common.Region + SecurityGroupId string +} + +type DeleteSecurityGroupResponse struct { + common.Response +} + +// DeleteSecurityGroup deletes security group +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&deletesecuritygroup +func (client *Client) DeleteSecurityGroup(regionId common.Region, securityGroupId string) error { + args := DeleteSecurityGroupArgs{ + RegionId: regionId, + SecurityGroupId: securityGroupId, + } + response := DeleteSecurityGroupResponse{} + err := client.Invoke("DeleteSecurityGroup", &args, &response) + return err +} + +type ModifySecurityGroupAttributeArgs struct { + RegionId common.Region + SecurityGroupId string + SecurityGroupName string + Description string +} + +type ModifySecurityGroupAttributeResponse struct { + common.Response +} + +// ModifySecurityGroupAttribute modifies attribute of security group +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&modifysecuritygroupattribute +func (client *Client) ModifySecurityGroupAttribute(args *ModifySecurityGroupAttributeArgs) error { + response := ModifySecurityGroupAttributeResponse{} + err := client.Invoke("ModifySecurityGroupAttribute", args, &response) + return err +} + +type AuthorizeSecurityGroupArgs struct { + SecurityGroupId string + RegionId common.Region + IpProtocol IpProtocol + PortRange string + SourceGroupId string + SourceGroupOwnerAccount string + SourceGroupOwnerID string + SourceCidrIp string // IPv4 only, default 0.0.0.0/0 + Policy PermissionPolicy // enum of accept (default) | drop + Priority int // 1 - 100, default 1 + NicType NicType // enum of internet | intranet (default) +} + +type AuthorizeSecurityGroupResponse struct { + common.Response +} + +// AuthorizeSecurityGroup authorize permissions to security group +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&authorizesecuritygroup +func (client *Client) AuthorizeSecurityGroup(args *AuthorizeSecurityGroupArgs) error { + response := AuthorizeSecurityGroupResponse{} + err := client.Invoke("AuthorizeSecurityGroup", args, &response) + return err +} + +type RevokeSecurityGroupArgs struct { + AuthorizeSecurityGroupArgs +} + +type RevokeSecurityGroupResponse struct { + common.Response +} + +// You can read doc at https://help.aliyun.com/document_detail/25557.html?spm=5176.doc25554.6.755.O6Tjz0 +func (client *Client) RevokeSecurityGroup(args *RevokeSecurityGroupArgs) error { + response := RevokeSecurityGroupResponse{} + err := client.Invoke("RevokeSecurityGroup", args, &response) + return err +} + +type AuthorizeSecurityGroupEgressArgs struct { + SecurityGroupId string + RegionId common.Region + IpProtocol IpProtocol + PortRange string + DestGroupId string + DestGroupOwnerAccount string + DestGroupOwnerId string + DestCidrIp string // IPv4 only, default 0.0.0.0/0 + Policy PermissionPolicy // enum of accept (default) | drop + Priority int // 1 - 100, default 1 + NicType NicType // enum of internet | intranet (default) +} + +type AuthorizeSecurityGroupEgressResponse struct { + common.Response +} + +// AuthorizeSecurityGroup authorize permissions to security group +// +// You can read doc at https://help.aliyun.com/document_detail/25560.html +func (client *Client) AuthorizeSecurityGroupEgress(args *AuthorizeSecurityGroupEgressArgs) error { + response := AuthorizeSecurityGroupEgressResponse{} + err := client.Invoke("AuthorizeSecurityGroupEgress", args, &response) + return err +} + +type RevokeSecurityGroupEgressArgs struct { + AuthorizeSecurityGroupEgressArgs +} + +type RevokeSecurityGroupEgressResponse struct { + common.Response +} + +// You can read doc at https://help.aliyun.com/document_detail/25561.html?spm=5176.doc25557.6.759.qcR4Az +func (client *Client) RevokeSecurityGroupEgress(args *RevokeSecurityGroupEgressArgs) error { + response := RevokeSecurityGroupEgressResponse{} + err := client.Invoke("RevokeSecurityGroupEgress", args, &response) + return err +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go b/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go new file mode 100644 index 0000000000..fb6f9c8e10 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go @@ -0,0 +1,131 @@ +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type DescribeSnapshotsArgs struct { + RegionId common.Region + InstanceId string + DiskId string + SnapshotIds []string //["s-xxxxxxxxx", "s-yyyyyyyyy", ..."s-zzzzzzzzz"] + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&snapshottype +type SnapshotType struct { + SnapshotId string + SnapshotName string + Description string + Progress string + SourceDiskId string + SourceDiskSize int + SourceDiskType string //enum for System | Data + ProductCode string + CreationTime util.ISO6801Time +} + +type DescribeSnapshotsResponse struct { + common.Response + common.PaginationResult + Snapshots struct { + Snapshot []SnapshotType + } +} + +// DescribeSnapshots describe snapshots +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/snapshot&describesnapshots +func (client *Client) DescribeSnapshots(args *DescribeSnapshotsArgs) (snapshots []SnapshotType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeSnapshotsResponse{} + + err = client.Invoke("DescribeSnapshots", args, &response) + + if err != nil { + return nil, nil, err + } + return response.Snapshots.Snapshot, &response.PaginationResult, nil + +} + +type DeleteSnapshotArgs struct { + SnapshotId string +} + +type DeleteSnapshotResponse struct { + common.Response +} + +// DeleteSnapshot deletes snapshot +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/snapshot&deletesnapshot +func (client *Client) DeleteSnapshot(snapshotId string) error { + args := DeleteSnapshotArgs{SnapshotId: snapshotId} + response := DeleteSnapshotResponse{} + + return client.Invoke("DeleteSnapshot", &args, &response) +} + +type CreateSnapshotArgs struct { + DiskId string + SnapshotName string + Description string + ClientToken string +} + +type CreateSnapshotResponse struct { + common.Response + SnapshotId string +} + +// CreateSnapshot creates a new snapshot +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/snapshot&createsnapshot +func (client *Client) CreateSnapshot(args *CreateSnapshotArgs) (snapshotId string, err error) { + + response := CreateSnapshotResponse{} + + err = client.Invoke("CreateSnapshot", args, &response) + if err == nil { + snapshotId = response.SnapshotId + } + return snapshotId, err +} + +// Default timeout value for WaitForSnapShotReady method +const SnapshotDefaultTimeout = 120 + +// WaitForSnapShotReady waits for snapshot ready +func (client *Client) WaitForSnapShotReady(regionId common.Region, snapshotId string, timeout int) error { + if timeout <= 0 { + timeout = SnapshotDefaultTimeout + } + for { + args := DescribeSnapshotsArgs{ + RegionId: regionId, + SnapshotIds: []string{snapshotId}, + } + + snapshots, _, err := client.DescribeSnapshots(&args) + if err != nil { + return err + } + if snapshots == nil || len(snapshots) == 0 { + return common.GetClientErrorFromString("Not found") + } + if snapshots[0].Progress == "100%" { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/tags.go b/vendor/github.com/denverdino/aliyungo/ecs/tags.go new file mode 100644 index 0000000000..5ffd4931aa --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/tags.go @@ -0,0 +1,120 @@ +package ecs + +import "github.com/denverdino/aliyungo/common" + +type TagResourceType string + +const ( + TagResourceImage = TagResourceType("image") + TagResourceInstance = TagResourceType("instance") + TagResourceSnapshot = TagResourceType("snapshot") + TagResourceDisk = TagResourceType("disk") +) + +type AddTagsArgs struct { + ResourceId string + ResourceType TagResourceType //image, instance, snapshot or disk + RegionId common.Region + Tag map[string]string +} + +type AddTagsResponse struct { + common.Response +} + +// AddTags Add tags to resource +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&addtags +func (client *Client) AddTags(args *AddTagsArgs) error { + response := AddTagsResponse{} + err := client.Invoke("AddTags", args, &response) + return err +} + +type RemoveTagsArgs struct { + ResourceId string + ResourceType TagResourceType //image, instance, snapshot or disk + RegionId common.Region + Tag map[string]string +} + +type RemoveTagsResponse struct { + common.Response +} + +// RemoveTags remove tags to resource +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&removetags +func (client *Client) RemoveTags(args *RemoveTagsArgs) error { + response := RemoveTagsResponse{} + err := client.Invoke("RemoveTags", args, &response) + return err +} + +type ResourceItemType struct { + ResourceId string + ResourceType TagResourceType + RegionId common.Region +} + +type DescribeResourceByTagsArgs struct { + ResourceType TagResourceType //image, instance, snapshot or disk + RegionId common.Region + Tag map[string]string + common.Pagination +} + +type DescribeResourceByTagsResponse struct { + common.Response + common.PaginationResult + Resources struct { + Resource []ResourceItemType + } +} + +// DescribeResourceByTags describe resource by tags +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&describeresourcebytags +func (client *Client) DescribeResourceByTags(args *DescribeResourceByTagsArgs) (resources []ResourceItemType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeResourceByTagsResponse{} + err = client.Invoke("DescribeResourceByTags", args, &response) + if err != nil { + return nil, nil, err + } + return response.Resources.Resource, &response.PaginationResult, nil +} + +type TagItemType struct { + TagKey string + TagValue string +} + +type DescribeTagsArgs struct { + RegionId common.Region + ResourceType TagResourceType //image, instance, snapshot or disk + ResourceId string + Tag map[string]string + common.Pagination +} + +type DescribeTagsResponse struct { + common.Response + common.PaginationResult + Tags struct { + Tag []TagItemType + } +} + +// DescribeResourceByTags describe resource by tags +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&describeresourcebytags +func (client *Client) DescribeTags(args *DescribeTagsArgs) (tags []TagItemType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeTagsResponse{} + err = client.Invoke("DescribeTags", args, &response) + if err != nil { + return nil, nil, err + } + return response.Tags.Tag, &response.PaginationResult, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/vpcs.go b/vendor/github.com/denverdino/aliyungo/ecs/vpcs.go new file mode 100644 index 0000000000..7a62857cd1 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/vpcs.go @@ -0,0 +1,151 @@ +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type CreateVpcArgs struct { + RegionId common.Region + CidrBlock string //192.168.0.0/16 or 172.16.0.0/16 (default) + VpcName string + Description string + ClientToken string +} + +type CreateVpcResponse struct { + common.Response + VpcId string + VRouterId string + RouteTableId string +} + +// CreateVpc creates Virtual Private Cloud +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&createvpc +func (client *Client) CreateVpc(args *CreateVpcArgs) (resp *CreateVpcResponse, err error) { + response := CreateVpcResponse{} + err = client.Invoke("CreateVpc", args, &response) + if err != nil { + return nil, err + } + return &response, err +} + +type DeleteVpcArgs struct { + VpcId string +} + +type DeleteVpcResponse struct { + common.Response +} + +// DeleteVpc deletes Virtual Private Cloud +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&deletevpc +func (client *Client) DeleteVpc(vpcId string) error { + args := DeleteVpcArgs{ + VpcId: vpcId, + } + response := DeleteVpcResponse{} + return client.Invoke("DeleteVpc", &args, &response) +} + +type VpcStatus string + +const ( + VpcStatusPending = VpcStatus("Pending") + VpcStatusAvailable = VpcStatus("Available") +) + +type DescribeVpcsArgs struct { + VpcId string + RegionId common.Region + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vpcsettype +type VpcSetType struct { + VpcId string + RegionId common.Region + Status VpcStatus // enum Pending | Available + VpcName string + VSwitchIds struct { + VSwitchId []string + } + CidrBlock string + VRouterId string + Description string + CreationTime util.ISO6801Time +} + +type DescribeVpcsResponse struct { + common.Response + common.PaginationResult + Vpcs struct { + Vpc []VpcSetType + } +} + +// DescribeInstanceStatus describes instance status +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&describevpcs +func (client *Client) DescribeVpcs(args *DescribeVpcsArgs) (vpcs []VpcSetType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeVpcsResponse{} + + err = client.Invoke("DescribeVpcs", args, &response) + + if err == nil { + return response.Vpcs.Vpc, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type ModifyVpcAttributeArgs struct { + VpcId string + VpcName string + Description string +} + +type ModifyVpcAttributeResponse struct { + common.Response +} + +// ModifyVpcAttribute modifies attribute of Virtual Private Cloud +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&modifyvpcattribute +func (client *Client) ModifyVpcAttribute(args *ModifyVpcAttributeArgs) error { + response := ModifyVpcAttributeResponse{} + return client.Invoke("ModifyVpcAttribute", args, &response) +} + +// WaitForInstance waits for instance to given status +func (client *Client) WaitForVpcAvailable(regionId common.Region, vpcId string, timeout int) error { + if timeout <= 0 { + timeout = DefaultTimeout + } + args := DescribeVpcsArgs{ + RegionId: regionId, + VpcId: vpcId, + } + for { + vpcs, _, err := client.DescribeVpcs(&args) + if err != nil { + return err + } + if len(vpcs) > 0 && vpcs[0].Status == VpcStatusAvailable { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/vrouters.go b/vendor/github.com/denverdino/aliyungo/ecs/vrouters.go new file mode 100644 index 0000000000..059a324bd9 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/vrouters.go @@ -0,0 +1,68 @@ +package ecs + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type DescribeVRoutersArgs struct { + VRouterId string + RegionId common.Region + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vroutersettype +type VRouterSetType struct { + VRouterId string + RegionId common.Region + VpcId string + RouteTableIds struct { + RouteTableId []string + } + VRouterName string + Description string + CreationTime util.ISO6801Time +} + +type DescribeVRoutersResponse struct { + common.Response + common.PaginationResult + VRouters struct { + VRouter []VRouterSetType + } +} + +// DescribeVRouters describes Virtual Routers +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vrouter&describevrouters +func (client *Client) DescribeVRouters(args *DescribeVRoutersArgs) (vrouters []VRouterSetType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeVRoutersResponse{} + + err = client.Invoke("DescribeVRouters", args, &response) + + if err == nil { + return response.VRouters.VRouter, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type ModifyVRouterAttributeArgs struct { + VRouterId string + VRouterName string + Description string +} + +type ModifyVRouterAttributeResponse struct { + common.Response +} + +// ModifyVRouterAttribute modifies attribute of Virtual Router +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vrouter&modifyvrouterattribute +func (client *Client) ModifyVRouterAttribute(args *ModifyVRouterAttributeArgs) error { + response := ModifyVRouterAttributeResponse{} + return client.Invoke("ModifyVRouterAttribute", args, &response) +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/vswitches.go b/vendor/github.com/denverdino/aliyungo/ecs/vswitches.go new file mode 100644 index 0000000000..8bf10394a7 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/vswitches.go @@ -0,0 +1,152 @@ +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type CreateVSwitchArgs struct { + ZoneId string + CidrBlock string + VpcId string + VSwitchName string + Description string + ClientToken string +} + +type CreateVSwitchResponse struct { + common.Response + VSwitchId string +} + +// CreateVSwitch creates Virtual Switch +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&createvswitch +func (client *Client) CreateVSwitch(args *CreateVSwitchArgs) (vswitchId string, err error) { + response := CreateVSwitchResponse{} + err = client.Invoke("CreateVSwitch", args, &response) + if err != nil { + return "", err + } + return response.VSwitchId, err +} + +type DeleteVSwitchArgs struct { + VSwitchId string +} + +type DeleteVSwitchResponse struct { + common.Response +} + +// DeleteVSwitch deletes Virtual Switch +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&deletevswitch +func (client *Client) DeleteVSwitch(VSwitchId string) error { + args := DeleteVSwitchArgs{ + VSwitchId: VSwitchId, + } + response := DeleteVSwitchResponse{} + return client.Invoke("DeleteVSwitch", &args, &response) +} + +type DescribeVSwitchesArgs struct { + VpcId string + VSwitchId string + ZoneId string + common.Pagination +} + +type VSwitchStatus string + +const ( + VSwitchStatusPending = VSwitchStatus("Pending") + VSwitchStatusAvailable = VSwitchStatus("Available") +) + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vswitchsettype +type VSwitchSetType struct { + VSwitchId string + VpcId string + Status VSwitchStatus // enum Pending | Available + CidrBlock string + ZoneId string + AvailableIpAddressCount int + Description string + VSwitchName string + CreationTime util.ISO6801Time +} + +type DescribeVSwitchesResponse struct { + common.Response + common.PaginationResult + VSwitches struct { + VSwitch []VSwitchSetType + } +} + +// DescribeVSwitches describes Virtual Switches +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&describevswitches +func (client *Client) DescribeVSwitches(args *DescribeVSwitchesArgs) (vswitches []VSwitchSetType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeVSwitchesResponse{} + + err = client.Invoke("DescribeVSwitches", args, &response) + + if err == nil { + return response.VSwitches.VSwitch, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type ModifyVSwitchAttributeArgs struct { + VSwitchId string + VSwitchName string + Description string +} + +type ModifyVSwitchAttributeResponse struct { + common.Response +} + +// ModifyVSwitchAttribute modifies attribute of Virtual Private Cloud +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&modifyvswitchattribute +func (client *Client) ModifyVSwitchAttribute(args *ModifyVSwitchAttributeArgs) error { + response := ModifyVSwitchAttributeResponse{} + return client.Invoke("ModifyVSwitchAttribute", args, &response) +} + +// WaitForVSwitchAvailable waits for VSwitch to given status +func (client *Client) WaitForVSwitchAvailable(vpcId string, vswitchId string, timeout int) error { + if timeout <= 0 { + timeout = DefaultTimeout + } + args := DescribeVSwitchesArgs{ + VpcId: vpcId, + VSwitchId: vswitchId, + } + for { + vswitches, _, err := client.DescribeVSwitches(&args) + if err != nil { + return err + } + if len(vswitches) == 0 { + return common.GetClientErrorFromString("Not found") + } + if vswitches[0].Status == VSwitchStatusAvailable { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/zones.go b/vendor/github.com/denverdino/aliyungo/ecs/zones.go new file mode 100644 index 0000000000..4818c760f8 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/zones.go @@ -0,0 +1,65 @@ +package ecs + +import "github.com/denverdino/aliyungo/common" + +type ResourceType string + +const ( + ResourceTypeInstance = ResourceType("Instance") + ResourceTypeDisk = ResourceType("Disk") + ResourceTypeVSwitch = ResourceType("VSwitch") + ResourceTypeIOOptimizedInstance = ResourceType("IoOptimized") +) + +type DescribeZonesArgs struct { + RegionId common.Region +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&availableresourcecreationtype +type AvailableResourceCreationType struct { + ResourceTypes []ResourceType //enum for Instance, Disk, VSwitch +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&availablediskcategoriestype +type AvailableDiskCategoriesType struct { + DiskCategories []DiskCategory //enum for cloud, ephemeral, ephemeral_ssd +} + +type AvailableInstanceTypesType struct { + InstanceTypes []string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&zonetype +type ZoneType struct { + ZoneId string + LocalName string + AvailableInstanceTypes AvailableInstanceTypesType + AvailableResourceCreation AvailableResourceCreationType + AvailableDiskCategories AvailableDiskCategoriesType +} + +type DescribeZonesResponse struct { + common.Response + Zones struct { + Zone []ZoneType + } +} + +// DescribeZones describes zones +func (client *Client) DescribeZones(regionId common.Region) (zones []ZoneType, err error) { + args := DescribeZonesArgs{ + RegionId: regionId, + } + response := DescribeZonesResponse{} + + err = client.Invoke("DescribeZones", &args, &response) + + if err == nil { + return response.Zones.Zone, nil + } + + return []ZoneType{}, err +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/certificates.go b/vendor/github.com/denverdino/aliyungo/slb/certificates.go new file mode 100644 index 0000000000..5cbc4ac093 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/certificates.go @@ -0,0 +1,108 @@ +package slb + +import "github.com/denverdino/aliyungo/common" + +type UploadServerCertificateArgs struct { + RegionId common.Region + ServerCertificate string + ServerCertificateName string + PrivateKey string +} + +type UploadServerCertificateResponse struct { + common.Response + ServerCertificateId string + ServerCertificateName string + Fingerprint string +} + +// UploadServerCertificate Upload server certificate +// +// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&UploadServerCertificate +func (client *Client) UploadServerCertificate(args *UploadServerCertificateArgs) (response *UploadServerCertificateResponse, err error) { + response = &UploadServerCertificateResponse{} + err = client.Invoke("UploadServerCertificate", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type DeleteServerCertificateArgs struct { + RegionId common.Region + ServerCertificateId string +} + +type DeleteServerCertificateResponse struct { + common.Response +} + +// DeleteServerCertificate Delete server certificate +// +// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&DeleteServerCertificate +func (client *Client) DeleteServerCertificate(regionId common.Region, serverCertificateId string) (err error) { + args := &DeleteServerCertificateArgs{ + RegionId: regionId, + ServerCertificateId: serverCertificateId, + } + response := &DeleteServerCertificateResponse{} + return client.Invoke("DeleteServerCertificate", args, response) +} + +type SetServerCertificateNameArgs struct { + RegionId common.Region + ServerCertificateId string + ServerCertificateName string +} + +type SetServerCertificateNameResponse struct { + common.Response +} + +// SetServerCertificateName Set name of server certificate +// +// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&SetServerCertificateName +func (client *Client) SetServerCertificateName(regionId common.Region, serverCertificateId string, name string) (err error) { + args := &SetServerCertificateNameArgs{ + RegionId: regionId, + ServerCertificateId: serverCertificateId, + ServerCertificateName: name, + } + response := &SetServerCertificateNameResponse{} + return client.Invoke("SetServerCertificateName", args, response) +} + +type DescribeServerCertificatesArgs struct { + RegionId common.Region + ServerCertificateId string +} + +type ServerCertificateType struct { + RegionId common.Region + ServerCertificateId string + ServerCertificateName string + Fingerprint string +} + +type DescribeServerCertificatesResponse struct { + common.Response + ServerCertificates struct { + ServerCertificate []ServerCertificateType + } +} + +// DescribeServerCertificates Describe server certificates +// +// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&DescribeServerCertificates +func (client *Client) DescribeServerCertificatesArgs(regionId common.Region, serverCertificateId string) (serverCertificates []ServerCertificateType, err error) { + args := &DescribeServerCertificatesArgs{ + RegionId: regionId, + ServerCertificateId: serverCertificateId, + } + response := &DescribeServerCertificatesResponse{} + err = client.Invoke("DescribeServerCertificates", args, response) + if err != nil { + return nil, err + } + return response.ServerCertificates.ServerCertificate, err +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/client.go b/vendor/github.com/denverdino/aliyungo/slb/client.go new file mode 100644 index 0000000000..8dd26b6913 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/client.go @@ -0,0 +1,31 @@ +package slb + +import ( + "github.com/denverdino/aliyungo/common" + "os" +) + +type Client struct { + common.Client +} + +const ( + // SLBDefaultEndpoint is the default API endpoint of SLB services + SLBDefaultEndpoint = "https://slb.aliyuncs.com" + SLBAPIVersion = "2014-05-15" +) + +// NewClient creates a new instance of ECS client +func NewClient(accessKeyId, accessKeySecret string) *Client { + endpoint := os.Getenv("SLB_ENDPOINT") + if endpoint == "" { + endpoint = SLBDefaultEndpoint + } + return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret) +} + +func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client { + client := &Client{} + client.Init(endpoint, SLBAPIVersion, accessKeyId, accessKeySecret) + return client +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/listeners.go b/vendor/github.com/denverdino/aliyungo/slb/listeners.go new file mode 100644 index 0000000000..d435576190 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/listeners.go @@ -0,0 +1,495 @@ +package slb + +import ( + "fmt" + "strings" + "time" + + "github.com/denverdino/aliyungo/common" +) + +type ListenerStatus string + +const ( + Starting = ListenerStatus("starting") + Running = ListenerStatus("running") + Configuring = ListenerStatus("configuring") + Stopping = ListenerStatus("stopping") + Stopped = ListenerStatus("stopped") +) + +type SchedulerType string + +const ( + WRRScheduler = SchedulerType("wrr") + WLCScheduler = SchedulerType("wlc") +) + +type FlagType string + +const ( + OnFlag = FlagType("on") + OffFlag = FlagType("off") +) + +type StickySessionType string + +const ( + InsertStickySessionType = StickySessionType("insert") + ServerStickySessionType = StickySessionType("server") +) + +const BackendServerPort = -520 + +type HealthCheckHttpCodeType string + +const ( + HTTP_2XX = HealthCheckHttpCodeType("http_2xx") + HTTP_3XX = HealthCheckHttpCodeType("http_3xx") + HTTP_4XX = HealthCheckHttpCodeType("http_4xx") + HTTP_5XX = HealthCheckHttpCodeType("http_5xx") +) + +func EncodeHealthCheckHttpCodeType(healthCheckHttpCodes []HealthCheckHttpCodeType) (HealthCheckHttpCodeType, error) { + code := "" + + if nil == healthCheckHttpCodes || len(healthCheckHttpCodes) < 1 { + return "", fmt.Errorf("Invalid size of healthCheckHttpCodes") + } + + for _, healthCheckHttpCode := range healthCheckHttpCodes { + if strings.EqualFold(string(HTTP_2XX), string(healthCheckHttpCode)) || + strings.EqualFold(string(HTTP_3XX), string(healthCheckHttpCode)) || + strings.EqualFold(string(HTTP_4XX), string(healthCheckHttpCode)) || + strings.EqualFold(string(HTTP_5XX), string(healthCheckHttpCode)) { + if "" == code { + code = string(healthCheckHttpCode) + } else { + if strings.Contains(code, string(healthCheckHttpCode)) { + return "", fmt.Errorf("Duplicates healthCheckHttpCode(%v in %v)", healthCheckHttpCode, healthCheckHttpCodes) + } + code += code + "," + string(healthCheckHttpCode) + } + } else { + return "", fmt.Errorf("Invalid healthCheckHttpCode(%v in %v)", healthCheckHttpCode, healthCheckHttpCodes) + } + } + return HealthCheckHttpCodeType(code), nil +} + +type CommonLoadBalancerListenerResponse struct { + common.Response +} + +type HTTPListenerType struct { + LoadBalancerId string + ListenerPort int + BackendServerPort int + Bandwidth int + Scheduler SchedulerType + StickySession FlagType + StickySessionType StickySessionType + CookieTimeout int + Cookie string + HealthCheck FlagType + HealthCheckDomain string + HealthCheckURI string + HealthCheckConnectPort int + HealthyThreshold int + UnhealthyThreshold int + HealthCheckTimeout int + HealthCheckInterval int + HealthCheckHttpCode HealthCheckHttpCodeType + VServerGroupId string + Gzip FlagType +} +type CreateLoadBalancerHTTPListenerArgs HTTPListenerType + +// CreateLoadBalancerHTTPListener create HTTP listener on loadbalancer +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerHTTPListener +func (client *Client) CreateLoadBalancerHTTPListener(args *CreateLoadBalancerHTTPListenerArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("CreateLoadBalancerHTTPListener", args, response) + return err +} + +type HTTPSListenerType struct { + HTTPListenerType + ServerCertificateId string +} + +type CreateLoadBalancerHTTPSListenerArgs HTTPSListenerType + +// CreateLoadBalancerHTTPSListener create HTTPS listener on loadbalancer +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerHTTPSListener +func (client *Client) CreateLoadBalancerHTTPSListener(args *CreateLoadBalancerHTTPSListenerArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("CreateLoadBalancerHTTPSListener", args, response) + return err +} + +type HealthCheckType string + +const ( + TCPHealthCheckType = HealthCheckType("tcp") + HTTPHealthCheckType = HealthCheckType("http") +) + +type TCPListenerType struct { + LoadBalancerId string + ListenerPort int + BackendServerPort int + Bandwidth int + Scheduler SchedulerType + PersistenceTimeout int + HealthCheckType HealthCheckType + HealthCheckDomain string + HealthCheckURI string + HealthCheckConnectPort int + HealthyThreshold int + UnhealthyThreshold int + HealthCheckTimeout int + HealthCheckInterval int + HealthCheckHttpCode HealthCheckHttpCodeType + VServerGroupId string +} + +type CreateLoadBalancerTCPListenerArgs TCPListenerType + +// CreateLoadBalancerTCPListener create TCP listener on loadbalancer +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerTCPListener +func (client *Client) CreateLoadBalancerTCPListener(args *CreateLoadBalancerTCPListenerArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("CreateLoadBalancerTCPListener", args, response) + return err +} + +type UDPListenerType struct { + LoadBalancerId string + ListenerPort int + BackendServerPort int + Bandwidth int + Scheduler SchedulerType + PersistenceTimeout int + HealthCheckConnectPort int + HealthyThreshold int + UnhealthyThreshold int + HealthCheckTimeout int + HealthCheckInterval int + VServerGroupId string +} +type CreateLoadBalancerUDPListenerArgs UDPListenerType + +// CreateLoadBalancerUDPListener create UDP listener on loadbalancer +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerUDPListener +func (client *Client) CreateLoadBalancerUDPListener(args *CreateLoadBalancerUDPListenerArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("CreateLoadBalancerUDPListener", args, response) + return err +} + +type CommonLoadBalancerListenerArgs struct { + LoadBalancerId string + ListenerPort int +} + +// DeleteLoadBalancerListener Delete listener +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DeleteLoadBalancerListener +func (client *Client) DeleteLoadBalancerListener(loadBalancerId string, port int) (err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("DeleteLoadBalancerListener", args, response) + return err +} + +// StartLoadBalancerListener Start listener +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&StartLoadBalancerListener +func (client *Client) StartLoadBalancerListener(loadBalancerId string, port int) (err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("StartLoadBalancerListener", args, response) + return err +} + +// StopLoadBalancerListener Stop listener +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&StopLoadBalancerListener +func (client *Client) StopLoadBalancerListener(loadBalancerId string, port int) (err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("StopLoadBalancerListener", args, response) + return err +} + +type AccessControlStatus string + +const ( + OpenWhileList = AccessControlStatus("open_white_list") + Close = AccessControlStatus("close") +) + +type SetListenerAccessControlStatusArgs struct { + LoadBalancerId string + ListenerPort int + AccessControlStatus AccessControlStatus +} + +// SetListenerAccessControlStatus Set listener access control status +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetListenerAccessControlStatus +func (client *Client) SetListenerAccessControlStatus(loadBalancerId string, port int, status AccessControlStatus) (err error) { + args := &SetListenerAccessControlStatusArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + AccessControlStatus: status, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("SetListenerAccessControlStatus", args, response) + return err +} + +type CommonListenerWhiteListItemArgs struct { + LoadBalancerId string + ListenerPort int + SourceItems string +} + +// AddListenerWhiteListItem Add listener white-list item +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&AddListenerWhiteListItem +func (client *Client) AddListenerWhiteListItem(loadBalancerId string, port int, sourceItems string) (err error) { + args := &CommonListenerWhiteListItemArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + SourceItems: sourceItems, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("AddListenerWhiteListItem", args, response) + return err +} + +// RemoveListenerWhiteListItem Remove listener white-list item +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&RemoveListenerWhiteListItem +func (client *Client) RemoveListenerWhiteListItem(loadBalancerId string, port int, sourceItems string) (err error) { + args := &CommonListenerWhiteListItemArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + SourceItems: sourceItems, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("RemoveListenerWhiteListItem", args, response) + return err +} + +type SetLoadBalancerHTTPListenerAttributeArgs CreateLoadBalancerHTTPListenerArgs + +// SetLoadBalancerHTTPListenerAttribute Set HTTP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerHTTPListenerAttribute +func (client *Client) SetLoadBalancerHTTPListenerAttribute(args *SetLoadBalancerHTTPListenerAttributeArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("SetLoadBalancerHTTPListenerAttribute", args, response) + return err +} + +type SetLoadBalancerHTTPSListenerAttributeArgs CreateLoadBalancerHTTPSListenerArgs + +// SetLoadBalancerHTTPSListenerAttribute Set HTTPS listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerHTTPSListenerAttribute +func (client *Client) SetLoadBalancerHTTPSListenerAttribute(args *SetLoadBalancerHTTPSListenerAttributeArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("SetLoadBalancerHTTPSListenerAttribute", args, response) + return err +} + +type SetLoadBalancerTCPListenerAttributeArgs CreateLoadBalancerTCPListenerArgs + +// SetLoadBalancerTCPListenerAttribute Set TCP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerTCPListenerAttribute +func (client *Client) SetLoadBalancerTCPListenerAttribute(args *SetLoadBalancerTCPListenerAttributeArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("SetLoadBalancerTCPListenerAttribute", args, response) + return err +} + +type SetLoadBalancerUDPListenerAttributeArgs CreateLoadBalancerUDPListenerArgs + +// SetLoadBalancerUDPListenerAttribute Set UDP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerUDPListenerAttribute +func (client *Client) SetLoadBalancerUDPListenerAttribute(args *SetLoadBalancerUDPListenerAttributeArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("SetLoadBalancerUDPListenerAttribute", args, response) + return err +} + +type DescribeLoadBalancerListenerAttributeResponse struct { + common.Response + Status ListenerStatus +} + +type DescribeLoadBalancerHTTPListenerAttributeResponse struct { + DescribeLoadBalancerListenerAttributeResponse + HTTPListenerType +} + +// DescribeLoadBalancerHTTPListenerAttribute Describe HTTP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerHTTPListenerAttribute +func (client *Client) DescribeLoadBalancerHTTPListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerHTTPListenerAttributeResponse, err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response = &DescribeLoadBalancerHTTPListenerAttributeResponse{} + err = client.Invoke("DescribeLoadBalancerHTTPListenerAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type DescribeLoadBalancerHTTPSListenerAttributeResponse struct { + DescribeLoadBalancerListenerAttributeResponse + HTTPSListenerType +} + +// DescribeLoadBalancerHTTPSListenerAttribute Describe HTTPS listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerHTTPSListenerAttribute +func (client *Client) DescribeLoadBalancerHTTPSListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerHTTPSListenerAttributeResponse, err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response = &DescribeLoadBalancerHTTPSListenerAttributeResponse{} + err = client.Invoke("DescribeLoadBalancerHTTPSListenerAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type DescribeLoadBalancerTCPListenerAttributeResponse struct { + DescribeLoadBalancerListenerAttributeResponse + TCPListenerType +} + +// DescribeLoadBalancerTCPListenerAttribute Describe TCP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerTCPListenerAttribute +func (client *Client) DescribeLoadBalancerTCPListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerTCPListenerAttributeResponse, err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response = &DescribeLoadBalancerTCPListenerAttributeResponse{} + err = client.Invoke("DescribeLoadBalancerTCPListenerAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type DescribeLoadBalancerUDPListenerAttributeResponse struct { + DescribeLoadBalancerListenerAttributeResponse + UDPListenerType +} + +// DescribeLoadBalancerUDPListenerAttribute Describe UDP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerUDPListenerAttribute +func (client *Client) DescribeLoadBalancerUDPListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerUDPListenerAttributeResponse, err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response = &DescribeLoadBalancerUDPListenerAttributeResponse{} + err = client.Invoke("DescribeLoadBalancerUDPListenerAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type ListenerType string + +const ( + UDP = ListenerType("UDP") + TCP = ListenerType("TCP") + HTTP = ListenerType("HTTP") + HTTPS = ListenerType("HTTPS") +) + +const DefaultWaitForInterval = 5 //5 seconds +const DefaultTimeout = 60 //60 seconds + +// WaitForListener waits for listener to given status +func (client *Client) WaitForListener(loadBalancerId string, port int, listenerType ListenerType) (status ListenerStatus, err error) { + timeout := DefaultTimeout + + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + + method := fmt.Sprintf("DescribeLoadBalancer%sListenerAttribute", listenerType) + response := &DescribeLoadBalancerListenerAttributeResponse{} + + for { + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return response.Status, common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + //Sleep first to ensure the previous request is sent + err = client.Invoke(method, args, response) + if err != nil { + return "", err + } + if response.Status == Running || response.Status == Stopped { + break + } + } + return response.Status, nil +} + +type DescribeListenerAccessControlAttributeResponse struct { + common.Response + AccessControlStatus AccessControlStatus + SourceItems string +} + +// DescribeListenerAccessControlAttribute Describe listener access control attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeListenerAccessControlAttribute +func (client *Client) DescribeListenerAccessControlAttribute(loadBalancerId string, port int) (response *DescribeListenerAccessControlAttributeResponse, err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response = &DescribeListenerAccessControlAttributeResponse{} + err = client.Invoke("DescribeListenerAccessControlAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go b/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go new file mode 100644 index 0000000000..d090570cee --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go @@ -0,0 +1,238 @@ +package slb + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type AddressType string + +const ( + InternetAddressType = AddressType("internet") + IntranetAddressType = AddressType("intranet") +) + +type InternetChargeType string + +const ( + PayByBandwidth = InternetChargeType("paybybandwidth") + PayByTraffic = InternetChargeType("paybytraffic") +) + +type CreateLoadBalancerArgs struct { + RegionId common.Region + LoadBalancerName string + AddressType AddressType + VSwitchId string + InternetChargeType InternetChargeType + Bandwidth int + ClientToken string +} + +type CreateLoadBalancerResponse struct { + common.Response + LoadBalancerId string + Address string + NetworkType string + VpcId string + VSwitchId string + LoadBalancerName string +} + +// CreateLoadBalancer create loadbalancer +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&CreateLoadBalancer +func (client *Client) CreateLoadBalancer(args *CreateLoadBalancerArgs) (response *CreateLoadBalancerResponse, err error) { + response = &CreateLoadBalancerResponse{} + err = client.Invoke("CreateLoadBalancer", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type DeleteLoadBalancerArgs struct { + LoadBalancerId string +} + +type DeleteLoadBalancerResponse struct { + common.Response +} + +// DeleteLoadBalancer delete loadbalancer +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DeleteLoadBalancer +func (client *Client) DeleteLoadBalancer(loadBalancerId string) (err error) { + args := &DeleteLoadBalancerArgs{ + LoadBalancerId: loadBalancerId, + } + response := &DeleteLoadBalancerResponse{} + err = client.Invoke("DeleteLoadBalancer", args, response) + return err +} + +type ModifyLoadBalancerInternetSpecArgs struct { + LoadBalancerId string + InternetChargeType InternetChargeType + Bandwidth int +} + +type ModifyLoadBalancerInternetSpecResponse struct { + common.Response +} + +// ModifyLoadBalancerInternetSpec Modify loadbalancer internet spec +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&ModifyLoadBalancerInternetSpec + +func (client *Client) ModifyLoadBalancerInternetSpec(args *ModifyLoadBalancerInternetSpecArgs) (err error) { + response := &ModifyLoadBalancerInternetSpecResponse{} + err = client.Invoke("ModifyLoadBalancerInternetSpec", args, response) + return err +} + +type Status string + +const InactiveStatus = Status("inactive") +const ActiveStatus = Status("active") +const LockedStatus = Status("locked") + +type SetLoadBalancerStatusArgs struct { + LoadBalancerId string + LoadBalancerStatus Status +} + +type SetLoadBalancerStatusResponse struct { + common.Response +} + +// SetLoadBalancerStatus Set loadbalancer status +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&SetLoadBalancerStatus + +func (client *Client) SetLoadBalancerStatus(loadBalancerId string, status Status) (err error) { + args := &SetLoadBalancerStatusArgs{ + LoadBalancerId: loadBalancerId, + LoadBalancerStatus: status, + } + response := &SetLoadBalancerStatusResponse{} + err = client.Invoke("SetLoadBalancerStatus", args, response) + return err +} + +type SetLoadBalancerNameArgs struct { + LoadBalancerId string + LoadBalancerName string +} + +type SetLoadBalancerNameResponse struct { + common.Response +} + +// SetLoadBalancerName Set loadbalancer name +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&SetLoadBalancerName + +func (client *Client) SetLoadBalancerName(loadBalancerId string, name string) (err error) { + args := &SetLoadBalancerNameArgs{ + LoadBalancerId: loadBalancerId, + LoadBalancerName: name, + } + response := &SetLoadBalancerNameResponse{} + err = client.Invoke("SetLoadBalancerName", args, response) + return err +} + +type DescribeLoadBalancersArgs struct { + RegionId common.Region + LoadBalancerId string + LoadBalancerName string + AddressType AddressType + NetworkType string + VpcId string + VSwitchId string + Address string + InternetChargeType InternetChargeType + ServerId string +} + +type ListenerPortAndProtocolType struct { + ListenerPort int + ListenerProtocol string +} + +type BackendServerType struct { + ServerId string + Weight int +} + +type LoadBalancerType struct { + LoadBalancerId string + LoadBalancerName string + LoadBalancerStatus string + Address string + RegionId common.Region + RegionIdAlias string + AddressType AddressType + VSwitchId string + VpcId string + NetworkType string + Bandwidth int + InternetChargeType InternetChargeType + CreateTime string //Why not ISO 6801 + CreateTimeStamp util.ISO6801Time + ListenerPorts struct { + ListenerPort []int + } + ListenerPortsAndProtocol struct { + ListenerPortAndProtocol []ListenerPortAndProtocolType + } + BackendServers struct { + BackendServer []BackendServerType + } +} + +type DescribeLoadBalancersResponse struct { + common.Response + LoadBalancers struct { + LoadBalancer []LoadBalancerType + } +} + +// DescribeLoadBalancers Describe loadbalancers +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DescribeLoadBalancers + +func (client *Client) DescribeLoadBalancers(args *DescribeLoadBalancersArgs) (loadBalancers []LoadBalancerType, err error) { + response := &DescribeLoadBalancersResponse{} + err = client.Invoke("DescribeLoadBalancers", args, response) + if err != nil { + return nil, err + } + return response.LoadBalancers.LoadBalancer, err +} + +type DescribeLoadBalancerAttributeArgs struct { + LoadBalancerId string +} + +type DescribeLoadBalancerAttributeResponse struct { + common.Response + LoadBalancerType +} + +// DescribeLoadBalancerAttribute Describe loadbalancer attribute +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DescribeLoadBalancerAttribute + +func (client *Client) DescribeLoadBalancerAttribute(loadBalancerId string) (loadBalancer *LoadBalancerType, err error) { + args := &DescribeLoadBalancersArgs{ + LoadBalancerId: loadBalancerId, + } + response := &DescribeLoadBalancerAttributeResponse{} + err = client.Invoke("DescribeLoadBalancerAttribute", args, response) + if err != nil { + return nil, err + } + return &response.LoadBalancerType, err +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/regions.go b/vendor/github.com/denverdino/aliyungo/slb/regions.go new file mode 100644 index 0000000000..870ffefe7d --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/regions.go @@ -0,0 +1,34 @@ +package slb + +import "github.com/denverdino/aliyungo/common" + +type DescribeRegionsArgs struct { +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype®iontype +type RegionType struct { + RegionId common.Region + LocalName string +} + +type DescribeRegionsResponse struct { + common.Response + Regions struct { + Region []RegionType + } +} + +// DescribeRegions describes regions +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DescribeRegions +func (client *Client) DescribeRegions() (regions []RegionType, err error) { + response := DescribeRegionsResponse{} + + err = client.Invoke("DescribeRegions", &DescribeRegionsArgs{}, &response) + + if err != nil { + return []RegionType{}, err + } + return response.Regions.Region, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/servers.go b/vendor/github.com/denverdino/aliyungo/slb/servers.go new file mode 100644 index 0000000000..a3fb2a406c --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/servers.go @@ -0,0 +1,122 @@ +package slb + +import ( + "encoding/json" + + "github.com/denverdino/aliyungo/common" +) + +type AddBackendServersArgs struct { + LoadBalancerId string + BackendServers string +} + +type SetBackendServersArgs AddBackendServersArgs + +type AddBackendServersResponse struct { + common.Response + LoadBalancerId string + BackendServers struct { + BackendServer []BackendServerType + } +} + +type SetBackendServersResponse AddBackendServersResponse + + +// SetBackendServers set weight of backend servers + +func (client *Client) SetBackendServers(loadBalancerId string, backendServers []BackendServerType) (result []BackendServerType, err error) { + bytes, _ := json.Marshal(backendServers) + + args := &SetBackendServersArgs{ + LoadBalancerId: loadBalancerId, + BackendServers: string(bytes), + } + response := &SetBackendServersResponse{} + + err = client.Invoke("SetBackendServers", args, response) + if err != nil { + return nil, err + } + return response.BackendServers.BackendServer, err +} + + +// AddBackendServers Add backend servers +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-backendserver&AddBackendServers +func (client *Client) AddBackendServers(loadBalancerId string, backendServers []BackendServerType) (result []BackendServerType, err error) { + + bytes, _ := json.Marshal(backendServers) + + args := &AddBackendServersArgs{ + LoadBalancerId: loadBalancerId, + BackendServers: string(bytes), + } + response := &AddBackendServersResponse{} + + err = client.Invoke("AddBackendServers", args, response) + if err != nil { + return nil, err + } + return response.BackendServers.BackendServer, err +} + +type RemoveBackendServersArgs struct { + LoadBalancerId string + BackendServers []string +} + +type RemoveBackendServersResponse struct { + common.Response + LoadBalancerId string + BackendServers struct { + BackendServer []BackendServerType + } +} + +// RemoveBackendServers Remove backend servers +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-backendserver&RemoveBackendServers +func (client *Client) RemoveBackendServers(loadBalancerId string, backendServers []string) (result []BackendServerType, err error) { + args := &RemoveBackendServersArgs{ + LoadBalancerId: loadBalancerId, + BackendServers: backendServers, + } + response := &RemoveBackendServersResponse{} + err = client.Invoke("RemoveBackendServers", args, response) + if err != nil { + return nil, err + } + return response.BackendServers.BackendServer, err +} + +type HealthStatusType struct { + ServerId string + ServerHealthStatus string +} + +type DescribeHealthStatusArgs struct { + LoadBalancerId string + ListenerPort int +} + +type DescribeHealthStatusResponse struct { + common.Response + BackendServers struct { + BackendServer []HealthStatusType + } +} + +// DescribeHealthStatus Describe health status +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-backendserver&DescribeHealthStatus +func (client *Client) DescribeHealthStatus(args *DescribeHealthStatusArgs) (response *DescribeHealthStatusResponse, err error) { + response = &DescribeHealthStatusResponse{} + err = client.Invoke("DescribeHealthStatus", args, response) + if err != nil { + return nil, err + } + return response, err +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/tags.go b/vendor/github.com/denverdino/aliyungo/slb/tags.go new file mode 100644 index 0000000000..4128559d4a --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/tags.go @@ -0,0 +1,85 @@ +package slb + +import "github.com/denverdino/aliyungo/common" + +type TagItem struct { + TagKey string + TagValue string +} + +type AddTagsArgs struct { + RegionId common.Region + LoadBalancerID string + Tags string +} + +type AddTagsResponse struct { + common.Response +} + +// AddTags Add tags to resource +// +// You can read doc at https://help.aliyun.com/document_detail/42871.html +func (client *Client) AddTags(args *AddTagsArgs) error { + response := AddTagsResponse{} + err := client.Invoke("AddTags", args, &response) + if err != nil { + return err + } + return err +} + +type RemoveTagsArgs struct { + RegionId common.Region + LoadBalancerID string + Tags string +} + +type RemoveTagsResponse struct { + common.Response +} + +// RemoveTags remove tags to resource +// +// You can read doc at https://help.aliyun.com/document_detail/42872.html +func (client *Client) RemoveTags(args *RemoveTagsArgs) error { + response := RemoveTagsResponse{} + err := client.Invoke("RemoveTags", args, &response) + if err != nil { + return err + } + return err +} + +type TagItemType struct { + TagItem + InstanceCount int +} + +type DescribeTagsArgs struct { + RegionId common.Region + LoadBalancerID string + Tags string + common.Pagination +} + +type DescribeTagsResponse struct { + common.Response + common.PaginationResult + TagSets struct { + TagSet []TagItemType + } +} + +// DescribeResourceByTags describe resource by tags +// +// You can read doc at https://help.aliyun.com/document_detail/42873.html?spm=5176.doc42872.6.267.CP1iWu +func (client *Client) DescribeTags(args *DescribeTagsArgs) (tags []TagItemType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeTagsResponse{} + err = client.Invoke("DescribeTags", args, &response) + if err != nil { + return nil, nil, err + } + return response.TagSets.TagSet, &response.PaginationResult, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/vserver_group.go b/vendor/github.com/denverdino/aliyungo/slb/vserver_group.go new file mode 100644 index 0000000000..1fa83d09b1 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/vserver_group.go @@ -0,0 +1,159 @@ +package slb + +import ( + "github.com/denverdino/aliyungo/common" +) + +type VBackendServerType struct { + ServerId string + Weight int + Port int +} + +type VServerGroup struct { + VServerGroupName string + VServerGroupId string +} + +type VBackendServers struct { + BackendServer []VBackendServerType +} + +type CreateVServerGroupArgs struct { + LoadBalancerId string + RegionId common.Region + VServerGroupName string + VServerGroupId string + BackendServers string +} + +type SetVServerGroupAttributeArgs struct { + LoadBalancerId string + RegionId common.Region + VServerGroupName string + VServerGroupId string + BackendServers string +} + +type AddVServerGroupBackendServersArgs CreateVServerGroupArgs +type RemoveVServerGroupBackendServersArgs CreateVServerGroupArgs +type ModifyVServerGroupBackendServersArgs struct { + VServerGroupId string + RegionId common.Region + OldBackendServers string + NewBackendServers string +} + +type DeleteVServerGroupArgs struct { + VServerGroupId string + RegionId common.Region +} + +type DescribeVServerGroupsArgs struct { + LoadBalancerId string + RegionId common.Region +} + +type DescribeVServerGroupAttributeArgs struct { + VServerGroupId string + RegionId common.Region +} + +type CreateVServerGroupResponse struct { + common.Response + VServerGroupId string + VServerGroupName string + BackendServers VBackendServers +} + +type SetVServerGroupAttributeResponse struct { + common.Response + VServerGroupId string + VServerGroupName string + BackendServers VBackendServers +} + +type AddVServerGroupBackendServersResponse CreateVServerGroupResponse +type RemoveVServerGroupBackendServersResponse CreateVServerGroupResponse +type ModifyVServerGroupBackendServersResponse CreateVServerGroupResponse +type DeleteVServerGroupResponse struct{ common.Response } +type DescribeVServerGroupsResponse struct { + common.Response + VServerGroups struct { + VServerGroup []VServerGroup + } +} +type DescribeVServerGroupAttributeResponse CreateVServerGroupResponse + + +func (client *Client) CreateVServerGroup(args *CreateVServerGroupArgs) (response *CreateVServerGroupResponse, err error) { + response = &CreateVServerGroupResponse{} + err = client.Invoke("CreateVServerGroup", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) SetVServerGroupAttribute(args *SetVServerGroupAttributeArgs) (response *SetVServerGroupAttributeResponse, err error) { + response = &SetVServerGroupAttributeResponse{} + err = client.Invoke("SetVServerGroupAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) AddVServerGroupBackendServers(args *AddVServerGroupBackendServersArgs) (response *AddVServerGroupBackendServersResponse, err error) { + response = &AddVServerGroupBackendServersResponse{} + err = client.Invoke("AddVServerGroupBackendServers", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) RemoveVServerGroupBackendServers(args *RemoveVServerGroupBackendServersArgs) (response *RemoveVServerGroupBackendServersResponse, err error) { + response = &RemoveVServerGroupBackendServersResponse{} + err = client.Invoke("RemoveVServerGroupBackendServers", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) ModifyVServerGroupBackendServers(args *ModifyVServerGroupBackendServersArgs) (response *ModifyVServerGroupBackendServersResponse, err error) { + response = &ModifyVServerGroupBackendServersResponse{} + err = client.Invoke("ModifyVServerGroupBackendServers", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) DeleteVServerGroup(args *DeleteVServerGroupArgs) (response *DeleteVServerGroupResponse, err error) { + response = &DeleteVServerGroupResponse{} + err = client.Invoke("DeleteVServerGroup", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) DescribeVServerGroups(args *DescribeVServerGroupsArgs) (response *DescribeVServerGroupsResponse, err error) { + response = &DescribeVServerGroupsResponse{} + err = client.Invoke("DescribeVServerGroups", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) DescribeVServerGroupAttribute(args *DescribeVServerGroupAttributeArgs) (response *DescribeVServerGroupAttributeResponse, err error) { + response = &DescribeVServerGroupAttributeResponse{} + err = client.Invoke("DescribeVServerGroupAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} diff --git a/vendor/github.com/denverdino/aliyungo/util/attempt.go b/vendor/github.com/denverdino/aliyungo/util/attempt.go new file mode 100644 index 0000000000..2d07f03a83 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/util/attempt.go @@ -0,0 +1,76 @@ +package util + +import ( + "time" +) + +// AttemptStrategy is reused from the goamz package + +// AttemptStrategy represents a strategy for waiting for an action +// to complete successfully. This is an internal type used by the +// implementation of other packages. +type AttemptStrategy struct { + Total time.Duration // total duration of attempt. + Delay time.Duration // interval between each try in the burst. + Min int // minimum number of retries; overrides Total +} + +type Attempt struct { + strategy AttemptStrategy + last time.Time + end time.Time + force bool + count int +} + +// Start begins a new sequence of attempts for the given strategy. +func (s AttemptStrategy) Start() *Attempt { + now := time.Now() + return &Attempt{ + strategy: s, + last: now, + end: now.Add(s.Total), + force: true, + } +} + +// Next waits until it is time to perform the next attempt or returns +// false if it is time to stop trying. +func (a *Attempt) Next() bool { + now := time.Now() + sleep := a.nextSleep(now) + if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count { + return false + } + a.force = false + if sleep > 0 && a.count > 0 { + time.Sleep(sleep) + now = time.Now() + } + a.count++ + a.last = now + return true +} + +func (a *Attempt) nextSleep(now time.Time) time.Duration { + sleep := a.strategy.Delay - now.Sub(a.last) + if sleep < 0 { + return 0 + } + return sleep +} + +// HasNext returns whether another attempt will be made if the current +// one fails. If it returns true, the following call to Next is +// guaranteed to return true. +func (a *Attempt) HasNext() bool { + if a.force || a.strategy.Min > a.count { + return true + } + now := time.Now() + if now.Add(a.nextSleep(now)).Before(a.end) { + a.force = true + return true + } + return false +} diff --git a/vendor/github.com/denverdino/aliyungo/util/encoding.go b/vendor/github.com/denverdino/aliyungo/util/encoding.go new file mode 100644 index 0000000000..e545e069d4 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/util/encoding.go @@ -0,0 +1,152 @@ +package util + +import ( + "encoding/json" + "fmt" + "log" + "net/url" + "reflect" + "strconv" + "time" +) + +//ConvertToQueryValues converts the struct to url.Values +func ConvertToQueryValues(ifc interface{}) url.Values { + values := url.Values{} + SetQueryValues(ifc, &values) + return values +} + +//SetQueryValues sets the struct to existing url.Values following ECS encoding rules +func SetQueryValues(ifc interface{}, values *url.Values) { + setQueryValues(ifc, values, "") +} + +func setQueryValues(i interface{}, values *url.Values, prefix string) { + // add to support url.Values + mapValues, ok := i.(url.Values) + if ok { + for k, _ := range mapValues { + values.Set(k, mapValues.Get(k)) + } + return + } + + elem := reflect.ValueOf(i) + if elem.Kind() == reflect.Ptr { + elem = elem.Elem() + } + elemType := elem.Type() + for i := 0; i < elem.NumField(); i++ { + + fieldName := elemType.Field(i).Name + anonymous := elemType.Field(i).Anonymous + field := elem.Field(i) + // TODO Use Tag for validation + // tag := typ.Field(i).Tag.Get("tagname") + kind := field.Kind() + if (kind == reflect.Ptr || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map || kind == reflect.Chan) && field.IsNil() { + continue + } + if kind == reflect.Ptr { + field = field.Elem() + kind = field.Kind() + } + var value string + //switch field.Interface().(type) { + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + i := field.Int() + if i != 0 { + value = strconv.FormatInt(i, 10) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + i := field.Uint() + if i != 0 { + value = strconv.FormatUint(i, 10) + } + case reflect.Float32: + value = strconv.FormatFloat(field.Float(), 'f', 4, 32) + case reflect.Float64: + value = strconv.FormatFloat(field.Float(), 'f', 4, 64) + case reflect.Bool: + value = strconv.FormatBool(field.Bool()) + case reflect.String: + value = field.String() + case reflect.Map: + ifc := field.Interface() + m := ifc.(map[string]string) + if m != nil { + j := 0 + for k, v := range m { + j++ + keyName := fmt.Sprintf("%s.%d.Key", fieldName, j) + values.Set(keyName, k) + valueName := fmt.Sprintf("%s.%d.Value", fieldName, j) + values.Set(valueName, v) + } + } + case reflect.Slice: + switch field.Type().Elem().Kind() { + case reflect.Uint8: + value = string(field.Bytes()) + case reflect.String: + l := field.Len() + if l > 0 { + strArray := make([]string, l) + for i := 0; i < l; i++ { + strArray[i] = field.Index(i).String() + } + bytes, err := json.Marshal(strArray) + if err == nil { + value = string(bytes) + } else { + log.Printf("Failed to convert JSON: %v", err) + } + } + default: + l := field.Len() + for j := 0; j < l; j++ { + prefixName := fmt.Sprintf("%s.%d.", fieldName, (j + 1)) + ifc := field.Index(j).Interface() + //log.Printf("%s : %v", prefixName, ifc) + if ifc != nil { + setQueryValues(ifc, values, prefixName) + } + } + continue + } + + default: + switch field.Interface().(type) { + case ISO6801Time: + t := field.Interface().(ISO6801Time) + value = t.String() + case time.Time: + t := field.Interface().(time.Time) + value = GetISO8601TimeStamp(t) + default: + ifc := field.Interface() + if ifc != nil { + if anonymous { + SetQueryValues(ifc, values) + } else { + prefixName := fieldName + "." + setQueryValues(ifc, values, prefixName) + } + continue + } + } + } + if value != "" { + name := elemType.Field(i).Tag.Get("ArgName") + if name == "" { + name = fieldName + } + if prefix != "" { + name = prefix + name + } + values.Set(name, value) + } + } +} diff --git a/vendor/github.com/denverdino/aliyungo/util/iso6801.go b/vendor/github.com/denverdino/aliyungo/util/iso6801.go new file mode 100644 index 0000000000..9c25e8f688 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/util/iso6801.go @@ -0,0 +1,80 @@ +package util + +import ( + "fmt" + "strconv" + "time" +) + +// GetISO8601TimeStamp gets timestamp string in ISO8601 format +func GetISO8601TimeStamp(ts time.Time) string { + t := ts.UTC() + return fmt.Sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()) +} + +const formatISO8601 = "2006-01-02T15:04:05Z" +const jsonFormatISO8601 = `"` + formatISO8601 + `"` +const formatISO8601withoutSeconds = "2006-01-02T15:04Z" +const jsonFormatISO8601withoutSeconds = `"` + formatISO8601withoutSeconds + `"` + +// A ISO6801Time represents a time in ISO8601 format +type ISO6801Time time.Time + +// New constructs a new iso8601.Time instance from an existing +// time.Time instance. This causes the nanosecond field to be set to +// 0, and its time zone set to a fixed zone with no offset from UTC +// (but it is *not* UTC itself). +func NewISO6801Time(t time.Time) ISO6801Time { + return ISO6801Time(time.Date( + t.Year(), + t.Month(), + t.Day(), + t.Hour(), + t.Minute(), + t.Second(), + 0, + time.UTC, + )) +} + +// IsDefault checks if the time is default +func (it *ISO6801Time) IsDefault() bool { + return *it == ISO6801Time{} +} + +// MarshalJSON serializes the ISO6801Time into JSON string +func (it ISO6801Time) MarshalJSON() ([]byte, error) { + return []byte(time.Time(it).Format(jsonFormatISO8601)), nil +} + +// UnmarshalJSON deserializes the ISO6801Time from JSON string +func (it *ISO6801Time) UnmarshalJSON(data []byte) error { + str := string(data) + + if str == "\"\"" || len(data) == 0 { + return nil + } + var t time.Time + var err error + if str[0] == '"' { + t, err = time.ParseInLocation(jsonFormatISO8601, str, time.UTC) + if err != nil { + t, err = time.ParseInLocation(jsonFormatISO8601withoutSeconds, str, time.UTC) + } + } else { + var i int64 + i, err = strconv.ParseInt(str, 10, 64) + if err == nil { + t = time.Unix(i/1000, i%1000) + } + } + if err == nil { + *it = ISO6801Time(t) + } + return err +} + +// String returns the time in ISO6801Time format +func (it ISO6801Time) String() string { + return time.Time(it).Format(formatISO8601) +} diff --git a/vendor/github.com/denverdino/aliyungo/util/signature.go b/vendor/github.com/denverdino/aliyungo/util/signature.go new file mode 100644 index 0000000000..a00b27c19b --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/util/signature.go @@ -0,0 +1,40 @@ +package util + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "net/url" + "strings" +) + +//CreateSignature creates signature for string following Aliyun rules +func CreateSignature(stringToSignature, accessKeySecret string) string { + // Crypto by HMAC-SHA1 + hmacSha1 := hmac.New(sha1.New, []byte(accessKeySecret)) + hmacSha1.Write([]byte(stringToSignature)) + sign := hmacSha1.Sum(nil) + + // Encode to Base64 + base64Sign := base64.StdEncoding.EncodeToString(sign) + + return base64Sign +} + +func percentReplace(str string) string { + str = strings.Replace(str, "+", "%20", -1) + str = strings.Replace(str, "*", "%2A", -1) + str = strings.Replace(str, "%7E", "~", -1) + + return str +} + +// CreateSignatureForRequest creates signature for query string values +func CreateSignatureForRequest(method string, values *url.Values, accessKeySecret string) string { + + canonicalizedQueryString := percentReplace(values.Encode()) + + stringToSign := method + "&%2F&" + url.QueryEscape(canonicalizedQueryString) + + return CreateSignature(stringToSign, accessKeySecret) +} diff --git a/vendor/github.com/denverdino/aliyungo/util/util.go b/vendor/github.com/denverdino/aliyungo/util/util.go new file mode 100644 index 0000000000..dd68214e3b --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/util/util.go @@ -0,0 +1,147 @@ +package util + +import ( + "bytes" + srand "crypto/rand" + "encoding/binary" + "math/rand" + "net/http" + "net/url" + "sort" + "time" +) + +const dictionary = "_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + +//CreateRandomString create random string +func CreateRandomString() string { + b := make([]byte, 32) + l := len(dictionary) + + _, err := srand.Read(b) + + if err != nil { + // fail back to insecure rand + rand.Seed(time.Now().UnixNano()) + for i := range b { + b[i] = dictionary[rand.Int()%l] + } + } else { + for i, v := range b { + b[i] = dictionary[v%byte(l)] + } + } + + return string(b) +} + +// Encode encodes the values into ``URL encoded'' form +// ("acl&bar=baz&foo=quux") sorted by key. +func Encode(v url.Values) string { + if v == nil { + return "" + } + var buf bytes.Buffer + keys := make([]string, 0, len(v)) + for k := range v { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + vs := v[k] + prefix := url.QueryEscape(k) + for _, v := range vs { + if buf.Len() > 0 { + buf.WriteByte('&') + } + buf.WriteString(prefix) + if v != "" { + buf.WriteString("=") + buf.WriteString(url.QueryEscape(v)) + } + } + } + return buf.String() +} + +func GetGMTime() string { + return time.Now().UTC().Format(http.TimeFormat) +} + +// + +func randUint32() uint32 { + return randUint32Slice(1)[0] +} + +func randUint32Slice(c int) []uint32 { + b := make([]byte, c*4) + + _, err := srand.Read(b) + + if err != nil { + // fail back to insecure rand + rand.Seed(time.Now().UnixNano()) + for i := range b { + b[i] = byte(rand.Int()) + } + } + + n := make([]uint32, c) + + for i := range n { + n[i] = binary.BigEndian.Uint32(b[i*4 : i*4+4]) + } + + return n +} + +func toByte(n uint32, st, ed byte) byte { + return byte(n%uint32(ed-st+1) + uint32(st)) +} + +func toDigit(n uint32) byte { + return toByte(n, '0', '9') +} + +func toLowerLetter(n uint32) byte { + return toByte(n, 'a', 'z') +} + +func toUpperLetter(n uint32) byte { + return toByte(n, 'A', 'Z') +} + +type convFunc func(uint32) byte + +var convFuncs = []convFunc{toDigit, toLowerLetter, toUpperLetter} + +// tools for generating a random ECS instance password +// from 8 to 30 char MUST contain digit upper, case letter and upper case letter +// http://docs.aliyun.com/#/pub/ecs/open-api/instance&createinstance +func GenerateRandomECSPassword() string { + + // [8, 30] + l := int(randUint32()%23 + 8) + + n := randUint32Slice(l) + + b := make([]byte, l) + + b[0] = toDigit(n[0]) + b[1] = toLowerLetter(n[1]) + b[2] = toUpperLetter(n[2]) + + for i := 3; i < l; i++ { + b[i] = convFuncs[n[i]%3](n[i]) + } + + s := make([]byte, l) + perm := rand.Perm(l) + for i, v := range perm { + s[v] = b[i] + } + + return string(s) + +} diff --git a/vendor/vendor.json b/vendor/vendor.json index dc1fb36f80..09c18afa77 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1121,6 +1121,30 @@ "path": "github.com/davecgh/go-spew/spew", "revision": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d" }, + { + "checksumSHA1": "ADySw3nBHyzEHB6afBSeVRN2A4g=", + "path": "github.com/denverdino/aliyungo/common", + "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", + "revisionTime": "2017-01-17T10:57:15Z" + }, + { + "checksumSHA1": "9ZY3RlumKp5DAMfL08YwMoOOT2o=", + "path": "github.com/denverdino/aliyungo/ecs", + "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", + "revisionTime": "2017-01-17T10:57:15Z" + }, + { + "checksumSHA1": "QlA7zv05k7HWeR3tg4uHqIlFcg8=", + "path": "github.com/denverdino/aliyungo/slb", + "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", + "revisionTime": "2017-01-17T10:57:15Z" + }, + { + "checksumSHA1": "Lp0KtT7ycgq31ox3Uzhpxyw0U+Y=", + "path": "github.com/denverdino/aliyungo/util", + "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", + "revisionTime": "2017-01-17T10:57:15Z" + }, { "checksumSHA1": "yDQQpeUxwqB3C+4opweg6znWJQk=", "comment": "v2.4.0-11-gf219341", diff --git a/website/source/docs/providers/alicloud/d/images.html.markdown b/website/source/docs/providers/alicloud/d/images.html.markdown new file mode 100644 index 0000000000..5c1d743ede --- /dev/null +++ b/website/source/docs/providers/alicloud/d/images.html.markdown @@ -0,0 +1,50 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_images" +sidebar_current: "docs-alicloud-datasource-images" +description: |- + Provides a list of images available to the user. +--- + +# alicloud\_images + +The Images data source list image resource list contains private images of the user and images of system resources provided by Alicloud, as well as other public images and those available on the image market. + +## Example Usage + +``` +data "alicloud_images" "multi_image" { + owners = "system" + name_regex = "^centos_6" +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `name_regex` - (Optional) A regex string to apply to the image list returned by Alicloud. +* `most_recent` - (Optional) If more than one result is returned, use the most recent image. +* `owners` - (Optional) Limit search to specific image owners. Valid items are `system`, `self`, `others`, `marketplace`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - ID of the image. +* `architecture` - Platform type of the image system:i386 | x86_64. +* `creation_time` - Time of creation. +* `description` - Description of the image. +* `image_owner_alias` - Alias of the image owner. +* `os_name` - Display name of the OS. +* `status` - Status of the image, with possible values: `UnAvailable`, `Available`, `Creating` or `CreateFailed`. +* `size` - Size of the image. +* `disk_device_mappings` - Description of the system with disks and snapshots under an image. + * `device` - Device information of the created disk: such as /dev/xvdb. + * `size` - Size of the created disk. + * `snapshot_id` - Snapshot ID. +* `product_code` - Product code of the image on the image market. +* `is_subscribed` - Whether the user has subscribed to the terms of service for the image product corresponding to the ProductCode. +* `image_version` - Version of the image. +* `progress` - Progress of image creation, presented in percentages. diff --git a/website/source/docs/providers/alicloud/d/instance_types.html.markdown b/website/source/docs/providers/alicloud/d/instance_types.html.markdown new file mode 100644 index 0000000000..1505991533 --- /dev/null +++ b/website/source/docs/providers/alicloud/d/instance_types.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_instance_types" +sidebar_current: "docs-alicloud-datasource-instance-types" +description: |- + Provides a list of Ecs Instance Types for use in alicloud_instance resource. +--- + +# alicloud\_instance\_types + +The Instance Types data source list the ecs_instance_types of Alicloud. + +## Example Usage + +``` +# Declare the data source +data "alicloud_instance_types" "1c2g" { + cpu_core_count = 1 + memory_size = 2 +} + +# Create ecs instance with the first matched instance_type + +resource "alicloud_instance" "instance" { + instance_type = "${data.alicloud_instance_types.1c2g.instance_types.0.id}" + + # Other properties... +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `cpu_core_count` - (Optional) Limit search to specific cpu core count. +* `memory_size` - (Optional) Limit search to specific memory size. +* `instance_type_family` - (Optional) Allows to filter list of Instance Types based on their +family name, for example 'ecs.n1'. + +## Attributes Reference + +The following attributes are exported: + +* `id` - ID of the instance type. +* `cpu_core_count` - Number of CPU cores. +* `memory_size` - Size of memory, measured in GB. +* `family` - The instance type family. diff --git a/website/source/docs/providers/alicloud/d/regions.html.markdown b/website/source/docs/providers/alicloud/d/regions.html.markdown new file mode 100644 index 0000000000..2999718fd6 --- /dev/null +++ b/website/source/docs/providers/alicloud/d/regions.html.markdown @@ -0,0 +1,34 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_regions" +sidebar_current: "docs-alicloud-datasource-regions" +description: |- + Provides a list of Availability Regions which can be used by an Alicloud account. +--- + +# alicloud\_regions + +The Regions data source allows access to the list of Alicloud Regions. + +## Example Usage + +``` +data "alicloud_regions" "current" { + current = true +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Optional) The full name of the region to select. +* `current` - (Optional) Set to true to match only the region configured in the provider. + +## Attributes Reference + +The following attributes are exported: + +* `id` - ID of the region. +* `local_name` - Name of the region in the local language. diff --git a/website/source/docs/providers/alicloud/d/zones.html.markdown b/website/source/docs/providers/alicloud/d/zones.html.markdown new file mode 100644 index 0000000000..cdab9fa2aa --- /dev/null +++ b/website/source/docs/providers/alicloud/d/zones.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_zones" +sidebar_current: "docs-alicloud-datasource-zones" +description: |- + Provides a list of Availability Zones which can be used by an Alicloud account. +--- + +# alicloud\_zones + +The Zones data source allows access to the list of Alicloud Zones which can be accessed by an Alicloud account within the region configured in the provider. + +## Example Usage + +``` +# Declare the data source +data "alicloud_zones" "default" { + "available_instance_type"= "ecs.s2.large" + "available_disk_category"= "cloud_ssd" +} + +# Create ecs instance with the first matched zone + +resource "alicloud_instance" "instance" { + availability_zone = "${data.alicloud_zones.default.zones.0.id}" + + # Other properties... +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `available_instance_type` - (Optional) Limit search to specific instance type. +* `available_resource_creation` - (Optional) Limit search to specific resource type. The following values are allowed `Instance`, `Disk` and `VSwitch`. +* `available_disk_category` - (Optional) Limit search to specific disk category. Can be either `cloud`, `ephemeral`, or `ephemeral_ssd`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - ID of the zone. +* `local_name` - Name of the zone in the local language. +* `available_instance_types` - Instance types allowed. +* `available_resource_creation` - Type of resource that can be created. +* `available_disk_categories` - Set of supported disk categories. diff --git a/website/source/docs/providers/alicloud/index.html.markdown b/website/source/docs/providers/alicloud/index.html.markdown new file mode 100644 index 0000000000..ee8c82a3da --- /dev/null +++ b/website/source/docs/providers/alicloud/index.html.markdown @@ -0,0 +1,113 @@ +--- +layout: "alicloud" +page_title: "Provider: alicloud" +sidebar_current: "docs-alicloud-index" +description: |- + The Alicloud provider is used to interact with many resources supported by Alicloud. The provider needs to be configured with the proper credentials before it can be used. +--- + +# Alicloud Provider + +The Alicloud provider is used to interact with the +many resources supported by [Alicloud](https://www.aliyun.com). 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 Alicloud Provider +provider "alicloud" { + access_key = "${var.access_key}" + secret_key = "${var.secret_key}" + region = "${var.region}" +} + +# Create a web server +resource "alicloud_instance" "web" { + # cn-beijing + provider = "alicloud" + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + instance_network_type = "Classic" + internet_charge_type = "PayByBandwidth" + + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + security_groups = ["${alicloud_security_group.default.id}"] + instance_name = "web" +} + +# Create security group +resource "alicloud_security_group" "default" { + name = "default" + provider = "alicloud" + description = "default" +} +``` + +## Authentication + +The Alicloud provider offers a flexible means of providing credentials for authentication. +The following methods are supported, in this order, and explained below: + +- Static credentials +- Environment variables + +### Static credentials ### + +Static credentials can be provided by adding an `access_key` `secret_key` and `region` in-line in the +alicloud provider block: + +Usage: + +``` +provider "alicloud" { + access_key = "${var.access_key}" + secret_key = "${var.secret_key}" + region = "${var.region}" +} +``` + + +###Environment variables + +You can provide your credentials via `ALICLOUD_ACCESS_KEY` and `ALICLOUD_SECRET_KEY`, +environment variables, representing your Alicloud Access Key and Secret Key, respectively. +`ALICLOUD_REGION` is also used, if applicable: + +``` +provider "alicloud" {} +``` + +Usage: + +``` +$ export ALICLOUD_ACCESS_KEY="anaccesskey" +$ export ALICLOUD_SECRET_KEY="asecretkey" +$ export ALICLOUD_REGION="cn-beijing" +$ terraform plan +``` + + +## Argument Reference + +The following arguments are supported: + +* `access_key` - (Optional) This is the Alicloud access key. It must be provided, but + it can also be sourced from the `ALICLOUD_ACCESS_KEY` environment variable. + +* `secret_key` - (Optional) This is the Alicloud secret key. It must be provided, but + it can also be sourced from the `ALICLOUD_SECRET_KEY` environment variable. + +* `region` - (Required) This is the Alicloud region. It must be provided, but + it can also be sourced from the `ALICLOUD_REGION` environment variables. + + +## Testing + +Credentials must be provided via the `ALICLOUD_ACCESS_KEY`, and `ALICLOUD_SECRET_KEY` environment variables in order to run acceptance tests. + diff --git a/website/source/docs/providers/alicloud/r/disk.html.markdown b/website/source/docs/providers/alicloud/r/disk.html.markdown new file mode 100644 index 0000000000..d295edbff2 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/disk.html.markdown @@ -0,0 +1,56 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_disk" +sidebar_current: "docs-alicloud-resource-disk" +description: |- + Provides a ECS Disk resource. +--- + +# alicloud\_disk + +Provides a ECS disk resource. + +~> **NOTE:** One of `size` or `snapshot_id` is required when specifying an ECS disk. If all of them be specified, `size` must more than the size of snapshot which `snapshot_id` represents. Currently, `alicloud_disk` doesn't resize disk. + +## Example Usage + +``` +# Create a new ECS disk. +resource "alicloud_disk" "ecs_disk" { + # cn-beijing + availability_zone = "cn-beijing-b" + name = "New-disk" + description = "Hello ecs disk." + category = "cloud_efficiency" + size = "30" + + tags { + Name = "TerraformTest" + } +} +``` +## Argument Reference + +The following arguments are supported: + +* `availability_zone` - (Required, Forces new resource) The Zone to create the disk in. +* `name` - (Optional) Name of the ECS disk. This name can have a string of 2 to 128 characters, must contain only alphanumeric characters or hyphens, such as "-",".","_", and must not begin or end with a hyphen, and must not begin with http:// or https://. Default value is null. +* `description` - (Optional) Description of the disk. This description can have a string of 2 to 256 characters, It cannot begin with http:// or https://. Default value is null. +* `category` - (Optional, Forces new resource) Category of the disk. Valid values are `cloud`, `cloud_efficiency` and `cloud_ssd`. Default is `cloud`. +* `size` - (Required) The size of the disk in GiBs, and its value depends on `Category`. `cloud` disk value range: 5GB ~ 2000GB and other category disk value range: 20 ~ 32768. +* `snapshot_id` - (Optional) A snapshot to base the disk off of. If it is specified, `size` will be invalid and the disk size is equals to the snapshot size. +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The disk ID. +* `availability_zone` - The Zone to create the disk in. +* `name` - The disk name. +* `description` - The disk description. +* `status` - The disk status. +* `category` - The disk category. +* `size` - The disk size. +* `snapshot_id` - The disk snapshot ID. +* `tags` - The disk tags. \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/disk_attachment.html.markdown b/website/source/docs/providers/alicloud/r/disk_attachment.html.markdown new file mode 100644 index 0000000000..133f6aeb0c --- /dev/null +++ b/website/source/docs/providers/alicloud/r/disk_attachment.html.markdown @@ -0,0 +1,68 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_disk_attachment" +sidebar_current: "docs-alicloud-resource-disk-attachment" +description: |- + Provides a ECS Disk Attachment resource. +--- + +# alicloud\_disk\_attachment + +Provides an Alicloud ECS Disk Attachment as a resource, to attach and detach disks from ECS Instances. + +## Example Usage + +Basic usage + +``` +# Create a new ECS disk-attachment and use it attach one disk to a new instance. + +resource "alicloud_security_group" "ecs_sg" { + name = "terraform-test-group" + description = "New security group" +} + +resource "alicloud_disk" "ecs_disk" { + availability_zone = "cn-beijing-a" + size = "50" + + tags { + Name = "TerraformTest-disk" + } +} + +resource "alicloud_instance" "ecs_instance" { + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_type = "ecs.s1.small" + availability_zone = "cn-beijing-a" + security_groups = ["${alicloud_security_group.ecs_sg.id}"] + instance_name = "Hello" + instance_network_type = "classic" + internet_charge_type = "PayByBandwidth" + + tags { + Name = "TerraformTest-instance" + } +} + +resource "alicloud_disk_attachment" "ecs_disk_att" { + disk_id = "${alicloud_disk.ecs_disk.id}" + instance_id = "${alicloud_instance.ecs_instance.id}" + device_name = "/dev/xvdb" +} +``` +## Argument Reference + +The following arguments are supported: + +* `instance_id` - (Required, Forces new resource) ID of the Instance to attach to. +* `disk_id` - (Required, Forces new resource) ID of the Disk to be attached. +* `device_name` - (Required, Forces new resource) The device name to expose to the instance (for example, /dev/xvdb). + +## Attributes Reference + +The following attributes are exported: + +* `instance_id` - ID of the Instance. +* `disk_id` - ID of the Disk. +* `device_name` - The device name exposed to the instance. \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/eip.html.markdown b/website/source/docs/providers/alicloud/r/eip.html.markdown new file mode 100644 index 0000000000..f0ee9bcb92 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/eip.html.markdown @@ -0,0 +1,38 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_eip" +sidebar_current: "docs-alicloud-resource-eip" +description: |- + Provides a ECS EIP resource. +--- + +# alicloud\_eip + +Provides a ECS EIP resource. + +## Example Usage + +``` +# Create a new EIP. +resource "alicloud_eip" "example" { + bandwidth = "10" + internet_charge_type = "PayByBandwidth" +} +``` +## Argument Reference + +The following arguments are supported: + +* `bandwidth` - (Optional) Maximum bandwidth to the elastic public network, measured in Mbps (Mega bit per second). If this value is not specified, then automatically sets it to 5 Mbps. +* `internet_charge_type` - (Optional, Forces new resource) Internet charge type of the EIP, Valid values are `PayByBandwidth`, `PayByTraffic`. Default is `PayByBandwidth`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The EIP ID. +* `bandwidth` - The elastic public network bandwidth. +* `internet_charge_type` - The EIP internet charge type. +* `status` - The EIP current status. +* `ip_address` - The elastic ip address +* `instance` - The ID of the instance which is associated with the EIP. diff --git a/website/source/docs/providers/alicloud/r/eip_association.html.markdown b/website/source/docs/providers/alicloud/r/eip_association.html.markdown new file mode 100644 index 0000000000..afaab31ac9 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/eip_association.html.markdown @@ -0,0 +1,75 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_eip_association" +sidebar_current: "docs-alicloud-resource-eip-association" +description: |- + Provides a ECS EIP Association resource. +--- + +# alicloud\_eip\_association + +Provides an Alicloud EIP Association resource, to associate and disassociate Elastic IPs from ECS Instances. + +~> **NOTE:** `alicloud_eip_association` is useful in scenarios where EIPs are either + pre-existing or distributed to customers or users and therefore cannot be changed. + In addition, it only supports ECS-VPC. + +## Example Usage + +``` +# Create a new EIP association and use it to associate a EIP form a instance. + +resource "alicloud_vpc" "vpc" { + cidr_block = "10.1.0.0/21" +} + +resource "alicloud_vswitch" "vsw" { + vpc_id = "${alicloud_vpc.vpc.id}" + cidr_block = "10.1.1.0/24" + availability_zone = "cn-beijing-a" + depends_on = [ + "alicloud_vpc.vpc"] +} + +resource "alicloud_instance" "ecs_instance" { + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_type = "ecs.s1.small" + availability_zone = "cn-beijing-a" + security_groups = ["${alicloud_security_group.group.id}"] + vswitch_id = "${alicloud_vswitch.vsw.id}" + instance_name = "hello" + instance_network_type = "vpc" + + tags { + Name = "TerraformTest-instance" + } +} + +resource "alicloud_eip" "eip" { +} + +resource "alicloud_eip_association" "eip_asso" { + allocation_id = "${alicloud_eip.eip.id}" + instance_id = "${alicloud_instance.ecs_instance.id}" +} + +resource "alicloud_security_group" "group" { + name = "terraform-test-group" + description = "New security group" + vpc_id = "${alicloud_vpc.vpc.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `allocation_id` - (Optional, Forces new resource) The allocation EIP ID. +* `instance_id` - (Optional, Forces new resource) The ID of the instance. + +## Attributes Reference + +The following attributes are exported: + +* `allocation_id` - As above. +* `instance_id` - As above. \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/instance.html.markdown b/website/source/docs/providers/alicloud/r/instance.html.markdown new file mode 100644 index 0000000000..c4a36a16f1 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/instance.html.markdown @@ -0,0 +1,100 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_instance" +sidebar_current: "docs-alicloud-resource-instance" +description: |- + Provides a ECS instance resource. +--- + +# alicloud\_ecs + +Provides a ECS instance resource. + +## Example Usage + +``` +# Create a new ECS instance for classic +resource "alicloud_security_group" "classic" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "classic" { + # cn-beijing + availability_zone = "cn-beijing-b" + security_group_id = "${alicloud_security_group.classic.id}" + + allocate_public_ip = "true" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_name = "test_foo" +} + +# Create a new ECS instance for VPC +resource "alicloud_vpc" "default" { + # Other parameters... +} + +resource "alicloud_vswitch" "default" { + # Other parameters... +} + +resource "alicloud_slb" "vpc" { + name = "test-slb-tf" + vpc_id = "${alicloud_vpc.default.id}" + vswitch_id = "${alicloud_vswitch.default.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `availability_zone` - (Required) The Zone to start the instance in. +* `image_id` - (Required) The Image to use for the instance. +* `instance_type` - (Required) The type of instance to start. +* `security_group_ids` - (Required) A list of security group ids to associate with. If you are creating Instances in a VPC, use `vpc_security_group_ids` instead. +`security_group_ids` instead. +* `instance_name` - (Optional) The name of the ECS. This instance_name can have a string of 2 to 128 characters, must contain only alphanumeric characters or hyphens, such as "-",".","_", and must not begin or end with a hyphen, and must not begin with http:// or https://. If not specified, +Terraform will autogenerate a name beginning with `tf-ecs`. +* `allocate_public_ip` - (Optional) Associate a public ip address with an instance in a VPC or Classic. Boolean value, Default is false. +* `io_optimized` - (Optional) Valid + values are `none`, `optimized`, If `optimized`, the launched ECS instance will be I/O optimized. Default is `optimized`. +* `system_disk_category` - (Optional) Valid values are `cloud`, `cloud_efficiency`, `cloud_ssd`, For I/O optimized instance type, `cloud_ssd` and `cloud_efficiency` disks are supported. For non I/O Optimized instance type, `cloud` disk are supported. +* `system_disk_size` - (Optional) Size of the system disk, value range: 40GB ~ 500GB. Default is 40GB. +* `description` - (Optional) Description of the instance, This description can have a string of 2 to 256 characters, It cannot begin with http:// or https://. Default value is null. +* `internet_charge_type` - (Optional) Internet charge type of the instance, Valid values are `PayByBandwidth`, `PayByTraffic`. Default is `PayByBandwidth`. +* `internet_max_bandwidth_in` - (Optional) Maximum incoming bandwidth from the public network, measured in Mbps (Mega bit per second). Value range: [1, 200]. If this value is not specified, then automatically sets it to 200 Mbps. +* `internet_max_bandwidth_out` - (Optional) Maximum outgoing bandwidth to the public network, measured in Mbps (Mega bit per second). Value range: +`internet_charge_type` is `PayByBandwidth`: this value range [0, 100], If this value is not specified, then automatically sets it to 0 Mbps; If `internet_charge_type` is `PayByTraffic`: this value range [1, 100]. this value must be set value, such as 5. +* `host_name` - (Optional) Host name of the ECS, which is a string of at least two characters. “hostname” cannot start or end with “.” or “-“. In addition, two or more consecutive “.” or “-“ symbols are not allowed. On Windows, the host name can contain a maximum of 15 characters, which can be a combination of uppercase/lowercase letters, numerals, and “-“. The host name cannot contain dots (“.”) or contain only numeric characters. +On other OSs such as Linux, the host name can contain a maximum of 30 characters, which can be segments separated by dots (“.”), where each segment can contain uppercase/lowercase letters, numerals, or “_“. +* `password` - (Optional) Password to an instance is a string of 8 to 30 characters. It must contain uppercase/lowercase letters and numerals, but cannot contain special symbols. +* `vswitch_id` - (Optional) The virtual switch ID to launch in VPC. If you want to create instances in VPC network, this parameter must be set. +* `instance_charge_type` - (Optional) Valid values are `PrePaid`, `PostPaid`, The default is `PostPaid`. +* `period` - (Optional) The time that you have bought the resource, in month. Only valid when instance_charge_type is set as `PrePaid`. Value range [1, 12]. +* `private_ip` - (Optional) Private IP address to associate with the instance in a VPC. +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The instance ID. +* `availability_zone` - The Zone to start the instance in. +* `instance_name` - The instance name. +* `host_name` - The instance host name. +* `description` - The instance description. +* `status` - The instance status. +* `image_id` - The instance Image Id. +* `instance_type` - The instance type. +* `instance_network_type` - The instance network type and it has two values: `vpc` and `classic`. +* `io_optimized` - The instance whether I/O optimized. +* `private_ip` - The instance private ip. +* `public_ip` - The instance public ip. +* `vswitch_id` - If the instance created in VPC, then this value is virtual switch ID. +* `tags` - The instance tags, use jsonencode(item) to display the value. \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/nat_gateway.html.markdown b/website/source/docs/providers/alicloud/r/nat_gateway.html.markdown new file mode 100644 index 0000000000..bc3597864c --- /dev/null +++ b/website/source/docs/providers/alicloud/r/nat_gateway.html.markdown @@ -0,0 +1,74 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_nat_gateway" +sidebar_current: "docs-alicloud-resource-nat-gateway" +description: |- + Provides a resource to create a VPC NAT Gateway. +--- + +# alicloud\_nat\_gateway + +Provides a resource to create a VPC NAT Gateway. + +## Example Usage + +Basic usage + +``` +resource "alicloud_vpc" "vpc" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "vsw" { + vpc_id = "${alicloud_vpc.vpc.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_nat_gateway" "nat_gateway" { + vpc_id = "${alicloud_vpc.vpc.id}" + spec = "Small" + name = "test_foo" + bandwidth_packages = [{ + ip_count = 1 + bandwidth = 5 + zone = "cn-beijing-b" + }, { + ip_count = 2 + bandwidth = 10 + zone = "cn-beijing-b" + }] + depends_on = [ + "alicloud_vswitch.vsw"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vpc_id` - (Required, Forces New Resorce) The VPC ID. +* `spec` - (Required, Forces New Resorce) The specification of the nat gateway. Valid values are `Small`, `Middle` and `Large`. Details refer to [Nat Gateway Specification](https://help.aliyun.com/document_detail/42757.html?spm=5176.doc32322.6.559.kFNBzv) +* `name` - (Optional) Name of the nat gateway. The value can have a string of 2 to 128 characters, must contain only alphanumeric characters or hyphens, such as "-",".","_", and must not begin or end with a hyphen, and must not begin with http:// or https://. Defaults to null. +* `description` - (Optional) Description of the nat gateway, This description can have a string of 2 to 256 characters, It cannot begin with http:// or https://. Defaults to null. +* `bandwidth_packages` - (Required) A list of bandwidth packages for the nat gatway. + +## Block bandwidth package + +The bandwidth package mapping supports the following: + +* `ip_count` - (Required) The IP number of the current bandwidth package. Its value range from 1 to 50. +* `bandwidth` - (Required) The bandwidth value of the current bandwidth package. Its value range from 5 to 5000. +* `zone` - (Optional) The AZ for the current bandwidth. If this value is not specified, Terraform will set a random AZ. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the nat gateway. +* `name` - The name of the nat gateway. +* `description` - The description of the nat gateway. +* `spec` - The specification of the nat gateway. +* `vpc_id` - The VPC ID for the nat gateway. +* `bandwidth_package_ids` - A list ID of the bandwidth packages, and split them with commas diff --git a/website/source/docs/providers/alicloud/r/security_group.html.markdown b/website/source/docs/providers/alicloud/r/security_group.html.markdown new file mode 100644 index 0000000000..6a65f0e8c8 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/security_group.html.markdown @@ -0,0 +1,53 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_security_group" +sidebar_current: "docs-alicloud-resource-security-group" +description: |- + Provides a Alicloud Security Group resource. +--- + +# alicloud\_security\_group + +Provides a security group resource. + +~> **NOTE:** `alicloud_security_group` is used to build and manage a security group, and `alicloud_security_group_rule` can define ingress or egress rules for it. + +## Example Usage + +Basic Usage + +``` +resource "alicloud_security_group" "group" { + name = "terraform-test-group" + description = "New security group" +} +``` +Basic usage for vpc + +``` +resource "alicloud_security_group" "group" { + name = "new-group" + vpc_id = "${alicloud_vpc.vpc.id}" +} + +resource "alicloud_vpc" "vpc" { + cidr_block = "10.1.0.0/21" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Optional) The name of the security group. Defaults to null. +* `description` - (Optional, Forces new resource) The security group description. Defaults to null. +* `vpc_id` - (Optional, Forces new resource) The VPC ID. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the security group +* `vpc_id` - The VPC ID. +* `name` - The name of the security group +* `description` - The description of the security group \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/security_group_rule.html.markdown b/website/source/docs/providers/alicloud/r/security_group_rule.html.markdown new file mode 100644 index 0000000000..49bc269ee1 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/security_group_rule.html.markdown @@ -0,0 +1,61 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_security_group_rule" +sidebar_current: "docs-alicloud-resource-security-group-rule" +description: |- + Provides a Alicloud Security Group Rule resource. +--- + +# alicloud\_security\_group\_rule + +Provides a security group rule resource. +Represents a single `ingress` or `egress` group rule, which can be added to external Security Groups. + +~> **NOTE:** `nic_type` should set to `intranet` when security group type is `vpc`. In this situation it does not distinguish between intranet and internet, the rule is effective on them both. + + +## Example Usage + +Basic Usage + +``` +resource "alicloud_security_group" "default" { + name = "default" +} + +resource "alicloud_security_group_rule" "allow_all_tcp" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "1/65535" + priority = 1 + security_group_id = "${alicloud_security_group.default.id}"ecs.InstanceAttributesType + cidr_ip = "0.0.0.0/0" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `type` - (Required) The type of rule being created. Valid options are `ingress` (inbound) or `egress` (outbound). +* `ip_protocol` - (Required) The protocol. Can be `tcp`, `udp`, `icmp`, `gre` or `all`. +* `port_range` - (Required) The range of port numbers relevant to the IP protocol. When the protocol is tcp or udp, the default port number range is 1-65535. For example, `1/200` means that the range of the port numbers is 1-200. +* `security_group_id` - (Required) The security group to apply this rule to. +* `nic_type` - (Optional, Forces new resource) Network type, can be either `internet` or `intranet`, the default value is `internet`. +* `policy` - (Optional, Forces new resource) Authorization policy, can be either `accept` or `drop`, the default value is `accept`. +* `priority` - (Optional, Forces new resource) Authorization policy priority, with parameter values: `1-100`, default value: 1. +* `cidr_ip` - (Optional, Forces new resource) The target IP address range. The default value is 0.0.0.0/0 (which means no restriction will be applied). Other supported formats include 10.159.6.18/12. Only IPv4 is supported. +* `source_security_group_id` - (Optional, Forces new resource) The target security group ID within the same region. Either the `source_security_group_id` or `cidr_ip` must be set. If both are set, then `cidr_ip` is authorized by default. If this field is specified, but no `cidr_ip` is specified, the `nic_type` can only select `intranet`. +* `source_group_owner_account` - (Optional, Forces new resource) The Alibaba Cloud user account Id of the target security group when security groups are authorized across accounts. This parameter is invalid if `cidr_ip` has already been set. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the security group rule +* `type` - The type of rule, `ingress` or `egress` +* `name` - The name of the security group +* `port_range` - The range of port numbers +* `ip_protocol` - The protocol of the security group rule \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/slb.html.markdown b/website/source/docs/providers/alicloud/r/slb.html.markdown new file mode 100644 index 0000000000..d021985c39 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/slb.html.markdown @@ -0,0 +1,90 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_slb" +sidebar_current: "docs-alicloud-resource-slb" +description: |- + Provides an Application Load Banlancer resource. +--- + +# alicloud\_slb + +Provides an Application Load Balancer resource. + +## Example Usage + +``` +# Create a new load balancer for classic +resource "alicloud_slb" "classic" { + name = "test-slb-tf" + internet = true + internet_charge_type = "paybybandwidth" + bandwidth = 5 + listener = [ + { + "instance_port" = "2111" + "lb_port" = "21" + "lb_protocol" = "tcp" + "bandwidth" = "5" + },{ + "instance_port" = "8000" + "lb_port" = "80" + "lb_protocol" = "http" + "bandwidth" = "5" + },{ + "instance_port" = "1611" + "lb_port" = "161" + "lb_protocol" = "udp" + "bandwidth" = "5" + }] +} + +# Create a new load balancer for VPC +resource "alicloud_vpc" "default" { + # Other parameters... +} + +resource "alicloud_vswitch" "default" { + # Other parameters... +} + +resource "alicloud_slb" "vpc" { + name = "test-slb-tf" + vswitch_id = "${alicloud_vswitch.default.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Optional) The name of the SLB. This name must be unique within your AliCloud account, can have a maximum of 80 characters, +must contain only alphanumeric characters or hyphens, such as "-","/",".","_", and must not begin or end with a hyphen. If not specified, +Terraform will autogenerate a name beginning with `tf-lb`. +* `internet` - (Optional, Forces New Resource) If true, the SLB addressType will be internet, false will be intranet, Default is false. If load balancer launched in VPC, this value must be "false". +* `internet_charge_type` - (Optional, Forces New Resource) Valid + values are `paybybandwidth`, `paybytraffic`. If this value is "paybybandwidth", then argument "internet" must be "true". Default is "paybytraffic". If load balancer launched in VPC, this value must be "paybytraffic". +* `bandwidth` - (Optional) Valid + value is between 1 and 1000, If argument "internet_charge_type" is "paybytraffic", then this value will be ignore. +* `listener` - (Optional) Additional SLB listener. See [Block listener](#block-listener) below for details. +* `vswitch_id` - (Required for a VPC SLB, Forces New Resource) The VSwitch ID to launch in. + +## Block listener + +The listener mapping supports the following: + +* `instance_port` - (Required) The port on which the backend servers are listening. Valid value is between 1 to 65535. +* `lb_port` - (Required) The port on which the load balancer is listening. Valid value is between 1 to 65535. +* `lb_protocol` - (Required) The protocol to listen on. Valid values are `http` and and `tcp` and `udp`. +* `bandwidth` - (Required) The bandwidth on which the load balancer is listening. Valid values is -1 or between 1 and 1000. If -1, the bindwidth will haven’t upper limit. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the load balancer. +* `name` - The name of the load balancer. +* `internet` - The internet of the load balancer. +* `internet_charge_type` - The internet_charge_type of the load balancer. +* `bandwidth` - The bandwidth of the load balancer. +* `vswitch_id` - The VSwitch ID of the load balancer. Only available on SLB launched in a VPC. +* `address` - The IP address of the load balancer. \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/slb_attachment.html.markdown b/website/source/docs/providers/alicloud/r/slb_attachment.html.markdown new file mode 100644 index 0000000000..bc35e2e8eb --- /dev/null +++ b/website/source/docs/providers/alicloud/r/slb_attachment.html.markdown @@ -0,0 +1,43 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_slb_attachment" +sidebar_current: "docs-alicloud-resource-slb-attachment" +description: |- + Provides an Application Load Banlancer Attachment resource. +--- + +# alicloud\_slb\_attachment + +Provides an Application Load Balancer Attachment resource. + +## Example Usage + +``` +# Create a new load balancer attachment for classic +resource "alicloud_slb" "default" { + # Other parameters... +} + +resource "alicloud_instance" "default" { + # Other parameters... +} + +resource "alicloud_slb_attachment" "default" { + slb_id = "${alicloud_slb.default.id}" + instances = ["${alicloud_instance.default.id}"] +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `slb_id` - (Required) The ID of the SLB.. +* `instances` - (Required) A list of instance ids to added backend server in the SLB. If dettachment instances then this value set []. + +## Attributes Reference + +The following attributes are exported: + +* `backend_servers` - The backend servers of the load balancer. diff --git a/website/source/docs/providers/alicloud/r/vpc.html.markdown b/website/source/docs/providers/alicloud/r/vpc.html.markdown new file mode 100644 index 0000000000..af34cf110b --- /dev/null +++ b/website/source/docs/providers/alicloud/r/vpc.html.markdown @@ -0,0 +1,41 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_vpc" +sidebar_current: "docs-alicloud-resource-vpc" +description: |- + Provides a Alicloud VPC resource. +--- + +# alicloud\_vpc + +Provides a VPC resource. + +~> **NOTE:** Terraform will auto build a router and a route table while it uses `alicloud_vpc` to build a vpc resource. + +## Example Usage + +Basic Usage + +``` +resource "alicloud_vpc" "vpc" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} +``` +## Argument Reference + +The following arguments are supported: + +* `cidr_block` - (Required, Forces new resource) The CIDR block for the VPC. +* `name` - (Optional) The name of the VPC. Defaults to null. +* `description` - (Optional) The VPC description. Defaults to null. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the VPC. +* `cidr_block` - The CIDR block for the VPC. +* `name` - The name of the VPC. +* `description` - The description of the VPC. +* `router_id` - The ID of the router created by default on VPC creation. diff --git a/website/source/docs/providers/alicloud/r/vroute_entry.html.markdown b/website/source/docs/providers/alicloud/r/vroute_entry.html.markdown new file mode 100644 index 0000000000..0b9d695378 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/vroute_entry.html.markdown @@ -0,0 +1,53 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_route_entry" +sidebar_current: "docs-alicloud-resource-route-entry" +description: |- + Provides a Alicloud Route Entry resource. +--- + +# alicloud\_route\_entry + +Provides a route entry resource. + +## Example Usage + +Basic Usage + +``` +resource "alicloud_vpc" "vpc" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_route_entry" "default" { + router_id = "${alicloud_vpc.default.router_id}" + route_table_id = "${alicloud_vpc.default.router_table_id}" + destination_cidrblock = "${var.entry_cidr}" + nexthop_type = "Instance" + nexthop_id = "${alicloud_instance.snat.id}" +} + +resource "alicloud_instance" "snat" { + // ... +} +``` +## Argument Reference + +The following arguments are supported: + +* `router_id` - (Required, Forces new resource) The ID of the virtual router attached to Vpc. +* `route_table_id` - (Required, Forces new resource) The ID of the route table. +* `destination_cidrblock` - (Required, Forces new resource) The RouteEntry's target network segment. +* `nexthop_type` - (Required, Forces new resource) The next hop type. Available value is Instance. +* `nexthop_id` - (Required, Forces new resource) The route entry's next hop. + +## Attributes Reference + +The following attributes are exported: + +* `router_id` - (Required, Forces new resource) The ID of the virtual router attached to Vpc. +* `route_table_id` - (Required, Forces new resource) The ID of the route table. +* `destination_cidrblock` - (Required, Forces new resource) The RouteEntry's target network segment. +* `nexthop_type` - (Required, Forces new resource) The next hop type. Available value is Instance. +* `nexthop_id` - (Required, Forces new resource) The route entry's next hop. diff --git a/website/source/docs/providers/alicloud/r/vswitch.html.markdown b/website/source/docs/providers/alicloud/r/vswitch.html.markdown new file mode 100644 index 0000000000..cce548c69e --- /dev/null +++ b/website/source/docs/providers/alicloud/r/vswitch.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_vswitch" +sidebar_current: "docs-alicloud-resource-vswitch" +description: |- + Provides a Alicloud VPC switch resource. +--- + +# alicloud\_vswitch + +Provides a VPC switch resource. + +## Example Usage + +Basic Usage + +``` +resource "alicloud_vpc" "vpc" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "vsw" { + vpc_id = "${alicloud_vpc.vpc.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} +``` +## Argument Reference + +The following arguments are supported: + +* `availability_zone` - (Required, Forces new resource) The AZ for the switch. +* `vpc_id` - (Required, Forces new resource) The VPC ID. +* `cidr_block` - (Required, Forces new resource) The CIDR block for the switch. +* `name` - (Optional) The name of the switch. Defaults to null. +* `description` - (Optional) The switch description. Defaults to null. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the switch. +* `availability_zone` The AZ for the switch. +* `cidr_block` - The CIDR block for the switch. +* `vpc_id` - The VPC ID. +* `name` - The name of the switch. +* `description` - The description of the switch.