update alicloud provider (#11235)
This commit is contained in:
parent
bae907566d
commit
fb8ef9f0f0
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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()))
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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}.*"
|
||||||
|
}
|
||||||
|
`
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
`
|
|
@ -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
|
||||||
|
}
|
|
@ -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" {
|
||||||
|
}
|
||||||
|
`
|
|
@ -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
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
`
|
|
@ -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"
|
||||||
|
)
|
|
@ -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")
|
||||||
|
)
|
|
@ -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")
|
||||||
|
)
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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",
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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."))
|
||||||
|
})
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
`
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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}"
|
||||||
|
}
|
||||||
|
`
|
|
@ -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"
|
||||||
|
}
|
||||||
|
`
|
|
@ -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)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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."))
|
||||||
|
})
|
||||||
|
}
|
|
@ -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"]
|
||||||
|
}
|
||||||
|
`
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
|
@ -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"
|
||||||
|
}
|
||||||
|
`
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
|
@ -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}"
|
||||||
|
}
|
||||||
|
`
|
|
@ -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
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
`
|
|
@ -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
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
|
@ -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
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
`
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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, ",")
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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}"
|
||||||
|
}
|
||||||
|
|
|
@ -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)}"
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
|
@ -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
|
|
@ -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}"]
|
||||||
|
}
|
|
@ -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}"
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
|
@ -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}"]
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
output "hostname_list" {
|
||||||
|
value = "${join(",", alicloud_instance.instance.*.instance_name)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ecs_ids" {
|
||||||
|
value = "${join(",", alicloud_instance.instance.*.id)}"
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
|
@ -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
|
|
@ -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")}"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
output "hostname" {
|
||||||
|
value = "${alicloud_instance.website.instance_name}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ecs_id" {
|
||||||
|
value = "${alicloud_instance.website.id}"
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash -v
|
||||||
|
apt-get update -y
|
||||||
|
apt-get install -y nginx > /tmp/nginx.log
|
||||||
|
|
||||||
|
cd /
|
||||||
|
mkdir -p alicloud/go
|
|
@ -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"
|
||||||
|
}
|
|
@ -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
|
|
@ -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}"
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
|
@ -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
|
|
@ -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}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
output "hostname_list" {
|
||||||
|
value = "${join(",", alicloud_instance.instance.*.instance_name)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ecs_ids" {
|
||||||
|
value = "${join(",", alicloud_instance.instance.*.id)}"
|
||||||
|
}
|
|
@ -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 = ""
|
||||||
|
}
|
|
@ -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
|
|
@ -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}"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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)}"
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
|
@ -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
|
|
@ -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}"
|
||||||
|
}
|
||||||
|
|
|
@ -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)}"
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
|
@ -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}"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
variable "security_group_name" {
|
||||||
|
default = "default-sg"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "nic_type" {
|
||||||
|
default = "internet"
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
||||||
|
resource "alicloud_security_group" "group" {
|
||||||
|
name = "${var.short_name}"
|
||||||
|
description = "New security group"
|
||||||
|
vpc_id = "${var.vpc_id}"
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
output "security_group" {
|
||||||
|
value = "${alicloud_security_group.group.id}"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
variable "short_name" {
|
||||||
|
}
|
||||||
|
variable "vpc_id" {
|
||||||
|
}
|
|
@ -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
|
|
@ -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"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
output "slb_id" {
|
||||||
|
value = "${alicloud_slb.instance.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "slbname" {
|
||||||
|
value = "${alicloud_slb.instance.name}"
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
Binary file not shown.
|
@ -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"
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
output "slb_id" {
|
||||||
|
value = "${alicloud_slb.instance.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "slbname" {
|
||||||
|
value = "${alicloud_slb.instance.name}"
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
variable "slb_name" {
|
||||||
|
default = "slb_worder"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "internet_charge_type" {
|
||||||
|
default = "paybytraffic"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "internet" {
|
||||||
|
default = true
|
||||||
|
}
|
|
@ -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
|
|
@ -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}"
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue