update alicloud provider (#11235)

This commit is contained in:
wangyue 2017-01-19 22:08:56 +08:00 committed by Paul Stack
parent bae907566d
commit fb8ef9f0f0
167 changed files with 17175 additions and 0 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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()))
}

View File

@ -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)
}

View File

@ -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}.*"
}
`

View File

@ -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
}

View File

@ -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
}
`

View File

@ -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
}

View File

@ -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" {
}
`

View File

@ -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
}

View File

@ -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"
}
`

View File

@ -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"
)

View File

@ -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")
)

View File

@ -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")
)

View File

@ -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
}

View File

@ -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
}

View File

@ -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",
}
}

View File

@ -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
}
}

View File

@ -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."))
})
}

View File

@ -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
})
}

View File

@ -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"
}
`

View File

@ -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"
}
}
`

View File

@ -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
}

View File

@ -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
}

View File

@ -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}"
}
`

View File

@ -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"
}
`

View File

@ -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

View File

@ -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."))
})
}

View File

@ -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"]
}
`

View File

@ -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
}

View File

@ -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
}

View File

@ -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"
}
`

View File

@ -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"
}
`

View File

@ -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
}

View File

@ -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
}

View File

@ -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}"]
}
`

View File

@ -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}"
}
`

View File

@ -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
}

View File

@ -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"
}
`

View File

@ -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
}

View File

@ -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"
}
`

View File

@ -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
}

View File

@ -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"
}
`

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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, ",")
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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}"
}

View File

@ -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)}"
}

View File

@ -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"
}

View File

@ -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

View File

@ -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}"]
}

View File

@ -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}"
}

View File

@ -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
}

View File

@ -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

View File

@ -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}"]
}

View File

@ -0,0 +1,7 @@
output "hostname_list" {
value = "${join(",", alicloud_instance.instance.*.instance_name)}"
}
output "ecs_ids" {
value = "${join(",", alicloud_instance.instance.*.id)}"
}

View File

@ -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"
}

View File

@ -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

View File

@ -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")}"
}

View File

@ -0,0 +1,7 @@
output "hostname" {
value = "${alicloud_instance.website.instance_name}"
}
output "ecs_id" {
value = "${alicloud_instance.website.id}"
}

View File

@ -0,0 +1,6 @@
#!/bin/bash -v
apt-get update -y
apt-get install -y nginx > /tmp/nginx.log
cd /
mkdir -p alicloud/go

View File

@ -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"
}

View File

@ -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

View File

@ -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}"
}

View File

@ -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"
}

View File

@ -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

View File

@ -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}"
}

View File

@ -0,0 +1,7 @@
output "hostname_list" {
value = "${join(",", alicloud_instance.instance.*.instance_name)}"
}
output "ecs_ids" {
value = "${join(",", alicloud_instance.instance.*.id)}"
}

View File

@ -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 = ""
}

View File

@ -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

View File

@ -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}"
}
}

View File

@ -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)}"
}

View File

@ -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"
}

View File

@ -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

View File

@ -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}"
}

View File

@ -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)}"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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}"
}

View File

@ -0,0 +1,7 @@
variable "security_group_name" {
default = "default-sg"
}
variable "nic_type" {
default = "internet"
}

View File

@ -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

View File

@ -0,0 +1,5 @@
resource "alicloud_security_group" "group" {
name = "${var.short_name}"
description = "New security group"
vpc_id = "${var.vpc_id}"
}

View File

@ -0,0 +1,3 @@
output "security_group" {
value = "${alicloud_security_group.group.id}"
}

View File

@ -0,0 +1,4 @@
variable "short_name" {
}
variable "vpc_id" {
}

View File

@ -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

View File

@ -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"
}]
}

View File

@ -0,0 +1,7 @@
output "slb_id" {
value = "${alicloud_slb.instance.id}"
}
output "slbname" {
value = "${alicloud_slb.instance.name}"
}

View File

@ -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.

View File

@ -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"
}]
}

View File

@ -0,0 +1,7 @@
output "slb_id" {
value = "${alicloud_slb.instance.id}"
}
output "slbname" {
value = "${alicloud_slb.instance.name}"
}

View File

@ -0,0 +1,11 @@
variable "slb_name" {
default = "slb_worder"
}
variable "internet_charge_type" {
default = "paybytraffic"
}
variable "internet" {
default = true
}

View File

@ -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

View File

@ -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