From e6665c0b972a0ccc5b418b9d83521de3115a0f80 Mon Sep 17 00:00:00 2001 From: He Guimin Date: Mon, 11 Dec 2017 09:13:44 +0800 Subject: [PATCH] add new resource ram policy and user_policy_attachment --- CHANGELOG.md | 61 ++- alicloud/config.go | 68 +++- alicloud/data_source_alicloud_ram_policies.go | 230 +++++++++++ .../data_source_alicloud_ram_policies_test.go | 149 +++++++ alicloud/errors.go | 52 ++- alicloud/import_alicloud_ram_policy_test.go | 29 ++ alicloud/provider.go | 31 +- .../resource_alicloud_ess_scalinggroup.go | 58 ++- ...resource_alicloud_ess_scalinggroup_test.go | 5 +- alicloud/resource_alicloud_ram_policy.go | 329 ++++++++++++++++ alicloud/resource_alicloud_ram_policy_test.go | 120 ++++++ ...rce_alicloud_ram_user_policy_attachment.go | 119 ++++++ ...licloud_ram_user_policy_attachment_test.go | 140 +++++++ alicloud/resource_alicloud_ram_user_test.go | 114 ++++++ .../resource_alicloud_security_group_rule.go | 86 ++-- ...ource_alicloud_security_group_rule_test.go | 133 ++++++- alicloud/service_alicloud_ecs.go | 268 +++++++------ alicloud/service_alicloud_ram.go | 180 +++++++++ alicloud/service_alicloud_rds.go | 8 +- alicloud/validators.go | 371 +++++++++++++++++- .../denverdino/aliyungo/common/client.go | 163 +++++++- .../denverdino/aliyungo/common/regions.go | 7 +- .../denverdino/aliyungo/common/request.go | 6 +- .../denverdino/aliyungo/cs/client.go | 192 +++++++++ .../denverdino/aliyungo/cs/clusters.go | 188 +++++++++ .../denverdino/aliyungo/cs/projects.go | 186 +++++++++ .../denverdino/aliyungo/cs/projects_client.go | 156 ++++++++ .../denverdino/aliyungo/cs/services.go | 143 +++++++ .../denverdino/aliyungo/cs/signature.go | 60 +++ .../denverdino/aliyungo/cs/volumes.go | 89 +++++ .../denverdino/aliyungo/ecs/client.go | 76 +++- .../denverdino/aliyungo/ecs/disks.go | 24 ++ .../github.com/denverdino/aliyungo/ecs/eni.go | 108 +++++ .../denverdino/aliyungo/ecs/images.go | 9 + .../denverdino/aliyungo/ecs/instances.go | 35 +- .../aliyungo/ecs/router_interface.go | 30 ++ .../aliyungo/ecs/security_groups.go | 9 +- .../denverdino/aliyungo/ecs/snapshots.go | 2 + .../denverdino/aliyungo/ess/configuration.go | 19 + .../denverdino/aliyungo/ess/group.go | 34 +- .../denverdino/aliyungo/ess/rule.go | 22 ++ .../denverdino/aliyungo/ram/client.go | 21 +- .../denverdino/aliyungo/rds/instances.go | 7 +- .../denverdino/aliyungo/slb/loadbalancers.go | 2 + .../denverdino/aliyungo/util/encoding.go | 12 +- vendor/vendor.json | 60 +-- website/alicloud.erb | 54 ++- website/docs/d/ram_policies.html.markdown | 47 +++ website/docs/r/ram_policy.html.markdown | 63 +++ .../ram_user_policy_attachment.html.markdown | 63 +++ 50 files changed, 4106 insertions(+), 332 deletions(-) create mode 100644 alicloud/data_source_alicloud_ram_policies.go create mode 100644 alicloud/data_source_alicloud_ram_policies_test.go create mode 100644 alicloud/import_alicloud_ram_policy_test.go create mode 100644 alicloud/resource_alicloud_ram_policy.go create mode 100644 alicloud/resource_alicloud_ram_policy_test.go create mode 100644 alicloud/resource_alicloud_ram_user_policy_attachment.go create mode 100644 alicloud/resource_alicloud_ram_user_policy_attachment_test.go create mode 100644 alicloud/resource_alicloud_ram_user_test.go create mode 100644 alicloud/service_alicloud_ram.go create mode 100644 vendor/github.com/denverdino/aliyungo/cs/client.go create mode 100644 vendor/github.com/denverdino/aliyungo/cs/clusters.go create mode 100644 vendor/github.com/denverdino/aliyungo/cs/projects.go create mode 100644 vendor/github.com/denverdino/aliyungo/cs/projects_client.go create mode 100644 vendor/github.com/denverdino/aliyungo/cs/services.go create mode 100644 vendor/github.com/denverdino/aliyungo/cs/signature.go create mode 100644 vendor/github.com/denverdino/aliyungo/cs/volumes.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/eni.go create mode 100644 website/docs/d/ram_policies.html.markdown create mode 100644 website/docs/r/ram_policy.html.markdown create mode 100644 website/docs/r/ram_user_policy_attachment.html.markdown diff --git a/CHANGELOG.md b/CHANGELOG.md index 35a38f11fdd..678f060f0d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,29 +1,56 @@ -## 0.1.1 (Unreleased) +## 1.0.0 (Unreleased) IMPROVEMENTS: -- *New Resource:* _alicloud_oss_bucket_ [GH-10] -- *New Resource*: _alicloud_oss_bucket_object [GH-14] -- *New output_file* option for data sources: export data to a specified file [GH-29] -- resource/rds: add ability to change instance password [GH-17] -- resource/rds: Add ability to import existing RDS resources [GH-16] -- datasource/alicloud_zones: Add more options for filtering [GH-19] +- *New Resource:* _alicloud_ram_policy_ [GH-46] +- *New Resource*: _alicloud_ram_user_policy_attachment_ [GH-46] +- *New Resource* _alicloud_ram_user_ [GH-44] +- *New Datasource* _alicloud_ram_policies_ [GH-46] +- *New Datasource* _alicloud_ram_users_ [GH-44] + +- Added support for importing: + - `alicloud_ram_policy` [GH-46] + - `alicloud_ram_user` [GH-44] + + +## 0.1.1 (December 11, 2017) + +IMPROVEMENTS: + +- *New Resource:* _alicloud_key_pair_ ([#27](https://github.com/terraform-providers/terraform-provider-alicloud/pull/27)) +- *New Resource*: _alicloud_key_pair_attachment_ ([#28](https://github.com/terraform-providers/terraform-provider-alicloud/pull/28)) +- *New Resource*: _alicloud_router_interface_ ([#40](https://github.com/terraform-providers/terraform-provider-alicloud/pull/40)) +- *New Resource:* _alicloud_oss_bucket_ ([#10](https://github.com/terraform-providers/terraform-provider-alicloud/pull/10)) +- *New Resource*: _alicloud_oss_bucket_object_ ([#14](https://github.com/terraform-providers/terraform-provider-alicloud/pull/14)) +- *New Datasource* _alicloud_key_pairs_ ([#30](https://github.com/terraform-providers/terraform-provider-alicloud/pull/30)) +- *New Datasource* _alicloud_vpcs_ ([#34](https://github.com/terraform-providers/terraform-provider-alicloud/pull/34)) +- *New output_file* option for data sources: export data to a specified file ([#29](https://github.com/terraform-providers/terraform-provider-alicloud/pull/29)) +- resource/instance:add new parameter `key_name` ([#31](https://github.com/terraform-providers/terraform-provider-alicloud/pull/31)) +- resource/route_entry: new nexthop type 'RouterInterface' for route entry ([#41](https://github.com/terraform-providers/terraform-provider-alicloud/pull/41)) +- resource/security_group_rule: Remove `cidr_ip` contribute "ConflictsWith" ([#39](https://github.com/terraform-providers/terraform-provider-alicloud/pull/39)) +- resource/rds: add ability to change instance password ([#17](https://github.com/terraform-providers/terraform-provider-alicloud/pull/17)) +- resource/rds: Add ability to import existing RDS resources ([#16](https://github.com/terraform-providers/terraform-provider-alicloud/pull/16)) +- datasource/alicloud_zones: Add more options for filtering ([#19](https://github.com/terraform-providers/terraform-provider-alicloud/pull/19)) - Added support for importing: - - `alicloud_nat_gateway` - - `alicloud_ess_schedule` - - `alicloud_ess_scaling_group` - - `alicloud_instance` - - `alicloud_eip` - - `alicloud_disk` + - `alicloud_vpc` ([#32](https://github.com/terraform-providers/terraform-provider-alicloud/pull/32)) + - `alicloud_route_entry` ([#33](https://github.com/terraform-providers/terraform-provider-alicloud/pull/33)) + - `alicloud_nat_gateway` ([#26](https://github.com/terraform-providers/terraform-provider-alicloud/pull/26)) + - `alicloud_ess_schedule` ([#25](https://github.com/terraform-providers/terraform-provider-alicloud/pull/25)) + - `alicloud_ess_scaling_group` ([#24](https://github.com/terraform-providers/terraform-provider-alicloud/pull/24)) + - `alicloud_instance` ([#23](https://github.com/terraform-providers/terraform-provider-alicloud/pull/23)) + - `alicloud_eip` ([#22](https://github.com/terraform-providers/terraform-provider-alicloud/pull/22)) + - `alicloud_disk` ([#21](https://github.com/terraform-providers/terraform-provider-alicloud/pull/21)) BUG FIXES: -- resource/disk_attachment: Fix issue attaching multiple disks and set disk_attachment's parameter 'device_name' as deprecated [GH-9] -- resource/rds: Fix diff error about rds security_ips [GH-13] -- resource/security_group_rule: Fix diff error when authorizing security group rules [GH-15] +- resource/disk_attachment: Fix issue attaching multiple disks and set disk_attachment's parameter 'device_name' as deprecated ([#9](https://github.com/terraform-providers/terraform-provider-alicloud/pull/9)) +- resource/rds: Fix diff error about rds security_ips ([#13](https://github.com/terraform-providers/terraform-provider-alicloud/pull/13)) +- resource/security_group_rule: Fix diff error when authorizing security group rules ([#15](https://github.com/terraform-providers/terraform-provider-alicloud/pull/15)) +- resource/security_group_rule: Fix diff bug by modifying 'DestCidrIp' to 'DestGroupId' when running read ([#35](https://github.com/terraform-providers/terraform-provider-alicloud/pull/35)) + ## 0.1.0 (June 20, 2017) NOTES: -* Same functionality as that of Terraform 0.9.8. Repacked as part of [Provider Splitout](https://www.hashicorp.com/blog/upcoming-provider-changes-in-terraform-0-10/) +* Same functionality as that of Terraform 0.9.8. Repacked as part of [Provider Splitout](https://www.hashicorp.com/blog/upcoming-provider-changes-in-terraform-0-10/) \ No newline at end of file diff --git a/alicloud/config.go b/alicloud/config.go index 4743ac9e4d9..5f14c34d3cf 100644 --- a/alicloud/config.go +++ b/alicloud/config.go @@ -4,12 +4,17 @@ import ( "fmt" "github.com/aliyun/aliyun-oss-go-sdk/oss" + "github.com/denverdino/aliyungo/cdn" "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/dns" "github.com/denverdino/aliyungo/ecs" "github.com/denverdino/aliyungo/ess" "github.com/denverdino/aliyungo/location" + "github.com/denverdino/aliyungo/ram" "github.com/denverdino/aliyungo/rds" "github.com/denverdino/aliyungo/slb" + + "github.com/denverdino/aliyungo/cs" "github.com/hashicorp/terraform/terraform" "log" "strings" @@ -33,6 +38,10 @@ type AliyunClient struct { vpcconn *ecs.Client slbconn *slb.Client ossconn *oss.Client + dnsconn *dns.Client + ramconn ram.RamClientInterface + csconn *cs.Client + cdnconn *cdn.CdnClient } // Client for AliyunClient @@ -72,11 +81,27 @@ func (c *Config) Client() (*AliyunClient, error) { if err != nil { return nil, err } - ossconn, err := c.ossConn() if err != nil { return nil, err } + dnsconn, err := c.dnsConn() + if err != nil { + return nil, err + } + ramconn, err := c.ramConn() + if err != nil { + return nil, err + } + csconn, err := c.csConn() + if err != nil { + return nil, err + } + cdnconn, err := c.cdnConn() + if err != nil { + return nil, err + } + return &AliyunClient{ Region: c.Region, ecsconn: ecsconn, @@ -86,6 +111,10 @@ func (c *Config) Client() (*AliyunClient, error) { rdsconn: rdsconn, essconn: essconn, ossconn: ossconn, + dnsconn: dnsconn, + ramconn: ramconn, + csconn: csconn, + cdnconn: cdnconn, }, nil } @@ -116,9 +145,7 @@ func (c *Config) ecsConn() (*ecs.Client, error) { client.SetBusinessInfo(BusinessInfoKey) client.SetUserAgent(getUserAgent()) - _, err := client.DescribeRegions() - - if err != nil { + if _, err := client.DescribeRegions(); err != nil { return nil, err } @@ -166,17 +193,46 @@ func (c *Config) ossConn() (*oss.Client, error) { return nil, fmt.Errorf("Describe endpoint using region: %#v got an error: %#v.", c.Region, err) } endpointItem := endpoints.Endpoints.Endpoint + var endpoint string if endpointItem == nil || len(endpointItem) <= 0 { - return nil, fmt.Errorf("Cannot find endpoint in the region: %#v", c.Region) + // return nil, fmt.Errorf("Cannot find endpoint in the region: %#v", c.Region") + log.Printf("Cannot find endpoint in the region: %#v", c.Region) + endpoint = "" + } else { + endpoint = strings.ToLower(endpointItem[0].Protocols.Protocols[0]) + "://" + endpointItem[0].Endpoint } - endpoint := strings.ToLower(endpointItem[0].Protocols.Protocols[0]) + "://" + endpointItem[0].Endpoint log.Printf("[DEBUG] Instantiate OSS client using endpoint: %#v", endpoint) client, err := oss.New(endpoint, c.AccessKey, c.SecretKey, oss.UserAgent(getUserAgent())) return client, err } +func (c *Config) dnsConn() (*dns.Client, error) { + client := dns.NewClientNew(c.AccessKey, c.SecretKey) + client.SetBusinessInfo(BusinessInfoKey) + client.SetUserAgent(getUserAgent()) + return client, nil +} + +func (c *Config) ramConn() (ram.RamClientInterface, error) { + client := ram.NewClient(c.AccessKey, c.SecretKey) + return client, nil +} + +func (c *Config) csConn() (*cs.Client, error) { + client := cs.NewClient(c.AccessKey, c.SecretKey) + client.SetUserAgent(getUserAgent()) + return client, nil +} + +func (c *Config) cdnConn() (*cdn.CdnClient, error) { + client := cdn.NewClient(c.AccessKey, c.SecretKey) + client.SetBusinessInfo(BusinessInfoKey) + client.SetUserAgent(getUserAgent()) + return client, nil +} + func getUserAgent() string { return fmt.Sprintf("HashiCorp-Terraform-v%s", terraform.VersionString()) } diff --git a/alicloud/data_source_alicloud_ram_policies.go b/alicloud/data_source_alicloud_ram_policies.go new file mode 100644 index 00000000000..a43593e22cf --- /dev/null +++ b/alicloud/data_source_alicloud_ram_policies.go @@ -0,0 +1,230 @@ +package alicloud + +import ( + "fmt" + "log" + "regexp" + + "github.com/denverdino/aliyungo/ram" + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceAlicloudRamPolicies() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAlicloudRamPoliciesRead, + + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validatePolicyType, + }, + "name_regex": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "group_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateRamGroupName, + }, + "user_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateRamName, + }, + "role_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateRamName, + }, + "output_file": { + Type: schema.TypeString, + Optional: true, + }, + + // Computed values + "policies": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "default_version": { + Type: schema.TypeString, + Computed: true, + }, + "create_date": { + Type: schema.TypeString, + Computed: true, + }, + "update_date": { + Type: schema.TypeString, + Computed: true, + }, + "attachment_count": { + Type: schema.TypeInt, + Computed: true, + }, + "document": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceAlicloudRamPoliciesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + allPolicies := []interface{}{} + + allPoliciesMap := make(map[string]interface{}) + userFilterPoliciesMap := make(map[string]interface{}) + groupFilterPoliciesMap := make(map[string]interface{}) + roleFilterPoliciesMap := make(map[string]interface{}) + + dataMap := []map[string]interface{}{} + + userName, userNameOk := d.GetOk("user_name") + groupName, groupNameOk := d.GetOk("group_name") + roleName, roleNameOk := d.GetOk("role_name") + policyType, policyTypeOk := d.GetOk("type") + nameRegex, nameRegexOk := d.GetOk("name_regex") + + // policies filtered by name_regex and type + args := ram.PolicyQueryRequest{} + for { + resp, err := conn.ListPolicies(args) + if err != nil { + return fmt.Errorf("ListPolicies got an error: %#v", err) + } + for _, v := range resp.Policies.Policy { + if policyTypeOk && policyType.(string) != v.PolicyType { + continue + } + if nameRegexOk { + r := regexp.MustCompile(nameRegex.(string)) + if !r.MatchString(v.PolicyName) { + continue + } + } + allPoliciesMap[v.PolicyType+v.PolicyName] = v + } + if !resp.IsTruncated { + break + } + args.Marker = resp.Marker + } + + // policies for user + if userNameOk { + resp, err := conn.ListPoliciesForUser(ram.UserQueryRequest{UserName: userName.(string)}) + if err != nil { + return fmt.Errorf("ListPoliciesForUser got an error: %#v", err) + } + + for _, v := range resp.Policies.Policy { + userFilterPoliciesMap[v.PolicyType+v.PolicyName] = v + } + dataMap = append(dataMap, userFilterPoliciesMap) + } + + // policies for group + if groupNameOk { + resp, err := conn.ListPoliciesForGroup(ram.GroupQueryRequest{GroupName: groupName.(string)}) + if err != nil { + return fmt.Errorf("ListPoliciesForGroup got an error: %#v", err) + } + + for _, v := range resp.Policies.Policy { + groupFilterPoliciesMap[v.PolicyType+v.PolicyName] = v + } + dataMap = append(dataMap, groupFilterPoliciesMap) + } + + // policies for role + if roleNameOk { + resp, err := conn.ListPoliciesForRole(ram.RoleQueryRequest{RoleName: roleName.(string)}) + if err != nil { + return fmt.Errorf("ListPoliciesForRole got an error: %#v", err) + } + + for _, v := range resp.Policies.Policy { + roleFilterPoliciesMap[v.PolicyType+v.PolicyName] = v + } + dataMap = append(dataMap, roleFilterPoliciesMap) + } + + // GetIntersection of each map + allPolicies = GetIntersection(dataMap, allPoliciesMap) + + if len(allPolicies) < 1 { + return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") + } + + log.Printf("[DEBUG] alicloud_ram_policies - Policies found: %#v", allPolicies) + + return ramPoliciesDescriptionAttributes(d, allPolicies, meta) +} + +func ramPoliciesDescriptionAttributes(d *schema.ResourceData, policies []interface{}, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + var ids []string + var s []map[string]interface{} + for _, v := range policies { + policy := v.(ram.Policy) + resp, err := conn.GetPolicyVersionNew(ram.PolicyRequest{ + PolicyName: policy.PolicyName, + PolicyType: ram.Type(policy.PolicyType), + VersionId: policy.DefaultVersion, + }) + if err != nil { + return err + } + + mapping := map[string]interface{}{ + "name": policy.PolicyName, + "type": policy.PolicyType, + "description": policy.Description, + "default_version": policy.DefaultVersion, + "attachment_count": int(policy.AttachmentCount), + "create_date": policy.CreateDate, + "update_date": policy.UpdateDate, + "document": resp.PolicyVersion.PolicyDocument, + } + + log.Printf("[DEBUG] alicloud_ram_policies - adding policy: %v", mapping) + ids = append(ids, policy.PolicyName) + s = append(s, mapping) + } + + d.SetId(dataResourceIdHash(ids)) + if err := d.Set("policies", s); err != nil { + return err + } + + // create a json file in current directory and write data source to it. + if output, ok := d.GetOk("output_file"); ok && output.(string) != "" { + writeToFile(output.(string), s) + } + return nil +} diff --git a/alicloud/data_source_alicloud_ram_policies_test.go b/alicloud/data_source_alicloud_ram_policies_test.go new file mode 100644 index 00000000000..7d97180144d --- /dev/null +++ b/alicloud/data_source_alicloud_ram_policies_test.go @@ -0,0 +1,149 @@ +package alicloud + +import ( + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccAlicloudRamPoliciesDataSource_for_group(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourceForGroupConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "1"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.0.policy_name", "ReadOnlyAccess"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.0.policy_type", "System"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.0.description", "只读访问所有阿里云资源的权限"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.0.default_version", "v2"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.0.policy_document", "{\n \"Version\": \"1\", \n \"Statement\": [\n {\n \"Action\": [\n \"*:Describe*\", \n \"*:List*\", \n \"*:Get*\", \n \"*:BatchGet*\", \n \"*:Query*\", \n \"*:BatchQuery*\", \n \"actiontrail:LookupEvents\", \n \"dm:Desc*\", \n \"dm:SenderStatistics*\"\n ], \n \"Resource\": \"*\", \n \"Effect\": \"Allow\"\n }\n ]\n}"), + ), + }, + }, + }) +} + +func TestAccAlicloudRamPoliciesDataSource_for_role(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourceForRoleConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "1"), + ), + }, + }, + }) +} + +func TestAccAlicloudRamPoliciesDataSource_for_user(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourceForUserConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "2"), + ), + }, + }, + }) +} + +func TestAccAlicloudRamPoliciesDataSource_for_all(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourceForAllConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "131"), + ), + }, + }, + }) +} + +func TestAccAlicloudRamPoliciesDataSource_policy_name_regex(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourcePolicyNameRegexConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "74"), + ), + }, + }, + }) +} + +func TestAccAlicloudRamPoliciesDataSource_policy_type(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourcePolicyTypeConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "1"), + ), + }, + }, + }) +} + +const testAccCheckAlicloudRamPoliciessDataSourceForGroupConfig = ` +data "alicloud_ram_policies" "policy" { + group_name = "group2" +}` + +const testAccCheckAlicloudRamPoliciessDataSourceForRoleConfig = ` +data "alicloud_ram_policies" "policy" { + role_name = "testrole" + type = "Custom" +}` + +const testAccCheckAlicloudRamPoliciessDataSourceForUserConfig = ` +data "alicloud_ram_policies" "policy" { + user_name = "user1" +}` + +const testAccCheckAlicloudRamPoliciessDataSourceForAllConfig = ` +data "alicloud_ram_policies" "policy" { +}` + +const testAccCheckAlicloudRamPoliciessDataSourcePolicyNameRegexConfig = ` +data "alicloud_ram_policies" "policy" { + name_regex = ".*Full.*" +}` + +const testAccCheckAlicloudRamPoliciessDataSourcePolicyTypeConfig = ` +data "alicloud_ram_policies" "policy" { + type = "Custom" +}` diff --git a/alicloud/errors.go b/alicloud/errors.go index e30b1e3b889..468a8b5efd4 100644 --- a/alicloud/errors.go +++ b/alicloud/errors.go @@ -23,9 +23,12 @@ const ( InstanceIncorrectStatus = "IncorrectInstanceStatus" HaVipIncorrectStatus = "IncorrectHaVipStatus" // slb - LoadBalancerNotFound = "InvalidLoadBalancerId.NotFound" - UnsupportedProtocalPort = "UnsupportedOperationonfixedprotocalport" - + LoadBalancerNotFound = "InvalidLoadBalancerId.NotFound" + UnsupportedProtocalPort = "UnsupportedOperationonfixedprotocalport" + ListenerNotFound = "The specified resource does not exist" + ListenerAlreadyExists = "ListenerAlreadyExists" + ServiceIsConfiguring = "ServiceIsConfiguring" + BackendServerconfiguring = "BackendServer.configuring" // security_group InvalidInstanceIdAlreadyExists = "InvalidInstanceId.AlreadyExists" InvalidSecurityGroupIdNotFound = "InvalidSecurityGroupId.NotFound" @@ -41,10 +44,15 @@ const ( VpcQuotaExceeded = "QuotaExceeded.Vpc" // vswitch VswitcInvalidRegionId = "InvalidRegionId.NotFound" + //vroute entry + IncorrectRouteEntryStatus = "IncorrectRouteEntryStatus" + TaskConflict = "TaskConflict" + RouterEntryForbbiden = "Forbbiden" // ess InvalidScalingGroupIdNotFound = "InvalidScalingGroupId.NotFound" IncorrectScalingConfigurationLifecycleState = "IncorrectScalingConfigurationLifecycleState" + IncorrectScalingGroupStatus = "IncorrectScalingGroupStatus" // oss OssBucketNotFound = "NoSuchBucket" @@ -54,6 +62,32 @@ const ( RamInstanceNotFound = "Forbidden.InstanceNotFound" AliyunGoClientFailure = "AliyunGoClientFailure" + // dns + RecordForbiddenDNSChange = "RecordForbidden.DNSChange" + FobiddenNotEmptyGroup = "Fobidden.NotEmptyGroup" + + // ram user + DeleteConflictUserGroup = "DeleteConflict.User.Group" + DeleteConflictUserAccessKey = "DeleteConflict.User.AccessKey" + DeleteConflictUserLoginProfile = "DeleteConflict.User.LoginProfile" + DeleteConflictUserMFADevice = "DeleteConflict.User.MFADevice" + DeleteConflictUserPolicy = "DeleteConflict.User.Policy" + + // ram mfa + DeleteConflictVirtualMFADeviceUser = "DeleteConflict.VirtualMFADevice.User" + + // ram group + DeleteConflictGroupUser = "DeleteConflict.Group.User" + DeleteConflictGroupPolicy = "DeleteConflict.Group.Policy" + + // ram role + DeleteConflictRolePolicy = "DeleteConflict.Role.Policy" + + // ram policy + DeleteConflictPolicyUser = "DeleteConflict.Policy.User" + DeleteConflictPolicyGroup = "DeleteConflict.Policy.Group" + DeleteConflictPolicyVersion = "DeleteConflict.Policy.Version" + //unknown Error UnknownError = "UnknownError" @@ -67,6 +101,11 @@ const ( // cdn ServiceBusy = "ServiceBusy" + // + InvalidRamRoleNotFound = "InvalidRamRole.NotFound" + RoleAttachmentUnExpectedJson = "unexpected end of JSON input" + InvalidInstanceIdNotFound = "InvalidInstanceId.NotFound" + RouterInterfaceIncorrectStatus = "IncorrectStatus" DependencyViolationRouterInterfaceReferedByRouteEntry = "DependencyViolation.RouterInterfaceReferedByRouteEntry" ) @@ -98,3 +137,10 @@ func IsExceptedError(err error, expectCode string) bool { return false } + +func RamEntityNotExist(err error) bool { + if e, ok := err.(*common.Error); ok && strings.Contains(e.Code, "EntityNotExist") { + return true + } + return false +} diff --git a/alicloud/import_alicloud_ram_policy_test.go b/alicloud/import_alicloud_ram_policy_test.go new file mode 100644 index 00000000000..c694a683043 --- /dev/null +++ b/alicloud/import_alicloud_ram_policy_test.go @@ -0,0 +1,29 @@ +package alicloud + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAlicloudRamPolicy_importBasic(t *testing.T) { + resourceName := "alicloud_ram_policy.policy" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRamPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRamPolicyConfig, + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force"}, + }, + }, + }) +} diff --git a/alicloud/provider.go b/alicloud/provider.go index f3e28518dc8..09d224b1871 100644 --- a/alicloud/provider.go +++ b/alicloud/provider.go @@ -39,6 +39,7 @@ func Provider() terraform.ResourceProvider { "alicloud_instance_types": dataSourceAlicloudInstanceTypes(), "alicloud_vpcs": dataSourceAlicloudVpcs(), "alicloud_key_pairs": dataSourceAlicloudKeyPairs(), + "alicloud_ram_policies": dataSourceAlicloudRamPolicies(), }, ResourcesMap: map[string]*schema.Resource{ "alicloud_instance": resourceAliyunInstance(), @@ -54,20 +55,22 @@ func Provider() terraform.ResourceProvider { "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_snat_entry": resourceAliyunSnatEntry(), - "alicloud_forward_entry": resourceAliyunForwardEntry(), - "alicloud_eip": resourceAliyunEip(), - "alicloud_eip_association": resourceAliyunEipAssociation(), - "alicloud_slb": resourceAliyunSlb(), - "alicloud_slb_attachment": resourceAliyunSlbAttachment(), - "alicloud_oss_bucket": resourceAlicloudOssBucket(), - "alicloud_oss_bucket_object": resourceAlicloudOssBucketObject(), - "alicloud_key_pair": resourceAlicloudKeyPair(), - "alicloud_key_pair_attachment": resourceAlicloudKeyPairAttachment(), - "alicloud_router_interface": resourceAlicloudRouterInterface(), + "alicloud_subnet": resourceAliyunSubnet(), + "alicloud_vswitch": resourceAliyunSubnet(), + "alicloud_route_entry": resourceAliyunRouteEntry(), + "alicloud_snat_entry": resourceAliyunSnatEntry(), + "alicloud_forward_entry": resourceAliyunForwardEntry(), + "alicloud_eip": resourceAliyunEip(), + "alicloud_eip_association": resourceAliyunEipAssociation(), + "alicloud_slb": resourceAliyunSlb(), + "alicloud_slb_attachment": resourceAliyunSlbAttachment(), + "alicloud_oss_bucket": resourceAlicloudOssBucket(), + "alicloud_oss_bucket_object": resourceAlicloudOssBucketObject(), + "alicloud_key_pair": resourceAlicloudKeyPair(), + "alicloud_key_pair_attachment": resourceAlicloudKeyPairAttachment(), + "alicloud_ram_policy": resourceAlicloudRamPolicy(), + "alicloud_ram_user_policy_attachment": resourceAlicloudRamUserPolicyAtatchment(), + "alicloud_router_interface": resourceAlicloudRouterInterface(), }, ConfigureFunc: providerConfigure, diff --git a/alicloud/resource_alicloud_ess_scalinggroup.go b/alicloud/resource_alicloud_ess_scalinggroup.go index f188dc2f698..006f7242c4d 100644 --- a/alicloud/resource_alicloud_ess_scalinggroup.go +++ b/alicloud/resource_alicloud_ess_scalinggroup.go @@ -2,12 +2,9 @@ package alicloud import ( "fmt" - "github.com/denverdino/aliyungo/common" "github.com/denverdino/aliyungo/ess" - "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "strings" - "time" ) func resourceAlicloudEssScalingGroup() *schema.Resource { @@ -91,7 +88,7 @@ func resourceAliyunEssScalingGroupRead(d *schema.ResourceData, meta interface{}) scaling, err := client.DescribeScalingGroupById(d.Id()) if err != nil { - if e, ok := err.(*common.Error); ok && e.Code == InstanceNotfound { + if NotFoundError(err) { d.SetId("") return nil } @@ -102,7 +99,7 @@ func resourceAliyunEssScalingGroupRead(d *schema.ResourceData, meta interface{}) d.Set("max_size", scaling.MaxSize) d.Set("scaling_group_name", scaling.ScalingGroupName) d.Set("default_cooldown", scaling.DefaultCooldown) - d.Set("removal_policies", scaling.RemovalPolicies) + d.Set("removal_policies", scaling.RemovalPolicies.RemovalPolicy) d.Set("db_instance_ids", scaling.DBInstanceIds) d.Set("loadbalancer_ids", scaling.LoadBalancerId) @@ -115,69 +112,64 @@ func resourceAliyunEssScalingGroupUpdate(d *schema.ResourceData, meta interface{ args := &ess.ModifyScalingGroupArgs{ ScalingGroupId: d.Id(), } + d.Partial(true) if d.HasChange("scaling_group_name") { args.ScalingGroupName = d.Get("scaling_group_name").(string) + d.SetPartial("scaling_group_name") } if d.HasChange("min_size") { - args.MinSize = d.Get("min_size").(int) + minsize := d.Get("min_size").(int) + args.MinSize = &minsize + d.SetPartial("min_size") } if d.HasChange("max_size") { - args.MaxSize = d.Get("max_size").(int) + maxsize := d.Get("max_size").(int) + args.MaxSize = &maxsize + d.SetPartial("max_size") } if d.HasChange("default_cooldown") { - args.DefaultCooldown = d.Get("default_cooldown").(int) + cooldown := d.Get("default_cooldown").(int) + args.DefaultCooldown = &cooldown + d.SetPartial("default_cooldown") } if d.HasChange("removal_policies") { policyStrings := d.Get("removal_policies").([]interface{}) args.RemovalPolicy = expandStringList(policyStrings) + d.SetPartial("removal_policies") } if _, err := conn.ModifyScalingGroup(args); err != nil { return err } + d.Partial(false) + return resourceAliyunEssScalingGroupRead(d, meta) } func resourceAliyunEssScalingGroupDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*AliyunClient) - - return resource.Retry(2*time.Minute, func() *resource.RetryError { - err := client.DeleteScalingGroupById(d.Id()) - - if err != nil { - e, _ := err.(*common.Error) - if e.ErrorResponse.Code != InvalidScalingGroupIdNotFound { - return resource.RetryableError(fmt.Errorf("Scaling group in use - trying again while it is deleted.")) - } - } - _, err = client.DescribeScalingGroupById(d.Id()) - if err != nil { - if notFoundError(err) { - return nil - } - return resource.NonRetryableError(err) - } - - return resource.RetryableError(fmt.Errorf("Scaling group in use - trying again while it is deleted.")) - }) + return meta.(*AliyunClient).DeleteScalingGroupById(d.Id()) } func buildAlicloudEssScalingGroupArgs(d *schema.ResourceData, meta interface{}) (*ess.CreateScalingGroupArgs, error) { client := meta.(*AliyunClient) args := &ess.CreateScalingGroupArgs{ - RegionId: getRegion(d, meta), - MinSize: d.Get("min_size").(int), - MaxSize: d.Get("max_size").(int), - DefaultCooldown: d.Get("default_cooldown").(int), + RegionId: getRegion(d, meta), } + minsize := d.Get("min_size").(int) + maxsize := d.Get("max_size").(int) + cooldown := d.Get("default_cooldown").(int) + args.MinSize = &minsize + args.MaxSize = &maxsize + args.DefaultCooldown = &cooldown + if v := d.Get("scaling_group_name").(string); v != "" { args.ScalingGroupName = v } diff --git a/alicloud/resource_alicloud_ess_scalinggroup_test.go b/alicloud/resource_alicloud_ess_scalinggroup_test.go index e707035b1f0..b973c60a770 100644 --- a/alicloud/resource_alicloud_ess_scalinggroup_test.go +++ b/alicloud/resource_alicloud_ess_scalinggroup_test.go @@ -210,7 +210,7 @@ func testAccCheckEssScalingGroupDestroy(s *terraform.State) error { if err != nil { // Verify the error is what we want e, _ := err.(*common.Error) - if e.ErrorResponse.Code == InstanceNotfound { + if e.Code == InstanceNotFound { continue } return err @@ -287,8 +287,7 @@ resource "alicloud_ess_scaling_configuration" "foo" { enable = true image_id = "${data.alicloud_images.ecs_image.images.0.id}" - instance_type = "ecs.n1.medium" - io_optimized = "optimized" + instance_type = "ecs.n4.large" system_disk_category = "cloud_efficiency" internet_charge_type = "PayByTraffic" internet_max_bandwidth_out = 10 diff --git a/alicloud/resource_alicloud_ram_policy.go b/alicloud/resource_alicloud_ram_policy.go new file mode 100644 index 00000000000..c9114dfa52a --- /dev/null +++ b/alicloud/resource_alicloud_ram_policy.go @@ -0,0 +1,329 @@ +package alicloud + +import ( + "fmt" + "time" + + "github.com/denverdino/aliyungo/ram" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAlicloudRamPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceAlicloudRamPolicyCreate, + Read: resourceAlicloudRamPolicyRead, + Update: resourceAlicloudRamPolicyUpdate, + Delete: resourceAlicloudRamPolicyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateRamPolicyName, + }, + "statement": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "effect": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := Effect(v.(string)) + if value != Allow && value != Deny { + errors = append(errors, fmt.Errorf( + "%q must be '%s' or '%s'.", k, Allow, Deny)) + } + return + }, + }, + "action": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "resource": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + ConflictsWith: []string{"document"}, + }, + "document": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"statement", "version"}, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + value := v.(string) + if len(value) > 2048 { + es = append(es, fmt.Errorf("%q can not be longer than 2048 characters.", k)) + } + return + }, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateRamDesc, + }, + "version": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "1", + ConflictsWith: []string{"document"}, + ValidateFunc: validatePolicyDocVersion, + }, + "force": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "attachment_count": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + }, + } +} + +func resourceAlicloudRamPolicyCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args, err := buildAlicloudRamPolicyCreateArgs(d, meta) + if err != nil { + return err + } + + response, err := conn.CreatePolicy(args) + if err != nil { + return fmt.Errorf("CreatePolicy got an error: %#v", err) + } + + d.SetId(response.Policy.PolicyName) + return resourceAlicloudRamPolicyUpdate(d, meta) +} + +func resourceAlicloudRamPolicyUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + d.Partial(true) + + args, attributeUpdate, err := buildAlicloudRamPolicyUpdateArgs(d, meta) + if err != nil { + return err + } + + if !d.IsNewResource() && attributeUpdate { + if _, err := conn.CreatePolicyVersion(args); err != nil { + return fmt.Errorf("Error updating policy %s: %#v", d.Id(), err) + } + } + + d.Partial(false) + + return resourceAlicloudRamPolicyRead(d, meta) +} + +func resourceAlicloudRamPolicyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args := ram.PolicyRequest{ + PolicyName: d.Id(), + PolicyType: ram.Custom, + } + + policyResp, err := conn.GetPolicy(args) + if err != nil { + if RamEntityNotExist(err) { + d.SetId("") + } + return fmt.Errorf("GetPolicy got an error: %#v", err) + } + policy := policyResp.Policy + + args.VersionId = policy.DefaultVersion + policyVersionResp, err := conn.GetPolicyVersionNew(args) + if err != nil { + return fmt.Errorf("GetPolicyVersion got an error: %#v", err) + } + + statement, version, err := ParsePolicyDocument(policyVersionResp.PolicyVersion.PolicyDocument) + if err != nil { + return err + } + + d.Set("name", policy.PolicyName) + d.Set("type", policy.PolicyType) + d.Set("description", policy.Description) + d.Set("attachment_count", policy.AttachmentCount) + d.Set("version", version) + d.Set("statement", statement) + d.Set("document", policyVersionResp.PolicyVersion.PolicyDocument) + + return nil +} + +func resourceAlicloudRamPolicyDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args := ram.PolicyRequest{ + PolicyName: d.Id(), + } + + if d.Get("force").(bool) { + args.PolicyType = ram.Custom + + // list and detach entities for this policy + response, err := conn.ListEntitiesForPolicy(args) + if err != nil { + return fmt.Errorf("Error listing entities for policy %s when trying to delete: %#v", d.Id(), err) + } + + if len(response.Users.User) > 0 { + for _, v := range response.Users.User { + _, err := conn.DetachPolicyFromUser(ram.AttachPolicyRequest{ + PolicyRequest: args, + UserName: v.UserName, + }) + if err != nil && !RamEntityNotExist(err) { + return fmt.Errorf("Error detaching policy %s from user %s:%#v", d.Id(), v.UserId, err) + } + } + } + + if len(response.Groups.Group) > 0 { + for _, v := range response.Groups.Group { + _, err := conn.DetachPolicyFromGroup(ram.AttachPolicyToGroupRequest{ + PolicyRequest: args, + GroupName: v.GroupName, + }) + if err != nil && !RamEntityNotExist(err) { + return fmt.Errorf("Error detaching policy %s from group %s:%#v", d.Id(), v.GroupName, err) + } + } + } + + if len(response.Roles.Role) > 0 { + for _, v := range response.Roles.Role { + _, err := conn.DetachPolicyFromRole(ram.AttachPolicyToRoleRequest{ + PolicyRequest: args, + RoleName: v.RoleName, + }) + if err != nil && !RamEntityNotExist(err) { + return fmt.Errorf("Error detaching policy %s from role %s:%#v", d.Id(), v.RoleId, err) + } + } + } + + // list and delete policy version which are not default + pvResp, err := conn.ListPolicyVersionsNew(args) + if err != nil { + return fmt.Errorf("Error listing policy versions for policy %s:%#v", d.Id(), err) + } + if len(pvResp.PolicyVersions.PolicyVersion) > 1 { + for _, v := range pvResp.PolicyVersions.PolicyVersion { + if !v.IsDefaultVersion { + args.VersionId = v.VersionId + if _, err = conn.DeletePolicyVersion(args); err != nil && !RamEntityNotExist(err) { + return fmt.Errorf("Error delete policy version %s for policy %s:%#v", v.VersionId, d.Id(), err) + } + } + } + } + } + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + if _, err := conn.DeletePolicy(args); err != nil { + if IsExceptedError(err, DeleteConflictPolicyUser) || IsExceptedError(err, DeleteConflictPolicyGroup) || IsExceptedError(err, DeleteConflictRolePolicy) { + return resource.RetryableError(fmt.Errorf("The policy can not been attached to any user or group or role while deleting the policy. - you can set force with true to force delete the policy.")) + } + if IsExceptedError(err, DeleteConflictPolicyVersion) { + return resource.RetryableError(fmt.Errorf("The policy can not has any version except the defaul version. - you can set force with true to force delete the policy.")) + } + return resource.NonRetryableError(fmt.Errorf("Error deleting policy %s: %#v", d.Id(), err)) + } + return nil + }) +} + +func buildAlicloudRamPolicyCreateArgs(d *schema.ResourceData, meta interface{}) (ram.PolicyRequest, error) { + var document string + + doc, docOk := d.GetOk("document") + statement, statementOk := d.GetOk("statement") + + if !docOk && !statementOk { + return ram.PolicyRequest{}, fmt.Errorf("One of 'document' and 'statement' must be specified.") + } + + if docOk { + document = doc.(string) + } else { + doc, err := AssemblePolicyDocument(statement.(*schema.Set).List(), d.Get("version").(string)) + if err != nil { + return ram.PolicyRequest{}, err + } + document = doc + } + + args := ram.PolicyRequest{ + PolicyName: d.Get("name").(string), + PolicyDocument: document, + } + + if v, ok := d.GetOk("description"); ok && v.(string) != "" { + args.Description = v.(string) + } + + return args, nil +} + +func buildAlicloudRamPolicyUpdateArgs(d *schema.ResourceData, meta interface{}) (ram.PolicyRequest, bool, error) { + args := ram.PolicyRequest{ + PolicyName: d.Id(), + SetAsDefault: "true", + } + + attributeUpdate := false + if d.HasChange("document") { + d.SetPartial("document") + attributeUpdate = true + args.PolicyDocument = d.Get("document").(string) + + } else if d.HasChange("statement") || d.HasChange("version") { + attributeUpdate = true + + if d.HasChange("statement") { + d.SetPartial("statement") + } + if d.HasChange("version") { + d.SetPartial("version") + } + + document, err := AssemblePolicyDocument(d.Get("statement").(*schema.Set).List(), d.Get("version").(string)) + if err != nil { + return ram.PolicyRequest{}, attributeUpdate, err + } + args.PolicyDocument = document + } + + return args, attributeUpdate, nil +} diff --git a/alicloud/resource_alicloud_ram_policy_test.go b/alicloud/resource_alicloud_ram_policy_test.go new file mode 100644 index 00000000000..49e2f26d83e --- /dev/null +++ b/alicloud/resource_alicloud_ram_policy_test.go @@ -0,0 +1,120 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ram" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" +) + +func TestAccAlicloudRamPolicy_basic(t *testing.T) { + var v ram.Policy + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_ram_policy.policy", + + Providers: testAccProviders, + CheckDestroy: testAccCheckRamPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRamPolicyConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRamPolicyExists( + "alicloud_ram_policy.policy", &v), + resource.TestCheckResourceAttr( + "alicloud_ram_policy.policy", + "name", + "policyname"), + resource.TestCheckResourceAttr( + "alicloud_ram_policy.policy", + "description", + "this is a policy test"), + ), + }, + }, + }) + +} + +func testAccCheckRamPolicyExists(n string, policy *ram.Policy) 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 Policy ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ramconn + + request := ram.PolicyRequest{ + PolicyName: rs.Primary.ID, + PolicyType: ram.Custom, + } + + response, err := conn.GetPolicy(request) + log.Printf("[WARN] Policy id %#v", rs.Primary.ID) + + if err == nil { + *policy = response.Policy + return nil + } + return fmt.Errorf("Error finding policy %#v", rs.Primary.ID) + } +} + +func testAccCheckRamPolicyDestroy(s *terraform.State) error { + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_ram_policy" { + continue + } + + // Try to find the policy + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ramconn + + request := ram.PolicyRequest{ + PolicyName: rs.Primary.ID, + PolicyType: ram.Custom, + } + + _, err := conn.GetPolicy(request) + + if err != nil { + if RamEntityNotExist(err) { + return nil + } + return err + } + } + return nil +} + +const testAccRamPolicyConfig = ` +resource "alicloud_ram_policy" "policy" { + name = "policyname" + statement = [ + { + effect = "Deny" + action = [ + "oss:ListObjects", + "oss:ListObjects"] + resource = [ + "acs:oss:*:*:mybucket", + "acs:oss:*:*:mybucket/*"] + }] + description = "this is a policy test" + force = true +}` diff --git a/alicloud/resource_alicloud_ram_user_policy_attachment.go b/alicloud/resource_alicloud_ram_user_policy_attachment.go new file mode 100644 index 00000000000..9e63d233259 --- /dev/null +++ b/alicloud/resource_alicloud_ram_user_policy_attachment.go @@ -0,0 +1,119 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ram" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAlicloudRamUserPolicyAtatchment() *schema.Resource { + return &schema.Resource{ + Create: resourceAlicloudRamUserPolicyAttachmentCreate, + Read: resourceAlicloudRamUserPolicyAttachmentRead, + Delete: resourceAlicloudRamUserPolicyAttachmentDelete, + + Schema: map[string]*schema.Schema{ + "user_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateRamName, + }, + "policy_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateRamPolicyName, + }, + "policy_type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validatePolicyType, + }, + }, + } +} + +func resourceAlicloudRamUserPolicyAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args := ram.AttachPolicyRequest{ + PolicyRequest: ram.PolicyRequest{ + PolicyName: d.Get("policy_name").(string), + PolicyType: ram.Type(d.Get("policy_type").(string)), + }, + UserName: d.Get("user_name").(string), + } + + if _, err := conn.AttachPolicyToUser(args); err != nil { + return fmt.Errorf("AttachPolicyToUser got an error: %#v", err) + } + + d.SetId("user" + args.PolicyName + string(args.PolicyType) + args.UserName) + return resourceAlicloudRamUserPolicyAttachmentRead(d, meta) +} + +func resourceAlicloudRamUserPolicyAttachmentRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args := ram.UserQueryRequest{ + UserName: d.Get("user_name").(string), + } + + response, err := conn.ListPoliciesForUser(args) + if err != nil { + return fmt.Errorf("Get list policies for user got an error: %#v", err) + } + + if len(response.Policies.Policy) > 0 { + for _, v := range response.Policies.Policy { + if v.PolicyName == d.Get("policy_name").(string) && v.PolicyType == d.Get("policy_type").(string) { + d.Set("user_name", args.UserName) + d.Set("policy_name", v.PolicyName) + d.Set("policy_type", v.PolicyType) + return nil + } + } + } + + d.SetId("") + return nil +} + +func resourceAlicloudRamUserPolicyAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args := ram.AttachPolicyRequest{ + PolicyRequest: ram.PolicyRequest{ + PolicyName: d.Get("policy_name").(string), + PolicyType: ram.Type(d.Get("policy_type").(string)), + }, + UserName: d.Get("user_name").(string), + } + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + if _, err := conn.DetachPolicyFromUser(args); err != nil { + if RamEntityNotExist(err) { + return nil + } + return resource.NonRetryableError(fmt.Errorf("Error deleting user policy attachment: %#v", err)) + } + + response, err := conn.ListPoliciesForUser(ram.UserQueryRequest{UserName: args.UserName}) + if err != nil { + if RamEntityNotExist(err) { + return nil + } + + return resource.NonRetryableError(err) + } + + if len(response.Policies.Policy) < 1 { + return nil + } + return resource.RetryableError(fmt.Errorf("Error deleting user policy attachment - trying again while it is deleted.")) + }) +} diff --git a/alicloud/resource_alicloud_ram_user_policy_attachment_test.go b/alicloud/resource_alicloud_ram_user_policy_attachment_test.go new file mode 100644 index 00000000000..d2212d0f609 --- /dev/null +++ b/alicloud/resource_alicloud_ram_user_policy_attachment_test.go @@ -0,0 +1,140 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ram" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAlicloudRamUserPolicyAttachment_basic(t *testing.T) { + var p ram.Policy + var u ram.User + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_ram_user_policy_attachment.attach", + + Providers: testAccProviders, + CheckDestroy: testAccCheckRamUserPolicyAttachmentDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRamUserPolicyAttachmentConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRamPolicyExists( + "alicloud_ram_policy.policy", &p), + testAccCheckRamUserExists( + "alicloud_ram_user.user", &u), + testAccCheckRamUserPolicyAttachmentExists( + "alicloud_ram_user_policy_attachment.attach", &p, &u), + ), + }, + }, + }) + +} + +func testAccCheckRamUserPolicyAttachmentExists(n string, policy *ram.Policy, user *ram.User) 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 Attachment ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ramconn + + request := ram.UserQueryRequest{ + UserName: user.UserName, + } + + response, err := conn.ListPoliciesForUser(request) + if err == nil { + if len(response.Policies.Policy) > 0 { + for _, v := range response.Policies.Policy { + if v.PolicyName == policy.PolicyName && v.PolicyType == policy.PolicyType { + return nil + } + } + } + return fmt.Errorf("Error finding attach %s", rs.Primary.ID) + } + return fmt.Errorf("Error finding attach %s: %#v", rs.Primary.ID, err) + } +} + +func testAccCheckRamUserPolicyAttachmentDestroy(s *terraform.State) error { + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_ram_user_policy_attachment" { + continue + } + + // Try to find the attachment + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ramconn + + request := ram.UserQueryRequest{ + UserName: rs.Primary.Attributes["user_name"], + } + + response, err := conn.ListPoliciesForUser(request) + + if err != nil { + if RamEntityNotExist(err) { + return nil + } + return err + } + + if len(response.Policies.Policy) > 0 { + for _, v := range response.Policies.Policy { + if v.PolicyName == rs.Primary.Attributes["policy_name"] && v.PolicyType == rs.Primary.Attributes["policy_type"] { + return fmt.Errorf("Error attachment still exist.") + } + } + } + } + return nil +} + +const testAccRamUserPolicyAttachmentConfig = ` +resource "alicloud_ram_policy" "policy" { + name = "policyname" + statement = [ + { + effect = "Deny" + action = [ + "oss:ListObjects", + "oss:ListObjects"] + resource = [ + "acs:oss:*:*:mybucket", + "acs:oss:*:*:mybucket/*"] + }] + description = "this is a policy test" + force = true +} + +resource "alicloud_ram_user" "user" { + name = "username" + display_name = "displayname" + mobile = "86-18888888888" + email = "hello.uuu@aaa.com" + comments = "yoyoyo" +} + +resource "alicloud_ram_user_policy_attachment" "attach" { + policy_name = "${alicloud_ram_policy.policy.name}" + user_name = "${alicloud_ram_user.user.name}" + policy_type = "${alicloud_ram_policy.policy.type}" +}` diff --git a/alicloud/resource_alicloud_ram_user_test.go b/alicloud/resource_alicloud_ram_user_test.go new file mode 100644 index 00000000000..50de98f265d --- /dev/null +++ b/alicloud/resource_alicloud_ram_user_test.go @@ -0,0 +1,114 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ram" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" +) + +func TestAccAlicloudRamUser_basic(t *testing.T) { + var v ram.User + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_ram_user.user", + + Providers: testAccProviders, + CheckDestroy: testAccCheckRamUserDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRamUserConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRamUserExists( + "alicloud_ram_user.user", &v), + resource.TestCheckResourceAttr( + "alicloud_ram_user.user", + "name", + "username"), + resource.TestCheckResourceAttr( + "alicloud_ram_user.user", + "display_name", + "displayname"), + resource.TestCheckResourceAttr( + "alicloud_ram_user.user", + "comments", + "yoyoyo"), + ), + }, + }, + }) + +} + +func testAccCheckRamUserExists(n string, user *ram.User) 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 User ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ramconn + + request := ram.UserQueryRequest{ + UserName: rs.Primary.Attributes["user_name"], + } + + response, err := conn.GetUser(request) + log.Printf("[WARN] User id %#v", rs.Primary.ID) + + if err == nil { + *user = response.User + return nil + } + return fmt.Errorf("Error finding user %#v", rs.Primary.ID) + } +} + +func testAccCheckRamUserDestroy(s *terraform.State) error { + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_ram_user" { + continue + } + + // Try to find the user + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ramconn + + request := ram.UserQueryRequest{ + UserName: rs.Primary.Attributes["user_name"], + } + + _, err := conn.GetUser(request) + + if err != nil { + if RamEntityNotExist(err) { + return nil + } + return err + } + } + return nil +} + +const testAccRamUserConfig = ` +resource "alicloud_ram_user" "user" { + name = "username" + display_name = "displayname" + mobile = "86-18888888888" + email = "hello.uuu@aaa.com" + comments = "yoyoyo" +}` diff --git a/alicloud/resource_alicloud_security_group_rule.go b/alicloud/resource_alicloud_security_group_rule.go index f728b465350..a8b2f32aff1 100644 --- a/alicloud/resource_alicloud_security_group_rule.go +++ b/alicloud/resource_alicloud_security_group_rule.go @@ -5,6 +5,7 @@ import ( "github.com/denverdino/aliyungo/ecs" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" + "strconv" "strings" "time" ) @@ -43,6 +44,7 @@ func resourceAliyunSecurityGroupRule() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, + Default: GroupRulePolicyAccept, ValidateFunc: validateSecurityRulePolicy, }, @@ -56,6 +58,7 @@ func resourceAliyunSecurityGroupRule() *schema.Resource { Type: schema.TypeInt, Optional: true, ForceNew: true, + Default: 1, ValidateFunc: validateSecurityPriority, }, @@ -96,6 +99,8 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac ptl := d.Get("ip_protocol").(string) port := d.Get("port_range").(string) nicType := d.Get("nic_type").(string) + policy := d.Get("policy").(string) + priority := d.Get("priority").(int) if _, ok := d.GetOk("cidr_ip"); !ok { if _, ok := d.GetOk("source_security_group_id"); !ok { @@ -104,14 +109,14 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac } var autherr error - switch GroupRuleDirection(direction) { - case GroupRuleIngress: + switch ecs.Direction(direction) { + case ecs.DirectionIngress: args, err := buildAliyunSecurityIngressArgs(d, meta) if err != nil { return err } autherr = conn.AuthorizeSecurityGroup(args) - case GroupRuleEgress: + case ecs.DirectionEgress: args, err := buildAliyunSecurityEgressArgs(d, meta) if err != nil { return err @@ -133,7 +138,7 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac } else { cidr_ip = d.Get("source_security_group_id").(string) } - d.SetId(sgId + ":" + direction + ":" + ptl + ":" + port + ":" + nicType + ":" + cidr_ip) + d.SetId(sgId + ":" + direction + ":" + ptl + ":" + port + ":" + nicType + ":" + cidr_ip + ":" + policy + ":" + strconv.Itoa(priority)) return resourceAliyunSecurityGroupRuleRead(d, meta) } @@ -141,14 +146,24 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*AliyunClient) parts := strings.Split(d.Id(), ":") + policy := parseSecurityRuleId(d, meta, 6) + strPriority := parseSecurityRuleId(d, meta, 7) + var priority int + if policy == "" || strPriority == "" { + policy = d.Get("policy").(string) + priority = d.Get("priority").(int) + d.SetId(d.Id() + ":" + policy + ":" + strconv.Itoa(priority)) + } else { + prior, err := strconv.Atoi(strPriority) + if err != nil { + return fmt.Errorf("SecrityGroupRuleRead parse rule id gets an error: %#v", err) + } + priority = prior + } sgId := parts[0] direction := parts[1] - ip_protocol := parts[2] - port_range := parts[3] - nic_type := parts[4] - cidr_ip := parts[5] - rules, err := client.DescribeSecurityByAttr(sgId, direction, nic_type) + rule, err := client.DescribeSecurityGroupRule(sgId, direction, parts[2], parts[3], parts[4], parts[5], policy, priority) if err != nil { if NotFoundError(err) { d.SetId("") @@ -157,26 +172,6 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("Error SecurityGroup rule: %#v", err) } - // Filter security group rule according to its attribute - var rule ecs.PermissionType - for _, ru := range rules.Permissions.Permission { - if strings.ToLower(string(ru.IpProtocol)) == ip_protocol && ru.PortRange == port_range { - cidr := ru.SourceCidrIp - if GroupRuleDirection(direction) == GroupRuleIngress && cidr == "" { - cidr = ru.SourceGroupId - } - if GroupRuleDirection(direction) == GroupRuleEgress { - if cidr = ru.DestCidrIp; cidr == "" { - cidr = ru.DestGroupId - } - } - if cidr == cidr_ip { - rule = ru - break - } - } - } - d.Set("type", rule.Direction) d.Set("ip_protocol", strings.ToLower(string(rule.IpProtocol))) d.Set("nic_type", rule.NicType) @@ -185,7 +180,7 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{ d.Set("priority", rule.Priority) d.Set("security_group_id", sgId) //support source and desc by type - if GroupRuleDirection(direction) == GroupRuleIngress { + if ecs.Direction(direction) == ecs.DirectionIngress { d.Set("cidr_ip", rule.SourceCidrIp) d.Set("source_security_group_id", rule.SourceGroupId) d.Set("source_group_owner_account", rule.SourceGroupOwnerAccount) @@ -194,7 +189,6 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{ d.Set("source_security_group_id", rule.DestGroupId) d.Set("source_group_owner_account", rule.DestGroupOwnerAccount) } - return nil } @@ -202,7 +196,7 @@ func deleteSecurityGroupRule(d *schema.ResourceData, meta interface{}) error { client := meta.(*AliyunClient) ruleType := d.Get("type").(string) - if GroupRuleDirection(ruleType) == GroupRuleIngress { + if ecs.Direction(ruleType) == ecs.DirectionIngress { args, err := buildAliyunSecurityIngressArgs(d, meta) if err != nil { return err @@ -227,7 +221,20 @@ func deleteSecurityGroupRule(d *schema.ResourceData, meta interface{}) error { func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*AliyunClient) parts := strings.Split(d.Id(), ":") - sgId, direction, ip_protocol, port_range, nic_type := parts[0], parts[1], parts[2], parts[3], parts[4] + policy := parseSecurityRuleId(d, meta, 6) + strPriority := parseSecurityRuleId(d, meta, 7) + var priority int + if policy == "" || strPriority == "" { + policy = d.Get("policy").(string) + priority = d.Get("priority").(int) + d.SetId(d.Id() + ":" + policy + ":" + strconv.Itoa(priority)) + } else { + prior, err := strconv.Atoi(strPriority) + if err != nil { + return fmt.Errorf("SecrityGroupRuleRead parse rule id gets an error: %#v", err) + } + priority = prior + } return resource.Retry(5*time.Minute, func() *resource.RetryError { err := deleteSecurityGroupRule(d, meta) @@ -236,7 +243,7 @@ func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interfac resource.RetryableError(fmt.Errorf("Security group rule in use - trying again while it is deleted.")) } - _, err = client.DescribeSecurityGroupRule(sgId, direction, nic_type, ip_protocol, port_range) + _, err = client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], policy, priority) if err != nil { if NotFoundError(err) { return nil @@ -371,3 +378,14 @@ func buildAliyunSecurityEgressArgs(d *schema.ResourceData, meta interface{}) (*e return args, nil } + +func parseSecurityRuleId(d *schema.ResourceData, meta interface{}, index int) (result string) { + parts := strings.Split(d.Id(), ":") + defer func() { + if e := recover(); e != nil { + fmt.Printf("Panicing %s\r\n", e) + result = "" + } + }() + return parts[index] +} diff --git a/alicloud/resource_alicloud_security_group_rule_test.go b/alicloud/resource_alicloud_security_group_rule_test.go index 2b3f965e3bf..231086d0262 100644 --- a/alicloud/resource_alicloud_security_group_rule_test.go +++ b/alicloud/resource_alicloud_security_group_rule_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/terraform" "log" "regexp" + "strconv" "strings" "testing" ) @@ -270,6 +271,45 @@ func TestAccAlicloudSecurityGroupRule_Multi(t *testing.T) { } +func TestAccAlicloudSecurityGroupRule_MultiAttri(t *testing.T) { + var pt ecs.PermissionType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group_rule.ingress_allow_tcp_22_sg", + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupRuleMultiAttri, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress_allow_tcp_22_sg", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress_allow_tcp_22_sg", "port_range", "22/22"), + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress_allow_tcp_22", &pt), + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress_deny_tcp_22", &pt), + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress_allow_tcp_22_prior", &pt), + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress_deny_tcp_22_prior", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress_deny_tcp_22_prior", + "port_range", + "22/22"), + ), + }, + }, + }) + +} + func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -284,8 +324,11 @@ func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resour client := testAccProvider.Meta().(*AliyunClient) log.Printf("[WARN]get sg rule %s", rs.Primary.ID) parts := strings.Split(rs.Primary.ID, ":") - // securityGroupId, direction, nicType, ipProtocol, portRange - rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[4], parts[2], parts[3]) + prior, err := strconv.Atoi(parts[7]) + if err != nil { + return fmt.Errorf("testSecrityGroupRuleExists parse rule id gets an error: %#v", err) + } + rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], prior) if err != nil { return err @@ -309,7 +352,11 @@ func testAccCheckSecurityGroupRuleDestroy(s *terraform.State) error { } parts := strings.Split(rs.Primary.ID, ":") - rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[4], parts[2], parts[3]) + prior, err := strconv.Atoi(parts[7]) + if err != nil { + return fmt.Errorf("testSecrityGroupRuleDestroy parse rule id gets an error: %#v", err) + } + rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], prior) if rule != nil { return fmt.Errorf("Error SecurityGroup Rule still exist") @@ -497,3 +544,83 @@ resource "alicloud_security_group_rule" "ingress" { source_security_group_id = "${alicloud_security_group.foo.id}" } ` +const testAccSecurityGroupRuleMultiAttri = ` +variable "name" { + default = "test_ssh" +} + +variable "source_cidr_blocks" { + type = "list" + default = ["0.0.0.0/0"] +} + + +resource "alicloud_vpc" "vpc" { + name = "test-ssh" + cidr_block = "172.16.0.0/24" +} + +resource "alicloud_security_group" "main" { + name = "${var.name}" + vpc_id = "${alicloud_vpc.vpc.id}" +} +resource "alicloud_security_group" "source" { + name = "${var.name}-2" + vpc_id = "${alicloud_vpc.vpc.id}" +} + +resource "alicloud_security_group_rule" "ingress_allow_tcp_22_sg" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.main.id}" + source_security_group_id = "${alicloud_security_group.source.id}" +} + +resource "alicloud_security_group_rule" "ingress_allow_tcp_22" { + count = "${length(var.source_cidr_blocks)}" + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.main.id}" + cidr_ip = "${element(var.source_cidr_blocks, count.index)}" +} + +resource "alicloud_security_group_rule" "ingress_deny_tcp_22" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "drop" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.main.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "ingress_allow_tcp_22_prior" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "22/22" + priority = 100 + security_group_id = "${alicloud_security_group.main.id}" + cidr_ip = "0.0.0.0/0" +} +resource "alicloud_security_group_rule" "ingress_deny_tcp_22_prior" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "drop" + port_range = "22/22" + priority = 100 + security_group_id = "${alicloud_security_group.main.id}" + cidr_ip = "0.0.0.0/0" +} +` diff --git a/alicloud/service_alicloud_ecs.go b/alicloud/service_alicloud_ecs.go index 8ee0bb0bfca..850bf3a6bc8 100644 --- a/alicloud/service_alicloud_ecs.go +++ b/alicloud/service_alicloud_ecs.go @@ -6,6 +6,7 @@ import ( "github.com/denverdino/aliyungo/common" "github.com/denverdino/aliyungo/ecs" "github.com/hashicorp/terraform/helper/schema" + "log" "strings" ) @@ -222,29 +223,36 @@ func (client *AliyunClient) DescribeSecurity(securityGroupId string) (*ecs.Descr return client.ecsconn.DescribeSecurityGroupAttribute(args) } -func (client *AliyunClient) DescribeSecurityByAttr(securityGroupId, direction, nicType string) (*ecs.DescribeSecurityGroupAttributeResponse, error) { - - args := &ecs.DescribeSecurityGroupAttributeArgs{ +func (client *AliyunClient) DescribeSecurityGroupRule(groupId, direction, ipProtocol, portRange, nicType, cidr_ip, policy string, priority int) (*ecs.PermissionType, error) { + rules, err := client.ecsconn.DescribeSecurityGroupAttribute(&ecs.DescribeSecurityGroupAttributeArgs{ RegionId: client.Region, - SecurityGroupId: securityGroupId, - Direction: direction, + SecurityGroupId: groupId, + Direction: ecs.Direction(direction), NicType: ecs.NicType(nicType), - } - - return client.ecsconn.DescribeSecurityGroupAttribute(args) -} + }) -func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, direction, nicType, ipProtocol, portRange string) (*ecs.PermissionType, error) { - sg, err := client.DescribeSecurityByAttr(securityGroupId, direction, nicType) if err != nil { return nil, err } - for _, p := range sg.Permissions.Permission { - if strings.ToLower(string(p.IpProtocol)) == ipProtocol && p.PortRange == portRange { - return &p, nil + for _, ru := range rules.Permissions.Permission { + if strings.ToLower(string(ru.IpProtocol)) == ipProtocol && ru.PortRange == portRange { + cidr := ru.SourceCidrIp + if ecs.Direction(direction) == ecs.DirectionIngress && cidr == "" { + cidr = ru.SourceGroupId + } + if ecs.Direction(direction) == ecs.DirectionEgress { + if cidr = ru.DestCidrIp; cidr == "" { + cidr = ru.DestGroupId + } + } + + if cidr == cidr_ip && strings.ToLower(string(ru.Policy)) == policy && ru.Priority == priority { + return &ru, nil + } } } + return nil, GetNotFoundErrorFromString("Security group rule not found") } @@ -295,14 +303,70 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta // Retrieve series instance type family ioOptimized := ecs.IoOptimizedOptimized var expectedFamilies []string - outdatedFamiliesMap, expectedFamiliesMap, err := client.FetchSpecifiedInstanceTypeFamily(getRegion(d, meta), zoneId, []string{GenerationOne, GenerationTwo}, zones) + mapOutdatedInstanceFamilies, mapUpgradedInstanceFamilies, err := client.FetchSpecifiedInstanceTypeFamily(getRegion(d, meta), zoneId, []string{GenerationOne, GenerationTwo}, zones) if err != nil { return nil, err } - for key := range expectedFamiliesMap { + for key := range mapUpgradedInstanceFamilies { expectedFamilies = append(expectedFamilies, key) } + validData := make(map[ResourceKeyType]interface{}) + mapZones := make(map[string]ecs.ZoneType) + mapSupportedInstanceTypes := make(map[string]string) + mapUpgradedInstanceTypes := make(map[string]string) + mapOutdatedInstanceTypes := make(map[string]string) + mapOutdatedDiskCategories := make(map[ecs.DiskCategory]ecs.DiskCategory) + mapDiskCategories := make(map[ecs.DiskCategory]ecs.DiskCategory) + for _, zone := range zones { + //Filter and get all instance types in the zones + for _, insType := range zone.AvailableInstanceTypes.InstanceTypes { + if _, ok := mapSupportedInstanceTypes[insType]; !ok { + insTypeSplit := strings.Split(insType, DOT_SEPARATED) + mapSupportedInstanceTypes[insType] = string(insTypeSplit[0] + DOT_SEPARATED + insTypeSplit[1]) + } + } + if len(zone.AvailableDiskCategories.DiskCategories) < 1 { + continue + } + //Filter and get all instance types in the zones + for _, category := range zone.AvailableDiskCategories.DiskCategories { + if _, ok := SupportedDiskCategory[category]; ok { + mapDiskCategories[category] = category + } + if _, ok := OutdatedDiskCategory[category]; ok { + mapOutdatedDiskCategories[category] = category + } + } + resources := zone.AvailableResources.ResourcesInfo + if len(resources) < 1 { + continue + } + mapZones[zone.ZoneId] = zone + } + //separate all instance types according generation 3 in the zones + for key, _ := range mapSupportedInstanceTypes { + find := false + for out, _ := range mapOutdatedInstanceFamilies { + if strings.HasPrefix(key, out) { + mapOutdatedInstanceTypes[key] = out + mapSupportedInstanceTypes[key] = out + find = true + break + } + } + if find { + continue + } + for upgrade, _ := range mapUpgradedInstanceFamilies { + if strings.HasPrefix(key, upgrade) { + mapUpgradedInstanceTypes[key] = upgrade + mapSupportedInstanceTypes[key] = upgrade + break + } + } + } + var instanceType string if insType, ok := d.GetOk("available_instance_type"); ok { instanceType = insType.(string) @@ -310,35 +374,36 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta instanceType = insType.(string) } if instanceType != "" { - - instanceTypeSplit := strings.Split(instanceType, DOT_SEPARATED) - prefix := string(instanceTypeSplit[0] + DOT_SEPARATED + instanceTypeSplit[1]) + if !strings.HasPrefix(instanceType, "ecs.") { + return nil, fmt.Errorf("Invalid instance_type: %s. Please modify it and try again.", instanceType) + } var instanceTypeObject ecs.InstanceTypeItemType - _, outdatedOk := outdatedFamiliesMap[prefix] - _, expectedOk := expectedFamiliesMap[prefix] - if outdatedOk || expectedOk { - mapInstanceTypes, err := client.FetchSpecifiedInstanceTypesByFamily(zoneId, prefix, zones) + targetFamily, ok := mapSupportedInstanceTypes[instanceType] + if ok { + mapInstanceTypes, err := client.FetchSpecifiedInstanceTypesByFamily(zoneId, targetFamily, zones) if err != nil { return nil, err } - if value, ok := mapInstanceTypes[instanceType]; ok { - instanceTypeObject = value - } + var validInstanceTypes []string for key, value := range mapInstanceTypes { if instanceType == key { instanceTypeObject = mapInstanceTypes[key] break } - validInstanceTypes = append(validInstanceTypes, fmt.Sprintf("%s(%dCPU,%.0fGB)", key, value.CpuCoreCount, value.MemorySize)) + core := "Core" + if value.CpuCoreCount > 1 { + core = "Cores" + } + validInstanceTypes = append(validInstanceTypes, fmt.Sprintf("%s(%d%s,%.0fGB)", key, value.CpuCoreCount, core, value.MemorySize)) } if instanceTypeObject.InstanceTypeId == "" { if zoneId == "" { return nil, fmt.Errorf("Instance type %s is not supported in the region %s. Expected instance types of family %s: %s.", - instanceType, getRegion(d, meta), prefix, strings.Join(validInstanceTypes, ", ")) + instanceType, getRegion(d, meta), targetFamily, strings.Join(validInstanceTypes, ", ")) } return nil, fmt.Errorf("Instance type %s is not supported in the availability zone %s. Expected instance types of family %s: %s.", - instanceType, zoneId, prefix, strings.Join(validInstanceTypes, ", ")) + instanceType, zoneId, targetFamily, strings.Join(validInstanceTypes, ", ")) } outDisk := false @@ -346,6 +411,9 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta _, outDisk = OutdatedDiskCategory[ecs.DiskCategory(disk.(string))] } + _, outdatedOk := mapOutdatedInstanceTypes[instanceType] + _, expectedOk := mapUpgradedInstanceTypes[instanceType] + if expectedOk && outDisk { return nil, fmt.Errorf("Instance type %s can't support 'cloud' as instance system disk. "+ "Please change your disk category to efficient disk '%s' or '%s'.", instanceType, ecs.DiskCategoryCloudSSD, ecs.DiskCategoryCloudEfficiency) @@ -353,16 +421,20 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta if outdatedOk { var expectedEqualCpus []string var expectedEqualMoreCpus []string - for _, fam := range expectedFamilies { + for _, fam := range mapUpgradedInstanceTypes { mapInstanceTypes, err := client.FetchSpecifiedInstanceTypesByFamily(zoneId, fam, zones) if err != nil { return nil, err } for _, value := range mapInstanceTypes { + core := "Core" + if value.CpuCoreCount > 1 { + core = "Cores" + } if instanceTypeObject.CpuCoreCount == value.CpuCoreCount { - expectedEqualCpus = append(expectedEqualCpus, fmt.Sprintf("%s(%dCPU,%.0fGB)", value.InstanceTypeId, value.CpuCoreCount, value.MemorySize)) + expectedEqualCpus = append(expectedEqualCpus, fmt.Sprintf("%s(%d%s,%.0fGB)", value.InstanceTypeId, value.CpuCoreCount, core, value.MemorySize)) } else if instanceTypeObject.CpuCoreCount*2 == value.CpuCoreCount { - expectedEqualMoreCpus = append(expectedEqualMoreCpus, fmt.Sprintf("%s(%dCPU,%.0fGB)", value.InstanceTypeId, value.CpuCoreCount, value.MemorySize)) + expectedEqualMoreCpus = append(expectedEqualMoreCpus, fmt.Sprintf("%s(%d%s,%.0fGB)", value.InstanceTypeId, value.CpuCoreCount, core, value.MemorySize)) } } } @@ -372,13 +444,17 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta } if out, ok := d.GetOk("is_outdated"); !(ok && out.(bool)) { - return nil, fmt.Errorf("The current instance type %s(%dCPU,%.0fGB) has been outdated. Expect to use the upgraded instance types: %s. You can keep the instance type %s by setting 'is_outdated' to true.", - instanceType, instanceTypeObject.CpuCoreCount, instanceTypeObject.MemorySize, strings.Join(expectedInstanceTypes, ", "), instanceType) + core := "Core" + if instanceTypeObject.CpuCoreCount > 1 { + core = "Cores" + } + return nil, fmt.Errorf("The current instance type %s(%d%s,%.0fGB) has been outdated. Expect to use the upgraded instance types: %s. You can keep the instance type %s by setting 'is_outdated' to true.", + instanceType, instanceTypeObject.CpuCoreCount, core, instanceTypeObject.MemorySize, strings.Join(expectedInstanceTypes, ", "), instanceType) } else { // Check none io optimized and cloud _, typeOk := NoneIoOptimizedInstanceType[instanceType] - _, famOk := NoneIoOptimizedFamily[prefix] - _, halfOk := HalfIoOptimizedFamily[prefix] + _, famOk := NoneIoOptimizedFamily[targetFamily] + _, halfOk := HalfIoOptimizedFamily[targetFamily] if typeOk || famOk { if outDisk { ioOptimized = ecs.IoOptimizedNone @@ -397,93 +473,32 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta } } - } else { - var validFamilies []string - for key := range expectedFamiliesMap { - validFamilies = append(validFamilies, key) - } - - if len(validFamilies) < 1 { - return nil, fmt.Errorf("There is no available instance type in the current availability zone." + - "Please change availability zone or region and try again.") - } - if zoneId == "" { - return nil, fmt.Errorf("Instance type family %s is not supported in the region %s. Expected instance type families: %s.", - prefix, getRegion(d, meta), strings.Join(validFamilies, ", ")) - } - return nil, fmt.Errorf("Instance type family %s is not supported in the availability zone %s. Expected instance type families: %s.", - prefix, zoneId, strings.Join(validFamilies, ", ")) + } else if err := getExpectInstanceTypesAndFormatOut(zoneId, targetFamily, getRegion(d, meta), mapUpgradedInstanceFamilies); err != nil { + return nil, err } } if instanceTypeFamily, ok := d.GetOk("instance_type_family"); ok { - _, outdatedOk := outdatedFamiliesMap[instanceTypeFamily.(string)] - _, expectedOk := expectedFamiliesMap[instanceTypeFamily.(string)] - if outdatedOk || !expectedOk { - var validFamilies []string - for key := range expectedFamiliesMap { - validFamilies = append(validFamilies, key) - } - if len(validFamilies) < 1 { - return nil, fmt.Errorf("There is no available instance type family in the current availability zone." + - "Please change availability zone or region and try again.") - } - if zoneId == "" { - return nil, fmt.Errorf("Instance type family %s is not supported in the region %s. Expected instance type families: %s.", - instanceTypeFamily, getRegion(d, meta), strings.Join(validFamilies, ", ")) - } - return nil, fmt.Errorf("Instance type family %s is not supported in the availability zone %s. Expected instance type families: %s.", - instanceTypeFamily, zoneId, strings.Join(validFamilies, ", ")) + if !strings.HasPrefix(instanceTypeFamily.(string), "ecs.") { + return nil, fmt.Errorf("Invalid instance_type_family: %s. Please modify it and try again.", instanceTypeFamily.(string)) } - } - validData := make(map[ResourceKeyType]interface{}) - mapZones := make(map[string]ecs.ZoneType) - mapSupportedInstanceTypes := make(map[string]string) - mapUpgradedInstanceTypes := make(map[string]string) - mapOutdatedInstanceTypes := make(map[string]string) - mapOutdatedDiskCategories := make(map[ecs.DiskCategory]ecs.DiskCategory) - mapDiskCategories := make(map[ecs.DiskCategory]ecs.DiskCategory) - for _, zone := range zones { - //var validInstanceTypes []string - for _, insType := range zone.AvailableInstanceTypes.InstanceTypes { - insTypeSplit := strings.Split(insType, DOT_SEPARATED) - prefix := string(insTypeSplit[0] + DOT_SEPARATED + insTypeSplit[1]) - if _, ok := outdatedFamiliesMap[prefix]; ok { - mapOutdatedInstanceTypes[insType] = prefix - } - if _, ok := expectedFamiliesMap[prefix]; ok { - mapUpgradedInstanceTypes[insType] = prefix - } - mapSupportedInstanceTypes[insType] = prefix - } - if len(zone.AvailableDiskCategories.DiskCategories) < 1 { - continue - } - //var validDiskCategories []ecs.DiskCategory - for _, category := range zone.AvailableDiskCategories.DiskCategories { - if _, ok := SupportedDiskCategory[category]; ok { - mapDiskCategories[category] = category - } - if _, ok := OutdatedDiskCategory[category]; ok { - mapOutdatedDiskCategories[category] = category + _, outdatedOk := mapOutdatedInstanceFamilies[instanceTypeFamily.(string)] + _, expectedOk := mapUpgradedInstanceFamilies[instanceTypeFamily.(string)] + if outdatedOk || !expectedOk { + if err := getExpectInstanceTypesAndFormatOut(zoneId, instanceTypeFamily.(string), getRegion(d, meta), mapUpgradedInstanceFamilies); err != nil { + return nil, err } } - resources := zone.AvailableResources.ResourcesInfo - if len(resources) < 1 { - continue - } - mapZones[zone.ZoneId] = zone - } validData[ZoneKey] = mapZones validData[InstanceTypeKey] = mapSupportedInstanceTypes validData[UpgradedInstanceTypeKey] = mapUpgradedInstanceTypes validData[OutdatedInstanceTypeKey] = mapOutdatedInstanceTypes - validData[UpgradedInstanceTypeFamilyKey] = expectedFamiliesMap - validData[OutdatedInstanceTypeFamilyKey] = outdatedFamiliesMap + validData[UpgradedInstanceTypeFamilyKey] = mapUpgradedInstanceFamilies + validData[OutdatedInstanceTypeFamilyKey] = mapOutdatedInstanceFamilies validData[OutdatedDiskCategoryKey] = mapOutdatedDiskCategories validData[DiskCategoryKey] = mapDiskCategories validData[IoOptimizedKey] = ioOptimized @@ -493,14 +508,16 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta func (client *AliyunClient) FetchSpecifiedInstanceTypeFamily(regionId common.Region, zoneId string, generations []string, all_zones []ecs.ZoneType) (map[string]ecs.InstanceTypeFamily, map[string]ecs.InstanceTypeFamily, error) { // Describe specified series instance type families - outdatedFamiliesMap := make(map[string]ecs.InstanceTypeFamily) - upgradedFamiliesMap := make(map[string]ecs.InstanceTypeFamily) + mapOutdatedInstanceFamilies := make(map[string]ecs.InstanceTypeFamily) + mapUpgradedInstanceFamilies := make(map[string]ecs.InstanceTypeFamily) response, err := client.ecsconn.DescribeInstanceTypeFamilies(&ecs.DescribeInstanceTypeFamiliesArgs{ RegionId: regionId, }) if err != nil { return nil, nil, fmt.Errorf("Error DescribeInstanceTypeFamilies: %#v.", err) } + log.Printf("All the instance families in the region %s: %#v", regionId, response) + tempOutdatedMap := make(map[string]string) for _, gen := range generations { tempOutdatedMap[gen] = gen @@ -508,10 +525,10 @@ func (client *AliyunClient) FetchSpecifiedInstanceTypeFamily(regionId common.Reg for _, family := range response.InstanceTypeFamilies.InstanceTypeFamily { if _, ok := tempOutdatedMap[family.Generation]; ok { - outdatedFamiliesMap[family.InstanceTypeFamilyId] = family + mapOutdatedInstanceFamilies[family.InstanceTypeFamilyId] = family continue } - upgradedFamiliesMap[family.InstanceTypeFamilyId] = family + mapUpgradedInstanceFamilies[family.InstanceTypeFamilyId] = family } // Filter specified zone's instance type families, and make them fit for specified generation @@ -523,10 +540,10 @@ func (client *AliyunClient) FetchSpecifiedInstanceTypeFamily(regionId common.Reg for _, resource := range zone.AvailableResources.ResourcesInfo { families := resource.InstanceTypeFamilies[ecs.SupportedInstanceTypeFamily] for _, familyId := range families { - if val, ok := outdatedFamiliesMap[familyId]; ok { + if val, ok := mapOutdatedInstanceFamilies[familyId]; ok { outdatedValidFamilies[familyId] = val } - if val, ok := upgradedFamiliesMap[familyId]; ok { + if val, ok := mapUpgradedInstanceFamilies[familyId]; ok { upgradedValidFamilies[familyId] = val } } @@ -536,7 +553,9 @@ func (client *AliyunClient) FetchSpecifiedInstanceTypeFamily(regionId common.Reg } } } - return outdatedFamiliesMap, upgradedFamiliesMap, nil + log.Printf("New generation instance families: %#v.\n Outdated instance families: %#v.", + mapUpgradedInstanceFamilies, mapOutdatedInstanceFamilies) + return mapOutdatedInstanceFamilies, mapUpgradedInstanceFamilies, nil } func (client *AliyunClient) FetchSpecifiedInstanceTypesByFamily(zoneId, instanceTypeFamily string, all_zones []ecs.ZoneType) (map[string]ecs.InstanceTypeItemType, error) { @@ -547,6 +566,7 @@ func (client *AliyunClient) FetchSpecifiedInstanceTypesByFamily(zoneId, instance if err != nil { return nil, fmt.Errorf("Error DescribeInstanceTypes: %#v.", err) } + log.Printf("All the instance types of family %s: %#v", instanceTypeFamily, types) instanceTypes := make(map[string]ecs.InstanceTypeItemType) for _, ty := range types { instanceTypes[ty.InstanceTypeId] = ty @@ -573,6 +593,24 @@ func (client *AliyunClient) FetchSpecifiedInstanceTypesByFamily(zoneId, instance return instanceTypes, nil } +func getExpectInstanceTypesAndFormatOut(zoneId, instanceTypeFamily string, regionId common.Region, mapInstanceFamilies map[string]ecs.InstanceTypeFamily) error { + var validFamilies []string + + for key := range mapInstanceFamilies { + validFamilies = append(validFamilies, key) + } + if len(validFamilies) < 1 { + return fmt.Errorf("There is no available instance type family in the current availability zone." + + "Please change availability zone or region and try again.") + } + if zoneId == "" { + return fmt.Errorf("Instance type family %s is not supported in the region %s. Expected instance type families: %s.", + instanceTypeFamily, regionId, strings.Join(validFamilies, ", ")) + } + return fmt.Errorf("Instance type family %s is not supported in the availability zone %s. Expected instance type families: %s.", + instanceTypeFamily, zoneId, strings.Join(validFamilies, ", ")) +} + func (client *AliyunClient) QueryInstancesWithKeyPair(region common.Region, instanceIds, keypair string) ([]interface{}, []ecs.InstanceAttributesType, error) { var instance_ids []interface{} var instanceList []ecs.InstanceAttributesType diff --git a/alicloud/service_alicloud_ram.go b/alicloud/service_alicloud_ram.go new file mode 100644 index 00000000000..61ca80222c6 --- /dev/null +++ b/alicloud/service_alicloud_ram.go @@ -0,0 +1,180 @@ +package alicloud + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/denverdino/aliyungo/ram" +) + +type Effect string + +const ( + Allow Effect = "Allow" + Deny Effect = "Deny" +) + +type Principal struct { + Service []string + RAM []string +} + +type RolePolicyStatement struct { + Effect Effect + Action string + Principal Principal +} + +type RolePolicy struct { + Statement []RolePolicyStatement + Version string +} + +type PolicyStatement struct { + Effect Effect + Action interface{} + Resource interface{} +} + +type Policy struct { + Statement []PolicyStatement + Version string +} + +func ParseRolePolicyDocument(policyDocument string) (RolePolicy, error) { + var policy RolePolicy + err := json.Unmarshal([]byte(policyDocument), &policy) + if err != nil { + return RolePolicy{}, err + } + return policy, nil +} + +func ParsePolicyDocument(policyDocument string) (statement []map[string]interface{}, version string, err error) { + policy := Policy{} + err = json.Unmarshal([]byte(policyDocument), &policy) + if err != nil { + return + } + + version = policy.Version + statement = make([]map[string]interface{}, 0, len(policy.Statement)) + for _, v := range policy.Statement { + item := make(map[string]interface{}) + + item["effect"] = v.Effect + if val, ok := v.Action.([]interface{}); ok { + item["action"] = val + } else { + item["action"] = []interface{}{v.Action} + } + + if val, ok := v.Resource.([]interface{}); ok { + item["resource"] = val + } else { + item["resource"] = []interface{}{v.Resource} + } + statement = append(statement, item) + } + return +} + +func AssembleRolePolicyDocument(ramUser, service []interface{}, version string) (string, error) { + services := expandStringList(service) + users := expandStringList(ramUser) + + statement := RolePolicyStatement{ + Effect: Allow, + Action: "sts:AssumeRole", + Principal: Principal{ + RAM: users, + Service: services, + }, + } + + policy := RolePolicy{ + Version: version, + Statement: []RolePolicyStatement{statement}, + } + + data, err := json.Marshal(policy) + if err != nil { + return "", err + } + return string(data), nil +} + +func AssemblePolicyDocument(document []interface{}, version string) (string, error) { + var statements []PolicyStatement + + for _, v := range document { + doc := v.(map[string]interface{}) + + actions := expandStringList(doc["action"].([]interface{})) + resources := expandStringList(doc["resource"].([]interface{})) + + statement := PolicyStatement{ + Effect: Effect(doc["effect"].(string)), + Action: actions, + Resource: resources, + } + statements = append(statements, statement) + } + + policy := Policy{ + Version: version, + Statement: statements, + } + + data, err := json.Marshal(policy) + if err != nil { + return "", err + } + return string(data), nil +} + +// Judge whether the role policy contains service "ecs.aliyuncs.com" +func (client *AliyunClient) JudgeRolePolicyPrincipal(roleName string) error { + conn := client.ramconn + resp, err := conn.GetRole(ram.RoleQueryRequest{RoleName: roleName}) + if err != nil { + return fmt.Errorf("GetRole %s got an error: %#v", roleName, err) + } + + policy, err := ParseRolePolicyDocument(resp.Role.AssumeRolePolicyDocument) + if err != nil { + return err + } + for _, v := range policy.Statement { + for _, val := range v.Principal.Service { + if strings.Trim(val, " ") == "ecs.aliyuncs.com" { + return nil + } + } + } + return fmt.Errorf("Role policy services must contains 'ecs.aliyuncs.com', Now is \n%v.", resp.Role.AssumeRolePolicyDocument) +} + +func GetIntersection(dataMap []map[string]interface{}, allDataMap map[string]interface{}) (allData []interface{}) { + if len(dataMap) == 1 { + allDataMap = dataMap[0] + } else { + for _, v := range dataMap { + if len(v) > 0 { + for key := range allDataMap { + if _, ok := v[key]; !ok { + allDataMap[key] = nil + } + } + } + } + } + + for _, v := range allDataMap { + if v != nil { + allData = append(allData, v) + } + } + return +} diff --git a/alicloud/service_alicloud_rds.go b/alicloud/service_alicloud_rds.go index 931f1cb4518..b7b7d412053 100644 --- a/alicloud/service_alicloud_rds.go +++ b/alicloud/service_alicloud_rds.go @@ -141,13 +141,9 @@ func (client *AliyunClient) ConfigDBBackup(instanceId, backupTime, backupPeriod } func (client *AliyunClient) ModifyDBSecurityIps(instanceId, ips string) error { - sargs := rds.DBInstanceIPArray{ - SecurityIps: ips, - } - args := rds.ModifySecurityIpsArgs{ - DBInstanceId: instanceId, - DBInstanceIPArray: sargs, + DBInstanceId: instanceId, + SecurityIps: ips, } if _, err := client.rdsconn.ModifySecurityIps(&args); err != nil { diff --git a/alicloud/validators.go b/alicloud/validators.go index 52b323d41f5..913459ee823 100644 --- a/alicloud/validators.go +++ b/alicloud/validators.go @@ -10,8 +10,11 @@ import ( "time" "github.com/aliyun/aliyun-oss-go-sdk/oss" + "github.com/denverdino/aliyungo/cdn" "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/dns" "github.com/denverdino/aliyungo/ecs" + "github.com/denverdino/aliyungo/ram" "github.com/denverdino/aliyungo/slb" "github.com/hashicorp/terraform/helper/schema" ) @@ -126,9 +129,9 @@ func validateSecurityGroupDescription(v interface{}, k string) (ws []string, err } 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)) + rt := ecs.Direction(v.(string)) + if rt != ecs.DirectionIngress && rt != ecs.DirectionEgress { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, ecs.DirectionIngress, ecs.DirectionEgress)) } return @@ -374,9 +377,9 @@ func validateSlbListenerCookie(v interface{}, k string) (ws []string, errors []e func validateSlbListenerCookieTimeout(v interface{}, k string) (ws []string, errors []error) { value := v.(int) - if value < 0 || value > 86400 { + if value < 1 || value > 86400 { errors = append(errors, fmt.Errorf( - "%q must be a valid load balancer cookie timeout between 0 and 86400", + "%q must be a valid load balancer cookie timeout between 1 and 86400", k)) return } @@ -396,9 +399,11 @@ func validateSlbListenerPersistenceTimeout(v interface{}, k string) (ws []string func validateSlbListenerHealthCheckDomain(v interface{}, k string) (ws []string, errors []error) { if value := v.(string); value != "" { - //the len add "$_ip",so to max is 84 - if len(value) < 1 || len(value) > 84 { - errors = append(errors, fmt.Errorf("%q cannot be longer than 84 characters", k)) + if value == "$_ip" { + errors = append(errors, fmt.Errorf("%q value '$_ip' has been deprecated, and empty string will replace it.", k)) + } + if reg := regexp.MustCompile(`^[\w\-.]{1,80}$`); !reg.MatchString(value) { + errors = append(errors, fmt.Errorf("%q length is limited to 1-80 and only characters such as letters, digits, '-' and '.' are allowed", k)) } } return @@ -632,6 +637,84 @@ func validateOssBucketObjectServerSideEncryption(v interface{}, k string) (ws [] return } +func validateDomainName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if vp := strings.Split(value, "."); len(vp) > 1 { + mainDomain := strings.Join(vp[:len(vp)-1], ".") + if len(mainDomain) > 63 || len(mainDomain) < 1 { + errors = append(errors, fmt.Errorf("Main domain cannot be longer than 63 characters or less than 1 character")) + } + } + + if strings.HasSuffix(value, ".sh") || strings.HasSuffix(value, ".tel") { + errors = append(errors, fmt.Errorf("Domain ends with .sh or .tel is not supported.")) + } + + if strings.HasPrefix(value, "-") || strings.HasSuffix(value, "-") { + errors = append(errors, fmt.Errorf("Domain name is invalid, it can not starts or ends with '-'")) + } + return +} + +func validateDomainRecordType(v interface{}, k string) (ws []string, errors []error) { + // Valid Record types + // A, NS, MX, TXT, CNAME, SRV, AAAA, REDIRECT_URL, FORWORD_URL + validTypes := map[string]string{ + dns.ARecord: "", + dns.NSRecord: "", + dns.MXRecord: "", + dns.TXTRecord: "", + dns.CNAMERecord: "", + dns.SRVRecord: "", + dns.AAAARecord: "", + dns.RedirectURLRecord: "", + dns.ForwordURLRecord: "", + } + + value := v.(string) + if _, ok := validTypes[value]; !ok { + errors = append(errors, fmt.Errorf("%q must be one of [A, NS, MX, TXT, CNAME, SRV, AAAA, REDIRECT_URL, FORWORD_URL]", k)) + } + return +} + +func validateDomainRecordPriority(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value > 10 || value < 1 { + errors = append(errors, fmt.Errorf("%q value is 1-10.", k)) + } + return +} + +func validateRR(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if strings.HasPrefix(value, "-") || strings.HasSuffix(value, "-") { + errors = append(errors, fmt.Errorf("RR is invalid, it can not starts or ends with '-'")) + } + + if len(value) > 253 { + errors = append(errors, fmt.Errorf("RR can not longer than 253 characters.")) + } + + for _, part := range strings.Split(value, ".") { + if len(part) > 63 { + errors = append(errors, fmt.Errorf("Each part of RR split with . can not longer than 63 characters.")) + return + } + } + return +} + +func validateDomainRecordLine(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if value != "default" && value != "telecom" && value != "unicom" && value != "mobile" && value != "oversea" && value != "edu" { + errors = append(errors, fmt.Errorf("Record parsing line must be one of [default, telecom, unicom, mobile, oversea, edu].")) + } + return +} + func validateKeyPairName(v interface{}, k string) (ws []string, errors []error) { value := v.(string) if len(value) < 2 || len(value) > 128 { @@ -655,6 +738,62 @@ func validateKeyPairPrefix(v interface{}, k string) (ws []string, errors []error return } +func validateRamName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if len(value) > 64 { + errors = append(errors, fmt.Errorf("%q can not be longer than 64 characters.", k)) + } + + pattern := `^[a-zA-Z0-9\.@\-_]+$` + if match, _ := regexp.Match(pattern, []byte(value)); !match { + errors = append(errors, fmt.Errorf("%q is invalid.", k)) + } + return +} + +func validateRamDisplayName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + pattern := `^[a-zA-Z0-9\.@\-\p{Han}]{1,12}$` + if match, _ := regexp.Match(pattern, []byte(value)); !match { + errors = append(errors, fmt.Errorf("%q is invalid.", k)) + } + return +} + +func validateComment(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if len(value) > 128 { + errors = append(errors, fmt.Errorf("%q can not be longer than 128 characters.", k)) + } + return +} + +func validateRamDesc(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if len(value) > 1024 { + errors = append(errors, fmt.Errorf("%q can not be longer than 1024 characters.", k)) + } + return +} + +func validateRamPolicyName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if len(value) > 128 { + errors = append(errors, fmt.Errorf("%q can not be longer than 128 characters.", k)) + } + + pattern := `^[a-zA-Z0-9\-]+$` + if match, _ := regexp.Match(pattern, []byte(value)); !match { + errors = append(errors, fmt.Errorf("%q is invalid.", k)) + } + return +} + // Takes a value containing JSON string and passes it through // the JSON parser to normalize it, returns either a parsing // error or normalized JSON string. @@ -679,6 +818,214 @@ func normalizeJsonString(jsonString interface{}) (string, error) { return string(bytes[:]), nil } +func validateJsonString(v interface{}, k string) (ws []string, errors []error) { + if _, err := normalizeJsonString(v); err != nil { + errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) + } + if strings.Contains(v.(string), " ") || strings.Contains(v.(string), "\n") { + errors = append(errors, fmt.Errorf("%q can not contain any space or newline character.", k)) + } + return +} + +func validatePolicyType(v interface{}, k string) (ws []string, errors []error) { + value := ram.Type(v.(string)) + + if value != ram.System && value != ram.Custom { + errors = append(errors, fmt.Errorf("%q must be '%s' or '%s'.", k, ram.System, ram.Custom)) + } + return +} + +func validateRamGroupName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if len(value) > 64 { + errors = append(errors, fmt.Errorf("%q can not be longer than 64 characters.", k)) + } + + pattern := `^[a-zA-Z0-9\-]+$` + if match, _ := regexp.Match(pattern, []byte(value)); !match { + errors = append(errors, fmt.Errorf("%q is invalid.", k)) + } + return +} + +func validateRamAlias(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if len(value) > 32 || len(value) < 2 { + errors = append(errors, fmt.Errorf("%q can not be longer than 32 or less than 2 characters.", k)) + } + return +} + +func validateRamAKStatus(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if value != "Active" && value != "Inactive" { + errors = append(errors, fmt.Errorf("%q must be 'Active' or 'Inactive'.", k)) + } + return +} + +func validateContainerClusterName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 1 || len(value) > 64 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 64 characters and less than 1", 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 validateContainerClusterNamePrefix(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) > 38 { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than 38 characters, name is limited to 64", k)) + } + + return +} + +func validateCdnChargeType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if value != "PayByTraffic" && value != "PayByBandwidth" { + errors = append(errors, fmt.Errorf("%q must be 'PayByTraffic' or 'PayByBandwidth'.", k)) + } + return +} + +func validateCdnType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + for _, val := range cdn.CdnTypes { + if val == value { + return + } + } + errors = append(errors, fmt.Errorf("%q must be one of %v.", k, cdn.CdnTypes)) + return +} + +func validateCdnSourceType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + for _, val := range cdn.SourceTypes { + if val == value { + return + } + } + errors = append(errors, fmt.Errorf("%q must be one of %v.", k, cdn.SourceTypes)) + return +} + +func validateCdnScope(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + for _, val := range cdn.Scopes { + if val == value { + return + } + } + errors = append(errors, fmt.Errorf("%q must be one of %v.", k, cdn.Scopes)) + return +} + +func validateCdnSourcePort(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value != 80 && value != 443 { + errors = append(errors, fmt.Errorf("%q must be one 80 or 443.", k)) + } + return +} + +func validateCdnHttpHeader(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + for _, val := range cdn.HeaderKeys { + if val == value { + return + } + } + errors = append(errors, fmt.Errorf("%q must be one of %v.", k, cdn.HeaderKeys)) + return +} + +func validateCacheType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if value != "suffix" && value != "path" { + errors = append(errors, fmt.Errorf("%q must be 'suffix' or 'path'.", k)) + } + return +} + +func validateCdnEnable(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if value != "on" && value != "off" { + errors = append(errors, fmt.Errorf("%q must be 'on' or 'off'.", k)) + } + return +} + +func validateCdnHashKeyArg(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if strings.Contains(value, ",") { + errors = append(errors, fmt.Errorf("%q can not contains any ','.", k)) + } + return +} + +func validateCdnPage404Type(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if value != "default" && value != "charity" && value != "other" { + errors = append(errors, fmt.Errorf("%q must be one of ['default', 'charity', 'other'].", k)) + } + return +} + +func validateCdnRedirectType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if value != "Off" && value != "Http" && value != "Https" { + errors = append(errors, fmt.Errorf("%q must be one of ['Off', 'Http', 'Https'].", k)) + } + return +} + +func validateCdnReferType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if value != "block" && value != "allow" { + errors = append(errors, fmt.Errorf("%q must be 'block' or 'allow'.", k)) + } + return +} + +func validateCdnAuthType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if value != "no_auth" && value != "type_a" && value != "type_b" && value != "type_c" { + errors = append(errors, fmt.Errorf("%q must be one of ['no_auth', 'type_a', 'type_b', 'type_c']", k)) + } + return +} + +func validateCdnAuthKey(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + pattern := `^[a-zA-Z0-9]{6,32}$` + if match, _ := regexp.Match(pattern, []byte(value)); !match { + errors = append(errors, fmt.Errorf("%q can only consists of alphanumeric characters and can not be longer than 32 or less than 6 characters.", k)) + } + return +} + +func validatePolicyDocVersion(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if value != "1" { + errors = append(errors, fmt.Errorf("%q can only be '1' so far.", k)) + } + return +} + func validateRouterInterfaceDescription(v interface{}, k string) (ws []string, errors []error) { value := v.(string) if len(value) < 2 || len(value) > 256 { @@ -690,3 +1037,11 @@ func validateRouterInterfaceDescription(v interface{}, k string) (ws []string, e } return } + +func validateInstanceType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !strings.HasPrefix(value, "ecs.") { + errors = append(errors, fmt.Errorf("Invalid %q: %s. It must be 'ecs.' as prefix.", k, value)) + } + return +} diff --git a/vendor/github.com/denverdino/aliyungo/common/client.go b/vendor/github.com/denverdino/aliyungo/common/client.go index a59789fee44..80841eb5f12 100755 --- a/vendor/github.com/denverdino/aliyungo/common/client.go +++ b/vendor/github.com/denverdino/aliyungo/common/client.go @@ -3,10 +3,14 @@ package common import ( "bytes" "encoding/json" + "errors" + "fmt" "io/ioutil" "log" "net/http" "net/url" + "os" + "strconv" "strings" "time" @@ -25,6 +29,7 @@ type UnderlineString string type Client struct { AccessKeyId string //Access Key Id AccessKeySecret string //Access Key Secret + securityToken string debug bool httpClient *http.Client endpoint string @@ -32,19 +37,30 @@ type Client struct { serviceCode string regionID Region businessInfo string - userAgent string + userAgent string } -// NewClient creates a new instance of ECS client +// Initialize properties of a client instance func (client *Client) Init(endpoint, version, accessKeyId, accessKeySecret string) { client.AccessKeyId = accessKeyId client.AccessKeySecret = accessKeySecret + "&" client.debug = false - client.httpClient = &http.Client{} + handshakeTimeout, err := strconv.Atoi(os.Getenv("TLSHandshakeTimeout")) + if err != nil { + handshakeTimeout = 0 + } + if handshakeTimeout == 0 { + client.httpClient = &http.Client{} + } else { + t := &http.Transport{ + TLSHandshakeTimeout: time.Duration(handshakeTimeout) * time.Second} + client.httpClient = &http.Client{Transport: t} + } client.endpoint = endpoint client.version = version } +// Initialize properties of a client instance including regionID func (client *Client) NewInit(endpoint, version, accessKeyId, accessKeySecret, serviceCode string, regionID Region) { client.Init(endpoint, version, accessKeyId, accessKeySecret) client.serviceCode = serviceCode @@ -52,6 +68,29 @@ func (client *Client) NewInit(endpoint, version, accessKeyId, accessKeySecret, s client.setEndpointByLocation(regionID, serviceCode, accessKeyId, accessKeySecret) } +// Intialize client object when all properties are ready +func (client *Client) InitClient() *Client { + client.debug = false + handshakeTimeout, err := strconv.Atoi(os.Getenv("TLSHandshakeTimeout")) + if err != nil { + handshakeTimeout = 0 + } + if handshakeTimeout == 0 { + client.httpClient = &http.Client{} + } else { + t := &http.Transport{ + TLSHandshakeTimeout: time.Duration(handshakeTimeout) * time.Second} + client.httpClient = &http.Client{Transport: t} + } + client.setEndpointByLocation(client.regionID, client.serviceCode, client.AccessKeyId, client.AccessKeySecret) + return client +} + +func (client *Client) NewInitForAssumeRole(endpoint, version, accessKeyId, accessKeySecret, serviceCode string, regionID Region, securityToken string) { + client.NewInit(endpoint, version, accessKeyId, accessKeySecret, serviceCode, regionID) + client.securityToken = securityToken +} + //NewClient using location service func (client *Client) setEndpointByLocation(region Region, serviceCode, accessKeyId, accessKeySecret string) { locationClient := NewLocationClient(accessKeyId, accessKeySecret) @@ -65,6 +104,95 @@ func (client *Client) setEndpointByLocation(region Region, serviceCode, accessKe } } +// Ensure all necessary properties are valid +func (client *Client) ensureProperties() error { + var msg string + + if client.endpoint == "" { + msg = fmt.Sprintf("endpoint cannot be empty!") + } else if client.version == "" { + msg = fmt.Sprintf("version cannot be empty!") + } else if client.AccessKeyId == "" { + msg = fmt.Sprintf("AccessKeyId cannot be empty!") + } else if client.AccessKeySecret == "" { + msg = fmt.Sprintf("AccessKeySecret cannot be empty!") + } + + if msg != "" { + return errors.New(msg) + } + + return nil +} + +// ---------------------------------------------------- +// WithXXX methods +// ---------------------------------------------------- + +// WithEndpoint sets custom endpoint +func (client *Client) WithEndpoint(endpoint string) *Client { + client.SetEndpoint(endpoint) + return client +} + +// WithVersion sets custom version +func (client *Client) WithVersion(version string) *Client { + client.SetVersion(version) + return client +} + +// WithRegionID sets Region ID +func (client *Client) WithRegionID(regionID Region) *Client { + client.SetRegionID(regionID) + return client +} + +//WithServiceCode sets serviceCode +func (client *Client) WithServiceCode(serviceCode string) *Client { + client.SetServiceCode(serviceCode) + return client +} + +// WithAccessKeyId sets new AccessKeyId +func (client *Client) WithAccessKeyId(id string) *Client { + client.SetAccessKeyId(id) + return client +} + +// WithAccessKeySecret sets new AccessKeySecret +func (client *Client) WithAccessKeySecret(secret string) *Client { + client.SetAccessKeySecret(secret) + return client +} + +// WithSecurityToken sets securityToken +func (client *Client) WithSecurityToken(securityToken string) *Client { + client.SetSecurityToken(securityToken) + return client +} + +// WithDebug sets debug mode to log the request/response message +func (client *Client) WithDebug(debug bool) *Client { + client.SetDebug(debug) + return client +} + +// WithBusinessInfo sets business info to log the request/response message +func (client *Client) WithBusinessInfo(businessInfo string) *Client { + client.SetBusinessInfo(businessInfo) + return client +} + +// WithUserAgent sets user agent to the request/response message +func (client *Client) WithUserAgent(userAgent string) *Client { + client.SetUserAgent(userAgent) + return client +} + +// ---------------------------------------------------- +// SetXXX methods +// ---------------------------------------------------- + // SetEndpoint sets custom endpoint func (client *Client) SetEndpoint(endpoint string) { client.endpoint = endpoint @@ -75,6 +203,7 @@ func (client *Client) SetVersion(version string) { client.version = version } +// SetEndpoint sets Region ID func (client *Client) SetRegionID(regionID Region) { client.regionID = regionID } @@ -113,11 +242,19 @@ func (client *Client) SetUserAgent(userAgent string) { client.userAgent = userAgent } +//set SecurityToken +func (client *Client) SetSecurityToken(securityToken string) { + client.securityToken = securityToken +} + // Invoke sends the raw HTTP request for ECS services func (client *Client) Invoke(action string, args interface{}, response interface{}) error { + if err := client.ensureProperties(); err != nil { + return err + } request := Request{} - request.init(client.version, action, client.AccessKeyId) + request.init(client.version, action, client.AccessKeyId, client.securityToken, client.regionID) query := util.ConvertToQueryValues(request) util.SetQueryValues(args, &query) @@ -137,7 +274,7 @@ func (client *Client) Invoke(action string, args interface{}, response interface // TODO move to util and add build val flag httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo) - httpReq.Header.Set("User-Agent", httpReq.UserAgent()+ " " +client.userAgent) + httpReq.Header.Set("User-Agent", httpReq.UserAgent()+" "+client.userAgent) t0 := time.Now() httpResp, err := client.httpClient.Do(httpReq) @@ -185,9 +322,12 @@ func (client *Client) Invoke(action string, args interface{}, response interface // Invoke sends the raw HTTP request for ECS services func (client *Client) InvokeByFlattenMethod(action string, args interface{}, response interface{}) error { + if err := client.ensureProperties(); err != nil { + return err + } request := Request{} - request.init(client.version, action, client.AccessKeyId) + request.init(client.version, action, client.AccessKeyId, client.securityToken, client.regionID) query := util.ConvertToQueryValues(request) @@ -208,7 +348,7 @@ func (client *Client) InvokeByFlattenMethod(action string, args interface{}, res // TODO move to util and add build val flag httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo) - httpReq.Header.Set("User-Agent", httpReq.UserAgent()+ " " +client.userAgent) + httpReq.Header.Set("User-Agent", httpReq.UserAgent()+" "+client.userAgent) t0 := time.Now() httpResp, err := client.httpClient.Do(httpReq) @@ -258,10 +398,12 @@ func (client *Client) InvokeByFlattenMethod(action string, args interface{}, res //改进了一下上面那个方法,可以使用各种Http方法 //2017.1.30 增加了一个path参数,用来拓展访问的地址 func (client *Client) InvokeByAnyMethod(method, action, path string, args interface{}, response interface{}) error { + if err := client.ensureProperties(); err != nil { + return err + } request := Request{} - request.init(client.version, action, client.AccessKeyId) - + request.init(client.version, action, client.AccessKeyId, client.securityToken, client.regionID) data := util.ConvertToQueryValues(request) util.SetQueryValues(args, &data) @@ -290,8 +432,7 @@ func (client *Client) InvokeByAnyMethod(method, action, path string, args interf // TODO move to util and add build val flag httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo) - - httpReq.Header.Set("User-Agent", httpReq.Header.Get("User-Agent")+ " " +client.userAgent) + httpReq.Header.Set("User-Agent", httpReq.Header.Get("User-Agent")+" "+client.userAgent) t0 := time.Now() httpResp, err := client.httpClient.Do(httpReq) diff --git a/vendor/github.com/denverdino/aliyungo/common/regions.go b/vendor/github.com/denverdino/aliyungo/common/regions.go index 62e6e9d814f..199ee5915d3 100644 --- a/vendor/github.com/denverdino/aliyungo/common/regions.go +++ b/vendor/github.com/denverdino/aliyungo/common/regions.go @@ -16,6 +16,7 @@ const ( APSouthEast1 = Region("ap-southeast-1") APNorthEast1 = Region("ap-northeast-1") APSouthEast2 = Region("ap-southeast-2") + APSouthEast3 = Region("ap-southeast-3") USWest1 = Region("us-west-1") USEast1 = Region("us-east-1") @@ -23,12 +24,16 @@ const ( MEEast1 = Region("me-east-1") EUCentral1 = Region("eu-central-1") + + ShenZhenFinance = Region("cn-shenzhen-finance-1") + ShanghaiFinance = Region("cn-shanghai-finance-1") ) var ValidRegions = []Region{ Hangzhou, Qingdao, Beijing, Shenzhen, Hongkong, Shanghai, Zhangjiakou, USWest1, USEast1, - APNorthEast1, APSouthEast1, APSouthEast2, + APNorthEast1, APSouthEast1, APSouthEast2, APSouthEast3, MEEast1, EUCentral1, + ShenZhenFinance, ShanghaiFinance, } diff --git a/vendor/github.com/denverdino/aliyungo/common/request.go b/vendor/github.com/denverdino/aliyungo/common/request.go index 2a883f19b20..f35c2990def 100644 --- a/vendor/github.com/denverdino/aliyungo/common/request.go +++ b/vendor/github.com/denverdino/aliyungo/common/request.go @@ -20,7 +20,9 @@ const ( type Request struct { Format string Version string + RegionId Region AccessKeyId string + SecurityToken string Signature string SignatureMethod string Timestamp util.ISO6801Time @@ -30,7 +32,7 @@ type Request struct { Action string } -func (request *Request) init(version string, action string, AccessKeyId string) { +func (request *Request) init(version string, action string, AccessKeyId string, securityToken string, regionId Region) { request.Format = JSONResponseFormat request.Timestamp = util.NewISO6801Time(time.Now().UTC()) request.Version = version @@ -39,6 +41,8 @@ func (request *Request) init(version string, action string, AccessKeyId string) request.SignatureNonce = util.CreateRandomString() request.Action = action request.AccessKeyId = AccessKeyId + request.SecurityToken = securityToken + request.RegionId = regionId } type Response struct { diff --git a/vendor/github.com/denverdino/aliyungo/cs/client.go b/vendor/github.com/denverdino/aliyungo/cs/client.go new file mode 100644 index 00000000000..ab71ccde4fa --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/cs/client.go @@ -0,0 +1,192 @@ +package cs + +import ( + "bytes" + "crypto/md5" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "net/url" + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +const ( + // CRMDefaultEndpoint is the default API endpoint of CRM services + CSDefaultEndpoint = "https://cs.aliyuncs.com" + CSAPIVersion = "2015-12-15" +) + +// The Client type encapsulates operations with an OSS region. +type Client struct { + AccessKeyId string + AccessKeySecret string + SecurityToken string + endpoint string + Version string + debug bool + userAgent string + httpClient *http.Client +} + +type Response struct { + RequestId string `json:"request_id"` +} + +// NewClient creates a new instance of CRM client +func NewClient(accessKeyId, accessKeySecret string) *Client { + return &Client{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + endpoint: CSDefaultEndpoint, + Version: CSAPIVersion, + httpClient: &http.Client{}, + } +} + +func NewClientForAussumeRole(accessKeyId, accessKeySecret, securityToken string) *Client { + return &Client{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + SecurityToken: securityToken, + endpoint: CSDefaultEndpoint, + Version: CSAPIVersion, + httpClient: &http.Client{}, + } +} + +// SetDebug sets debug mode to log the request/response message +func (client *Client) SetDebug(debug bool) { + client.debug = debug +} + +// SetUserAgent sets user agent to log the request/response message +func (client *Client) SetUserAgent(userAgent string) { + client.userAgent = userAgent +} + +type Request struct { + Method string + URL string + Version string + Region common.Region + Signature string + SignatureMethod string + SignatureNonce string + Timestamp util.ISO6801Time + Body []byte +} + +// Invoke sends the raw HTTP request for ECS services +func (client *Client) Invoke(region common.Region, method string, path string, query url.Values, args interface{}, response interface{}) error { + + var reqBody []byte + var err error + var contentType string + var contentMD5 string + + if args != nil { + reqBody, err = json.Marshal(args) + if err != nil { + return err + } + contentType = "application/json" + hasher := md5.New() + hasher.Write(reqBody) + contentMD5 = base64.StdEncoding.EncodeToString(hasher.Sum(nil)) + } + + requestURL := client.endpoint + path + if query != nil && len(query) > 0 { + requestURL = requestURL + "?" + util.Encode(query) + } + var bodyReader io.Reader + if reqBody != nil { + bodyReader = bytes.NewReader(reqBody) + } + httpReq, err := http.NewRequest(method, requestURL, bodyReader) + if err != nil { + return common.GetClientError(err) + } + + if region != "" { + httpReq.Header.Set("x-acs-region-id", string(region)) + } + + if contentType != "" { + httpReq.Header.Set("Content-Type", contentType) + } + if contentMD5 != "" { + httpReq.Header.Set("Content-MD5", contentMD5) + } + // TODO move to util and add build val flag + httpReq.Header.Set("Date", util.GetGMTime()) + httpReq.Header.Set("Accept", "application/json") + //httpReq.Header.Set("x-acs-version", client.Version) + httpReq.Header["x-acs-signature-version"] = []string{"1.0"} + httpReq.Header["x-acs-signature-nonce"] = []string{util.CreateRandomString()} + httpReq.Header["x-acs-signature-method"] = []string{"HMAC-SHA1"} + + fmt.Printf("Header = %++v", httpReq.Header) + + if client.userAgent != "" { + httpReq.Header.Set("User-Agent", client.userAgent) + } + + if client.SecurityToken != "" { + httpReq.Header["x-acs-security-token"] = []string{client.SecurityToken} + } + + client.signRequest(httpReq) + + t0 := time.Now() + httpResp, err := client.httpClient.Do(httpReq) + t1 := time.Now() + if err != nil { + return common.GetClientError(err) + } + statusCode := httpResp.StatusCode + + if client.debug { + log.Printf("Invoke %s %s %d (%v)", method, requestURL, statusCode, t1.Sub(t0)) + } + + defer httpResp.Body.Close() + body, err := ioutil.ReadAll(httpResp.Body) + + if err != nil { + return common.GetClientError(err) + } + + if client.debug { + var prettyJSON bytes.Buffer + err = json.Indent(&prettyJSON, body, "", " ") + log.Println(string(prettyJSON.Bytes())) + } + + if statusCode >= 400 && statusCode <= 599 { + errorResponse := common.ErrorResponse{} + err = json.Unmarshal(body, &errorResponse) + ecsError := &common.Error{ + ErrorResponse: errorResponse, + StatusCode: statusCode, + } + return ecsError + } + + if response != nil { + err = json.Unmarshal(body, response) + //log.Printf("%++v", response) + if err != nil { + return common.GetClientError(err) + } + } + + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/cs/clusters.go b/vendor/github.com/denverdino/aliyungo/cs/clusters.go new file mode 100644 index 00000000000..9fd462f10c4 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/cs/clusters.go @@ -0,0 +1,188 @@ +package cs + +import ( + "net/http" + "net/url" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/denverdino/aliyungo/util" + "math" + "time" +) + +type ClusterState string + +const ( + Initial = ClusterState("initial") + Running = ClusterState("running") + Updating = ClusterState("updating") + Scaling = ClusterState("scaling") + Failed = ClusterState("failed") + Deleting = ClusterState("deleting") + DeleteFailed = ClusterState("deleteFailed") + Deleted = ClusterState("deleted") + InActive = ClusterState("inactive") +) + +type NodeStatus struct { + Health int64 `json:"health"` + Unhealth int64 `json:"unhealth"` +} + +type NetworkModeType string + +const ( + ClassicNetwork = NetworkModeType("classic") + VPCNetwork = NetworkModeType("vpc") +) + +// https://help.aliyun.com/document_detail/26053.html +type ClusterType struct { + AgentVersion string `json:"agent_version"` + ClusterID string `json:"cluster_id"` + Name string `json:"name"` + Created util.ISO6801Time `json:"created"` + ExternalLoadbalancerID string `json:"external_loadbalancer_id"` + MasterURL string `json:"master_url"` + NetworkMode NetworkModeType `json:"network_mode"` + RegionID common.Region `json:"region_id"` + SecurityGroupID string `json:"security_group_id"` + Size int64 `json:"size"` + State ClusterState `json:"state"` + Updated util.ISO6801Time `json:"updated"` + VPCID string `json:"vpc_id"` + VSwitchID string `json:"vswitch_id"` + NodeStatus string `json:"node_status"` + DockerVersion string `json:"docker_version"` +} + +func (client *Client) DescribeClusters(nameFilter string) (clusters []ClusterType, err error) { + query := make(url.Values) + + if nameFilter != "" { + query.Add("name", nameFilter) + } + + err = client.Invoke("", http.MethodGet, "/clusters", query, nil, &clusters) + return +} + +func (client *Client) DescribeCluster(id string) (cluster ClusterType, err error) { + err = client.Invoke("", http.MethodGet, "/clusters/"+id, nil, nil, &cluster) + return +} + +type ClusterCreationArgs struct { + Name string `json:"name"` + Size int64 `json:"size"` + NetworkMode NetworkModeType `json:"network_mode"` + SubnetCIDR string `json:"subnet_cidr,omitempty"` + InstanceType string `json:"instance_type"` + VPCID string `json:"vpc_id,omitempty"` + VSwitchID string `json:"vswitch_id,omitempty"` + Password string `json:"password"` + DataDiskSize int64 `json:"data_disk_size"` + DataDiskCategory ecs.DiskCategory `json:"data_disk_category"` + ECSImageID string `json:"ecs_image_id,omitempty"` + IOOptimized ecs.IoOptimized `json:"io_optimized"` +} + +type ClusterCreationResponse struct { + Response + ClusterID string `json:"cluster_id"` +} + +func (client *Client) CreateCluster(region common.Region, args *ClusterCreationArgs) (cluster ClusterCreationResponse, err error) { + err = client.Invoke(region, http.MethodPost, "/clusters", nil, args, &cluster) + return +} + +type ClusterResizeArgs struct { + Size int64 `json:"size"` + InstanceType string `json:"instance_type"` + Password string `json:"password"` + DataDiskSize int64 `json:"data_disk_size"` + DataDiskCategory ecs.DiskCategory `json:"data_disk_category"` + ECSImageID string `json:"ecs_image_id,omitempty"` + IOOptimized ecs.IoOptimized `json:"io_optimized"` +} + +func (client *Client) ResizeCluster(clusterID string, args *ClusterResizeArgs) error { + return client.Invoke("", http.MethodPut, "/clusters/"+clusterID, nil, args, nil) +} + +func (client *Client) DeleteCluster(clusterID string) error { + return client.Invoke("", http.MethodDelete, "/clusters/"+clusterID, nil, nil, nil) +} + +type ClusterCerts struct { + CA string `json:"ca,omitempty"` + Key string `json:"key,omitempty"` + Cert string `json:"cert,omitempty"` +} + +func (client *Client) GetClusterCerts(id string) (certs ClusterCerts, err error) { + err = client.Invoke("", http.MethodGet, "/clusters/"+id+"/certs", nil, nil, &certs) + return +} + +const ClusterDefaultTimeout = 300 +const DefaultWaitForInterval = 10 +const DefaultPreSleepTime = 240 + +// WaitForCluster waits for instance to given status +// when instance.NotFound wait until timeout +func (client *Client) WaitForClusterAsyn(clusterId string, status ClusterState, timeout int) error { + if timeout <= 0 { + timeout = ClusterDefaultTimeout + } + cluster, err := client.DescribeCluster(clusterId) + if err != nil { + return err + } else if cluster.State == status { + //TODO + return nil + } + // Create or Reset cluster usually cost at least 4 min, so there will sleep a long time before polling + sleep := math.Min(float64(timeout), float64(DefaultPreSleepTime)) + time.Sleep(time.Duration(sleep) * time.Second) + + for { + cluster, err := client.DescribeCluster(clusterId) + if err != nil { + return err + } else if cluster.State == status { + //TODO + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} + +func (client *Client) GetProjectClient(clusterId string) (projectClient *ProjectClient, err error) { + cluster, err := client.DescribeCluster(clusterId) + if err != nil { + return + } + + certs, err := client.GetClusterCerts(clusterId) + if err != nil { + return + } + + projectClient, err = NewProjectClient(clusterId, cluster.MasterURL, certs) + + if err != nil { + return + } + + projectClient.SetDebug(client.debug) + + return +} diff --git a/vendor/github.com/denverdino/aliyungo/cs/projects.go b/vendor/github.com/denverdino/aliyungo/cs/projects.go new file mode 100644 index 00000000000..c2e18793301 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/cs/projects.go @@ -0,0 +1,186 @@ +package cs + +import ( + "errors" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +type Project struct { + Name string `json:"name"` + Description string `json:"description"` + Template string `json:"template"` + Version string `json:"version"` + Created string `json:"created"` + Updated string `json:"updated"` + DesiredState string `json:"desired_state"` + CurrentState string `json:"current_state"` + Environment map[string]string `json:"environment"` + Services []Service `json:"services"` +} + +type GetProjectsResponse []Project + +type GetProjectResponse Project + +type Port struct { + HostIP string `json:"host_ip"` + HostPort string `json:"host_port"` +} + +type Labels map[string]string + +type Definition struct { + Environment []string `json:"environment"` + Image string `json:"image"` + KernelMemory int `json:"kernel_memory"` + Labels Labels `json:"labels"` + MemLimit int `json:"mem_limit"` + MemswapLimit int `json:"memswap_limit"` + MemswapReservation int `json:"memswap_reservation"` + OomKillDisable bool `json:"oom_kill_disable"` + Restart string `json:"restart"` + ShmSize int `json:"shm_size"` + Volumes []string `json:"volumes"` +} + +type ProjectCreationArgs struct { + Name string `json:"name"` + Description string `json:"description"` + Template string `json:"template"` + Version string `json:"version"` + Environment map[string]string `json:"environment"` + LatestImage bool `json:"latest_image"` +} + +type ProjectUpdationArgs struct { + Name string `json:"-"` + Description string `json:"description"` + Template string `json:"template"` + Version string `json:"version"` + Environment map[string]string `json:"environment"` + LatestImage bool `json:"latest_image"` +} + +func (client *ProjectClient) GetProjects(q string, services, containers bool) (projects GetProjectsResponse, err error) { + query := make(url.Values) + + if len(q) != 0 { + query.Add("q", q) + } + + query.Add("services", strconv.FormatBool(services)) + query.Add("containers", strconv.FormatBool(containers)) + + err = client.Invoke(http.MethodGet, "/projects/", query, nil, &projects) + + return +} + +func (client *ProjectClient) GetProject(name string) (project GetProjectResponse, err error) { + + if len(name) == 0 { + err = errors.New("project name is empty") + return + } + + err = client.Invoke(http.MethodGet, "/projects/"+name, nil, nil, &project) + + return +} + +func (client *ProjectClient) StartProject(name string) (err error) { + + if len(name) == 0 { + err = errors.New("project name is empty") + return + } + + err = client.Invoke(http.MethodPost, "/projects/"+name+"/start", nil, nil, nil) + + return +} + +func (client *ProjectClient) StopProject(name string, timeout ...time.Duration) (err error) { + + if len(name) == 0 { + err = errors.New("project name is empty") + return + } + + query := make(url.Values) + + if len(timeout) > 0 { + if timeout[0] > 0 { + query.Add("t", strconv.Itoa(int(timeout[0].Seconds()))) + } + } + + err = client.Invoke(http.MethodPost, "/projects/"+name+"/stop", query, nil, nil) + + return +} + +func (client *ProjectClient) KillProject(name string, signal ...string) (err error) { + + if len(name) == 0 { + err = errors.New("project name is empty") + return + } + + query := make(url.Values) + + if len(signal) > 0 { + if len(signal[0]) > 0 { + query.Add("signal", signal[0]) + } + } + + err = client.Invoke(http.MethodPost, "/projects/"+name+"/kill", query, nil, nil) + + return +} + +func (client *ProjectClient) CreateProject(args *ProjectCreationArgs) (err error) { + + args.Template = strings.TrimSpace(args.Template) + + err = client.Invoke(http.MethodPost, "/projects/", nil, args, nil) + + return +} + +func (client *ProjectClient) UpdateProject(args *ProjectUpdationArgs) (err error) { + + if len(args.Name) == 0 { + err = errors.New("project name is empty") + return + } + + args.Template = strings.TrimSpace(args.Template) + + err = client.Invoke(http.MethodPost, "/projects/"+args.Name+"/update", nil, args, nil) + + return +} + +func (client *ProjectClient) DeleteProject(name string, forceDelete, deleteVolume bool) (err error) { + + if len(name) == 0 { + err = errors.New("project name is empty") + return + } + + query := make(url.Values) + + query.Add("name", name) + query.Add("force", strconv.FormatBool(forceDelete)) + query.Add("volume", strconv.FormatBool(deleteVolume)) + + err = client.Invoke(http.MethodDelete, "/projects/"+name, query, nil, nil) + + return +} diff --git a/vendor/github.com/denverdino/aliyungo/cs/projects_client.go b/vendor/github.com/denverdino/aliyungo/cs/projects_client.go new file mode 100644 index 00000000000..0890fbcd46a --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/cs/projects_client.go @@ -0,0 +1,156 @@ +package cs + +import ( + "bytes" + "crypto/tls" + "crypto/x509" + "encoding/json" + "io" + "io/ioutil" + "log" + "net/http" + "net/url" + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type ProjectClient struct { + clusterId string + endpoint string + debug bool + userAgent string + httpClient *http.Client +} + +func NewProjectClient(clusterId, endpoint string, clusterCerts ClusterCerts) (client *ProjectClient, err error) { + + certs, err := tls.X509KeyPair([]byte(clusterCerts.Cert), []byte(clusterCerts.Key)) + + if err != nil { + return + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM([]byte(clusterCerts.CA)) + + httpClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + Certificates: []tls.Certificate{certs}, + ClientCAs: caCertPool, + ClientAuth: tls.RequireAndVerifyClientCert, + }, + }, + } + + client = &ProjectClient{ + endpoint: endpoint, + httpClient: httpClient, + } + + return +} + +// SetDebug sets debug mode to log the request/response message +func (client *ProjectClient) SetDebug(debug bool) { + client.debug = debug +} + +// SetUserAgent sets user agent to log the request/response message +func (client *ProjectClient) SetUserAgent(userAgent string) { + client.userAgent = userAgent +} + +func (client *ProjectClient) ClusterId() string { + return client.clusterId +} + +func (client *ProjectClient) Endpoint() string { + return client.endpoint +} + +func (client *ProjectClient) Invoke(method string, path string, query url.Values, args interface{}, response interface{}) error { + var reqBody []byte + var err error + var contentType string + + if args != nil { + reqBody, err = json.Marshal(args) + if err != nil { + return err + } + contentType = "application/json" + } + + requestURL := client.endpoint + path + if query != nil && len(query) > 0 { + requestURL = requestURL + "?" + util.Encode(query) + } + var bodyReader io.Reader + if reqBody != nil { + bodyReader = bytes.NewReader(reqBody) + } + + httpReq, err := http.NewRequest(method, requestURL, bodyReader) + if err != nil { + return common.GetClientError(err) + } + + httpReq.Header.Set("Date", util.GetGMTime()) + httpReq.Header.Set("Accept", "application/json") + + if contentType != "" { + httpReq.Header.Set("Content-Type", contentType) + } + + if client.userAgent != "" { + httpReq.Header.Set("User-Agent", client.userAgent) + } + + t0 := time.Now() + httpResp, err := client.httpClient.Do(httpReq) + t1 := time.Now() + if err != nil { + return common.GetClientError(err) + } + statusCode := httpResp.StatusCode + + if client.debug { + log.Printf("Invoke %s %s %d (%v)", method, requestURL, statusCode, t1.Sub(t0)) + } + + defer httpResp.Body.Close() + body, err := ioutil.ReadAll(httpResp.Body) + + if err != nil { + return common.GetClientError(err) + } + + if client.debug { + var prettyJSON bytes.Buffer + err = json.Indent(&prettyJSON, body, "", " ") + log.Println(string(prettyJSON.Bytes())) + } + + if statusCode >= 400 && statusCode <= 599 { + errorResponse := common.ErrorResponse{} + err = json.Unmarshal(body, &errorResponse) + ecsError := &common.Error{ + ErrorResponse: errorResponse, + StatusCode: statusCode, + } + return ecsError + } + + if response != nil { + err = json.Unmarshal(body, response) + if err != nil { + return common.GetClientError(err) + } + } + + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/cs/services.go b/vendor/github.com/denverdino/aliyungo/cs/services.go new file mode 100644 index 00000000000..37f779a9051 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/cs/services.go @@ -0,0 +1,143 @@ +package cs + +import ( + "errors" + "net/http" + "net/url" + "strconv" + "time" +) + +type Container struct { + Name string `json:"name"` + Node string `json:"node"` + VMID string `json:"vm_id"` + IP string `json:"ip"` + Running bool `json:"running"` + Status string `json:"status"` + Health string `json:"health"` + StatusExt string `json:"status_ext"` + FailCount int `json:"fail_count"` + Ports map[string][]Port `json:"ports"` +} + +type Service struct { + ID string `json:"id"` + Name string `json:"name"` + Project string `json:"project"` + ComposeVersion string `json:"compose_version"` + Containers map[string]Container `json:"containers"` + Created time.Time `json:"created"` + CurrentState string `json:"current_state"` + Definition Definition `json:"definition"` + DesiredState string `json:"desired_state"` + Extensions map[string]interface{} `json:"extensions"` + Hash string `json:"hash"` + Updated time.Time `json:"updated"` + Version string `json:"version"` +} + +type ScaleType string + +const ( + ScaleTo ScaleType = "scale_to" +) + +type ScaleServiceArgs struct { + ServiceId string `json:"-"` + Type ScaleType `json:"type"` + Value int `json:"value"` +} + +type GetServiceResponse Service +type GetServicesResponse []Service + +func (client *ProjectClient) GetServices(q string, containers bool) (services GetServicesResponse, err error) { + query := make(url.Values) + + if len(q) != 0 { + query.Add("q", q) + } + + query.Add("containers", strconv.FormatBool(containers)) + + err = client.Invoke(http.MethodGet, "/services/", query, nil, &services) + + return +} + +func (client *ProjectClient) GetService(serviceId string) (service GetServiceResponse, err error) { + + if len(serviceId) == 0 { + err = errors.New("service id is empty") + return + } + + err = client.Invoke(http.MethodGet, "/services/"+serviceId, nil, nil, &service) + + return +} + +func (client *ProjectClient) StartService(serviceId string) (err error) { + + if len(serviceId) == 0 { + err = errors.New("service id is empty") + return + } + + err = client.Invoke(http.MethodPost, "/services/"+serviceId+"/start", nil, nil, nil) + + return +} + +func (client *ProjectClient) StopService(serviceId string, timeout ...time.Duration) (err error) { + + if len(serviceId) == 0 { + err = errors.New("service id is empty") + return + } + + query := make(url.Values) + + if len(timeout) > 0 { + if timeout[0] > 0 { + query.Add("t", strconv.Itoa(int(timeout[0].Seconds()))) + } + } + + err = client.Invoke(http.MethodPost, "/services/"+serviceId+"/stop", query, nil, nil) + + return +} + +func (client *ProjectClient) KillService(serviceId string, signal ...string) (err error) { + + if len(serviceId) == 0 { + err = errors.New("service id is empty") + return + } + + query := make(url.Values) + + if len(signal) > 0 { + if len(signal[0]) > 0 { + query.Add("signal", signal[0]) + } + } + + err = client.Invoke(http.MethodPost, "/services/"+serviceId+"/kill", query, nil, nil) + + return +} + +func (client *ProjectClient) ScaleService(args *ScaleServiceArgs) (err error) { + + if len(args.ServiceId) == 0 { + err = errors.New("service id is empty") + return + } + + err = client.Invoke(http.MethodPost, "/services/"+args.ServiceId+"/scale", nil, args, nil) + + return +} diff --git a/vendor/github.com/denverdino/aliyungo/cs/signature.go b/vendor/github.com/denverdino/aliyungo/cs/signature.go new file mode 100644 index 00000000000..e8d94b5a60c --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/cs/signature.go @@ -0,0 +1,60 @@ +package cs + +import ( + "fmt" + "net/http" + "sort" + "strings" + + "github.com/denverdino/aliyungo/util" +) + +func (client *Client) signRequest(request *http.Request) { + + headers := request.Header + contentMd5 := headers.Get("Content-Md5") + contentType := headers.Get("Content-Type") + accept := headers.Get("Accept") + date := headers.Get("Date") + + canonicalizedResource := request.URL.RequestURI() + + _, canonicalizedHeader := canonicalizeHeader(headers) + + stringToSign := request.Method + "\n" + accept + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedHeader + canonicalizedResource + + fmt.Printf("stringToSign = %s: ", stringToSign) + signature := util.CreateSignature(stringToSign, client.AccessKeySecret) + headers.Set("Authorization", "acs "+client.AccessKeyId+":"+signature) +} + +const headerOSSPrefix = "x-acs-" + +//Have to break the abstraction to append keys with lower case. +func canonicalizeHeader(headers http.Header) (newHeaders http.Header, result string) { + var canonicalizedHeaders []string + newHeaders = http.Header{} + + for k, v := range headers { + if lower := strings.ToLower(k); strings.HasPrefix(lower, headerOSSPrefix) { + newHeaders[lower] = v + canonicalizedHeaders = append(canonicalizedHeaders, lower) + } else { + newHeaders[k] = v + } + } + + sort.Strings(canonicalizedHeaders) + + var canonicalizedHeader string + + for _, k := range canonicalizedHeaders { + v := "" + if len(headers[k]) > 0 { + v = headers[k][0] + } + canonicalizedHeader += k + ":" + v + "\n" + } + + return newHeaders, canonicalizedHeader +} diff --git a/vendor/github.com/denverdino/aliyungo/cs/volumes.go b/vendor/github.com/denverdino/aliyungo/cs/volumes.go new file mode 100644 index 00000000000..1b9912b17c0 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/cs/volumes.go @@ -0,0 +1,89 @@ +package cs + +import ( + "net/http" +) + +type VolumeDriverType string + +const ( + OSSFSDriver VolumeDriverType = "ossfs" + NASDriver VolumeDriverType = "nas" +) + +type DriverOptions interface { + driverOptions() +} + +type OSSOpts struct { + Bucket string `json:"bucket"` + AccessKeyId string `json:"ak_id"` + AccessKeySecret string `json:"ak_secret"` + URL string `json:"url"` + NoStatCache string `json:"no_stat_cache"` + OtherOpts string `json:"other_opts"` +} + +func (*OSSOpts) driverOptions() {} + +type NASOpts struct { + DiskId string `json:"disk_id"` + Host string `json:"host"` + Path string `json:"path"` + Mode string `json:"mode"` +} + +func (*NASOpts) driverOptions() {} + +type VolumeRef struct { + Name string `json:"Name"` + ID string `json:"ID"` +} + +type VolumeCreationArgs struct { + Name string `json:"name"` + Driver VolumeDriverType `json:"driver"` + DriverOpts DriverOptions `json:"driverOpts"` +} + +type VolumeCreationResponse struct { + Name string `json:"Name"` + Driver string `json:"Driver"` + Mountpoint string `json:"Mountpoint"` + Labels map[string]string `json:"Labels"` + Scope string `json:"Scope"` +} + +type GetVolumeResponse struct { + Name string `json:"Name"` + Driver string `json:"Driver"` + Mountpoint string `json:"Mountpoint"` + Labels map[string]string `json:"Labels"` + Scope string `json:"Scope"` + Node string `json:"Node"` + Refs []VolumeRef `json:"Refs"` +} + +type GetVolumesResponse struct { + Volumes []GetVolumeResponse `json:"Volumes"` +} + +func (client *ProjectClient) CreateVolume(args *VolumeCreationArgs) (err error) { + err = client.Invoke(http.MethodPost, "/volumes/create", nil, args, nil) + return +} + +func (client *ProjectClient) GetVolume(name string) (volume GetVolumeResponse, err error) { + err = client.Invoke(http.MethodGet, "/volumes/"+name, nil, nil, &volume) + return +} + +func (client *ProjectClient) GetVolumes() (volumes GetVolumesResponse, err error) { + err = client.Invoke(http.MethodGet, "/volumes", nil, nil, &volumes) + return +} + +func (client *ProjectClient) DeleteVolume(name string) (err error) { + err = client.Invoke(http.MethodDelete, "/volumes/"+name, nil, nil, nil) + return +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/client.go b/vendor/github.com/denverdino/aliyungo/ecs/client.go index d70a1554eb2..5b9d62619ba 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/client.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/client.go @@ -20,8 +20,7 @@ const ( // ECSDefaultEndpoint is the default API endpoint of ECS services ECSDefaultEndpoint = "https://ecs-cn-hangzhou.aliyuncs.com" ECSAPIVersion = "2014-05-26" - - ECSServiceCode = "ecs" + ECSServiceCode = "ecs" VPCDefaultEndpoint = "https://vpc.aliyuncs.com" VPCAPIVersion = "2016-04-28" @@ -37,38 +36,89 @@ func NewClient(accessKeyId, accessKeySecret string) *Client { return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret) } +func NewClientWithRegion(endpoint string, accessKeyId string, accessKeySecret string, regionID common.Region) *Client { + client := &Client{} + client.NewInit(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret, ECSServiceCode, regionID) + return client +} + +func NewClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string) *Client { + client := &Client{} + client.Init(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret) + return client +} + +// --------------------------------------- +// NewECSClient creates a new instance of ECS client +// --------------------------------------- func NewECSClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client { + return NewECSClientWithSecurityToken(accessKeyId, accessKeySecret, "", regionID) +} + +func NewECSClientWithSecurityToken(accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client { endpoint := os.Getenv("ECS_ENDPOINT") if endpoint == "" { endpoint = ECSDefaultEndpoint } - return NewClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID) + return NewECSClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID) } -func NewClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client { - client := &Client{} - client.NewInit(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret, ECSServiceCode, regionID) - return client +func NewECSClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string, regionID common.Region) *Client { + return NewECSClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, "", regionID) } -func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client { +func NewECSClientWithEndpointAndSecurityToken(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client { client := &Client{} - client.Init(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret) + client.WithEndpoint(endpoint). + WithVersion(ECSAPIVersion). + WithAccessKeyId(accessKeyId). + WithAccessKeySecret(accessKeySecret). + WithSecurityToken(securityToken). + WithServiceCode(ECSServiceCode). + WithRegionID(regionID). + InitClient() return client } -func NewVPCClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client { +// --------------------------------------- +// NewVPCClient creates a new instance of VPC client +// --------------------------------------- +func NewVPCClient(accessKeyId string, accessKeySecret string, regionID common.Region) *Client { + return NewVPCClientWithSecurityToken(accessKeyId, accessKeySecret, "", regionID) +} + +func NewVPCClientWithSecurityToken(accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client { endpoint := os.Getenv("VPC_ENDPOINT") if endpoint == "" { endpoint = VPCDefaultEndpoint } - return NewVPCClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID) + return NewVPCClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID) } -func NewVPCClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client { +func NewVPCClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string, regionID common.Region) *Client { + return NewVPCClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, "", regionID) +} + +func NewVPCClientWithEndpointAndSecurityToken(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client { client := &Client{} - client.NewInit(endpoint, VPCAPIVersion, accessKeyId, accessKeySecret, VPCServiceCode, regionID) + client.WithEndpoint(endpoint). + WithVersion(VPCAPIVersion). + WithAccessKeyId(accessKeyId). + WithAccessKeySecret(accessKeySecret). + WithSecurityToken(securityToken). + WithServiceCode(VPCServiceCode). + WithRegionID(regionID). + InitClient() return client } + +// --------------------------------------- +// NewVPCClientWithRegion creates a new instance of VPC client automatically get endpoint +// --------------------------------------- +func NewVPCClientWithRegion(endpoint string, accessKeyId string, accessKeySecret string, regionID common.Region) *Client { + client := &Client{} + client.NewInit(endpoint, VPCAPIVersion, accessKeyId, accessKeySecret, VPCServiceCode, regionID) + return client +} \ No newline at end of file diff --git a/vendor/github.com/denverdino/aliyungo/ecs/disks.go b/vendor/github.com/denverdino/aliyungo/ecs/disks.go index 2cfbffc379f..6b898c60d6d 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/disks.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/disks.go @@ -241,6 +241,29 @@ func (client *Client) DetachDisk(instanceId string, diskId string) error { return err } +type ResizeDiskArgs struct { + DiskId string + NewSize int +} + +type ResizeDiskResponse struct { + common.Response +} + +// +// ResizeDisk can only support to enlarge disk size +// You can read doc at https://help.aliyun.com/document_detail/25522.html +func (client *Client) ResizeDisk(diskId string, sizeGB int) error { + args := ResizeDiskArgs{ + DiskId:diskId, + NewSize:sizeGB, + } + response := ResizeDiskResponse{} + err := client.Invoke("ResizeDisk", &args, &response) + return err +} + + type ResetDiskArgs struct { DiskId string SnapshotId string @@ -250,6 +273,7 @@ type ResetDiskResponse struct { common.Response } + // ResetDisk resets disk to original status // // You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&resetdisk diff --git a/vendor/github.com/denverdino/aliyungo/ecs/eni.go b/vendor/github.com/denverdino/aliyungo/ecs/eni.go new file mode 100644 index 00000000000..38173a029cd --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/eni.go @@ -0,0 +1,108 @@ +package ecs + +import "github.com/denverdino/aliyungo/common" + +type CreateNetworkInterfaceArgs struct { + RegionId common.Region + VSwitchId string + PrimaryIpAddress string // optional + SecurityGroupId string + NetworkInterfaceName string // optional + Description string // optional + ClientToken string // optional +} + +type CreateNetworkInterfaceResponse struct { + common.Response + NetworkInterfaceId string +} +type DeleteNetworkInterfaceArgs struct { + RegionId common.Region + NetworkInterfaceId string +} + +type DeleteNetworkInterfaceResponse struct { + common.Response +} + +type DescribeNetworkInterfacesArgs struct { + RegionId common.Region + VSwitchId string + PrimaryIpAddress string + SecurityGroupId string + NetworkInterfaceName string + Type string + InstanceId string + NetworkInterfaceId []string + PageNumber int + PageSize int +} +type NetworkInterfaceType struct { + NetworkInterfaceId string + PrimaryIpAddress string + MacAddress string +} + +type DescribeNetworkInterfacesResponse struct { + common.Response + NetworkInterfaceSet []NetworkInterfaceType + TotalCount int + PageNumber int + PageSize int +} +type AttachNetworkInterfaceArgs struct { + RegionId common.Region + NetworkInterfaceId string + InstanceId string +} + +type AttachNetworkInterfaceResponse common.Response + +type DetachNetworkInterfaceArgs AttachNetworkInterfaceArgs + +type DetachNetworkInterfaceResponse common.Response + +type ModifyNetworkInterfaceAttributeArgs struct { + RegionId common.Region + NetworkInterfaceId string + SecurityGroupId []string + NetworkInterfaceName string + Description string +} +type ModifyNetworkInterfaceAttributeResponse common.Response + +func (client *Client) CreateNetworkInterface(args *CreateNetworkInterfaceArgs) (resp *CreateNetworkInterfaceResponse, err error) { + resp = &CreateNetworkInterfaceResponse{} + err = client.Invoke("CreateNetworkInterface", args, resp) + return resp, err +} + +func (client *Client) DeleteNetworkInterface(args *DeleteNetworkInterfaceArgs) (resp *DeleteNetworkInterfaceResponse, err error) { + resp = &DeleteNetworkInterfaceResponse{} + err = client.Invoke("DeleteNetworkInterface", args, resp) + return resp, err +} + +func (client *Client) DescribeNetworkInterfaces(args *DescribeNetworkInterfacesArgs) (resp *DescribeNetworkInterfacesResponse, err error) { + resp = &DescribeNetworkInterfacesResponse{} + err = client.Invoke("DescribeNetworkInterfaces", args, resp) + return resp, err +} + +func (client *Client) AttachNetworkInterface(args *AttachNetworkInterfaceArgs) (resp *AttachNetworkInterfaceResponse, err error) { + resp = &AttachNetworkInterfaceResponse{} + err = client.Invoke("AttachNetworkInterface", args, resp) + return resp, err +} + +func (client *Client) DetachNetworkInterface(args *DetachNetworkInterfaceArgs) (resp *DetachNetworkInterfaceResponse, err error) { + resp = &DetachNetworkInterfaceResponse{} + err = client.Invoke("DetachNetworkInterface", args, resp) + return resp, err +} + +func (client *Client) ModifyNetworkInterfaceAttribute(args *ModifyNetworkInterfaceAttributeArgs) (resp *ModifyNetworkInterfaceAttributeResponse, err error) { + resp = &ModifyNetworkInterfaceAttributeResponse{} + err = client.Invoke("ModifyNetworkInterfaceAttribute", args, resp) + return resp, err +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/images.go b/vendor/github.com/denverdino/aliyungo/ecs/images.go index 54fe86defe9..9dea891dd8b 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/images.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/images.go @@ -37,6 +37,13 @@ const ( ImageUsageNone = ImageUsage("none") ) +type ImageFormatType string + +const ( + RAW = ImageFormatType("RAW") + VHD = ImageFormatType("VHD") +) + // DescribeImagesArgs repsents arguments to describe images type DescribeImagesArgs struct { RegionId common.Region @@ -64,6 +71,8 @@ type DiskDeviceMapping struct { SnapshotId string //Why Size Field is string-type. Size string + // Now the key Size change to DiskImageSize + DiskImageSize string Device string //For import images Format string diff --git a/vendor/github.com/denverdino/aliyungo/ecs/instances.go b/vendor/github.com/denverdino/aliyungo/ecs/instances.go index a10bcc05357..74be6e50130 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/instances.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/instances.go @@ -224,6 +224,7 @@ type SpotStrategyType string const ( NoSpot = SpotStrategyType("NoSpot") SpotWithPriceLimit = SpotStrategyType("SpotWithPriceLimit") + SpotAsPriceGo = SpotStrategyType("SpotAsPriceGo") ) // @@ -244,7 +245,7 @@ type InstanceAttributesType struct { SerialNumber string Status InstanceStatus OperationLocks OperationLocksType - SecurityGroupIds struct { + SecurityGroupIds struct { SecurityGroupId []string } PublicIpAddress IpAddressSetType @@ -259,11 +260,12 @@ type InstanceAttributesType struct { IoOptimized StringOrBool InstanceChargeType common.InstanceChargeType ExpiredTime util.ISO6801Time - Tags struct { + Tags struct { Tag []TagItemType } - SpotStrategy SpotStrategyType - KeyPairName string + SpotStrategy SpotStrategyType + SpotPriceLimit float64 + KeyPairName string } type DescribeInstanceAttributeResponse struct { @@ -438,6 +440,28 @@ func (client *Client) DescribeInstancesWithRaw(args *DescribeInstancesArgs) (res return response, nil } +type ModifyInstanceAutoReleaseTimeArgs struct { + InstanceId string + AutoReleaseTime string +} + +type ModifyInstanceAutoReleaseTimeResponse struct { + common.Response +} + +// 对给定的实例设定自动释放时间。 +// +// You can read doc at https://help.aliyun.com/document_detail/47576.html +func (client *Client) ModifyInstanceAutoReleaseTime(instanceId, time string) error { + args := ModifyInstanceAutoReleaseTimeArgs{ + InstanceId: instanceId, + AutoReleaseTime: time, + } + response := ModifyInstanceAutoReleaseTimeResponse{} + err := client.Invoke("ModifyInstanceAutoReleaseTime", &args, &response) + return err +} + type DeleteInstanceArgs struct { InstanceId string } @@ -535,6 +559,7 @@ type CreateInstanceArgs struct { AutoRenew bool AutoRenewPeriod int SpotStrategy SpotStrategyType + SpotPriceLimit float64 KeyPairName string RamRoleName string } @@ -658,7 +683,7 @@ func (client *Client) DetachInstanceRamRole(args *AttachInstancesArgs) (err erro type DescribeInstanceRamRoleResponse struct { common.Response - InstanceRamRoleSets struct{ + InstanceRamRoleSets struct { InstanceRamRoleSet []InstanceRamRoleSetType } } diff --git a/vendor/github.com/denverdino/aliyungo/ecs/router_interface.go b/vendor/github.com/denverdino/aliyungo/ecs/router_interface.go index 62af6786023..3cdef2f99ed 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/router_interface.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/router_interface.go @@ -2,6 +2,7 @@ package ecs import ( "github.com/denverdino/aliyungo/common" + "time" ) type EcsCommonResponse struct { @@ -19,6 +20,8 @@ const ( Idl = InterfaceStatus("Idl") Active = InterfaceStatus("Active") Inactive = InterfaceStatus("Inactive") + // 'Idle' means the router interface is not connected. 'Idl' may be a incorrect status. + Idle = InterfaceStatus("Idle") InitiatingSide = Role("InitiatingSide") AcceptingSide = Role("AcceptingSide") @@ -225,3 +228,30 @@ func (client *Client) DeleteRouterInterface(args *OperateRouterInterfaceArgs) (r } return response, nil } + +// WaitForRouterInterface waits for router interface to given status +func (client *Client) WaitForRouterInterfaceAsyn(regionId common.Region, interfaceId string, status InterfaceStatus, timeout int) error { + if timeout <= 0 { + timeout = InstanceDefaultTimeout + } + for { + interfaces, err := client.DescribeRouterInterfaces(&DescribeRouterInterfacesArgs{ + RegionId: regionId, + Filter: []Filter{Filter{Key: "RouterInterfaceId", Value: []string{interfaceId}}}, + + }) + if err != nil { + return err + } else if interfaces != nil && InterfaceStatus(interfaces.RouterInterfaceSet.RouterInterfaceType[0].Status) == status { + //TODO + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + + } + return nil +} \ No newline at end of file diff --git a/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go index 54af3a79824..a35de940756 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go @@ -6,10 +6,15 @@ import ( ) type NicType string +type Direction string const ( NicTypeInternet = NicType("internet") NicTypeIntranet = NicType("intranet") + + DirectionIngress = Direction("ingress") + DirectionEgress = Direction("egress") + DirectionAll = Direction("all") ) type IpProtocol string @@ -32,8 +37,8 @@ const ( type DescribeSecurityGroupAttributeArgs struct { SecurityGroupId string RegionId common.Region - NicType NicType //enum for internet (default) |intranet - Direction string // enum ingress egress + NicType NicType //enum for internet (default) |intranet + Direction Direction // enum ingress egress } // diff --git a/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go b/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go index f3c1b09c55e..0b7cbe87bf0 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go @@ -26,6 +26,8 @@ type SnapshotType struct { SourceDiskSize int SourceDiskType string //enum for System | Data ProductCode string + Status string + Usage string CreationTime util.ISO6801Time } diff --git a/vendor/github.com/denverdino/aliyungo/ess/configuration.go b/vendor/github.com/denverdino/aliyungo/ess/configuration.go index 59fae142b8b..28d1c291a72 100644 --- a/vendor/github.com/denverdino/aliyungo/ess/configuration.go +++ b/vendor/github.com/denverdino/aliyungo/ess/configuration.go @@ -3,6 +3,7 @@ package ess import ( "github.com/denverdino/aliyungo/common" "github.com/denverdino/aliyungo/ecs" + "encoding/base64" ) type CreateScalingConfigurationArgs struct { @@ -18,6 +19,10 @@ type CreateScalingConfigurationArgs struct { SystemDisk_Category common.UnderlineString SystemDisk_Size common.UnderlineString DataDisk []DataDiskType + UserData string + KeyPairName string + RamRoleName string + Tags string } type DataDiskType struct { @@ -36,6 +41,10 @@ type CreateScalingConfigurationResponse struct { // // You can read doc at https://help.aliyun.com/document_detail/25944.html?spm=5176.doc25942.6.625.KcE5ir func (client *Client) CreateScalingConfiguration(args *CreateScalingConfigurationArgs) (resp *CreateScalingConfigurationResponse, err error) { + if args.UserData != "" { + // Encode to base64 string + args.UserData = base64.StdEncoding.EncodeToString([]byte(args.UserData)) + } response := CreateScalingConfigurationResponse{} err = client.InvokeByFlattenMethod("CreateScalingConfiguration", args, &response) @@ -60,6 +69,10 @@ type DescribeScalingConfigurationsResponse struct { ScalingConfiguration []ScalingConfigurationItemType } } +type TagItemType struct { + Key string + Value string +} type ScalingConfigurationItemType struct { ScalingConfigurationId string @@ -78,6 +91,12 @@ type ScalingConfigurationItemType struct { DataDisks struct { DataDisk []DataDiskItemType } + KeyPairName string + RamRoleName string + UserData string + Tags struct { + Tag []TagItemType + } } type DataDiskItemType struct { diff --git a/vendor/github.com/denverdino/aliyungo/ess/group.go b/vendor/github.com/denverdino/aliyungo/ess/group.go index 4441beb5ef9..709601d7938 100644 --- a/vendor/github.com/denverdino/aliyungo/ess/group.go +++ b/vendor/github.com/denverdino/aliyungo/ess/group.go @@ -6,7 +6,7 @@ type LifecycleState string const ( Active = LifecycleState("Active") - Inacitve = LifecycleState("Inacitve") + Inacitve = LifecycleState("Inactive") Deleting = LifecycleState("Deleting") InService = LifecycleState("InService") Pending = LifecycleState("Pending") @@ -19,9 +19,10 @@ type CreateScalingGroupArgs struct { LoadBalancerId string VpcId string VSwitchId string - MaxSize int - MinSize int - DefaultCooldown int + // NOTE: Set MinSize, MaxSize and DefaultCooldown type to int pointer to distinguish zero value from unset value. + MinSize *int + MaxSize *int + DefaultCooldown *int RemovalPolicy common.FlattenArray DBInstanceId common.FlattenArray } @@ -48,10 +49,11 @@ type ModifyScalingGroupArgs struct { ScalingGroupId string ScalingGroupName string ActiveScalingConfigurationId string - MinSize int - MaxSize int - DefaultCooldown int - RemovalPolicy common.FlattenArray + // NOTE: Set MinSize/MaxSize type to int pointer to distinguish zero value from unset value. + MinSize *int + MaxSize *int + DefaultCooldown *int + RemovalPolicy common.FlattenArray } type ModifyScalingGroupResponse struct { @@ -251,6 +253,16 @@ type AttachInstancesResponse struct { ScalingActivityId string } +type RemoveInstancesArgs struct { + ScalingGroupId string + InstanceId common.FlattenArray +} + +type RemoveInstancesResponse struct { + common.Response + ScalingActivityId string +} + // AttachInstances attach instances to scaling group // // You can read doc at https://help.aliyun.com/document_detail/25954.html?spm=5176.product25855.6.633.y5gmzX @@ -267,12 +279,12 @@ func (client *Client) AttachInstances(args *AttachInstancesArgs) (resp *AttachIn // RemoveInstances detach instances from scaling group // // You can read doc at https://help.aliyun.com/document_detail/25955.html?spm=5176.doc25954.6.634.GtpzuJ -func (client *Client) RemoveInstances(args *AttachInstancesArgs) (resp *AttachInstancesResponse, err error) { - response := AttachInstancesResponse{} +func (client *Client) RemoveInstances(args *RemoveInstancesArgs) (resp *RemoveInstancesResponse, err error) { + response := RemoveInstancesResponse{} err = client.InvokeByFlattenMethod("RemoveInstances", args, &response) if err != nil { return nil, err } return &response, nil -} \ No newline at end of file +} diff --git a/vendor/github.com/denverdino/aliyungo/ess/rule.go b/vendor/github.com/denverdino/aliyungo/ess/rule.go index b6ce2900243..399ec1ae7c1 100644 --- a/vendor/github.com/denverdino/aliyungo/ess/rule.go +++ b/vendor/github.com/denverdino/aliyungo/ess/rule.go @@ -128,3 +128,25 @@ func (client *Client) DeleteScalingRule(args *DeleteScalingRuleArgs) (resp *Dele } return &response, nil } + +type ExecuteScalingRuleArgs struct { + ScalingRuleAri string + ClientToken string +} + +type ExecuteScalingRuleResponse struct { + common.Response + ScalingActivityId string +} + +// ExecuteScalingRule execute scaling rule +// +// You can read doc at https://help.aliyun.com/document_detail/25953.html?spm=5176.doc25961.6.632.7sXDx6 +func (client *Client) ExecuteScalingRule(args *ExecuteScalingRuleArgs) (*ExecuteScalingRuleResponse, error) { + resp := ExecuteScalingRuleResponse{} + err := client.InvokeByFlattenMethod("ExecuteScalingRule", args, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ram/client.go b/vendor/github.com/denverdino/aliyungo/ram/client.go index 974bc023f95..2a29bd804eb 100644 --- a/vendor/github.com/denverdino/aliyungo/ram/client.go +++ b/vendor/github.com/denverdino/aliyungo/ram/client.go @@ -1,8 +1,9 @@ package ram import ( - "github.com/denverdino/aliyungo/common" "os" + + "github.com/denverdino/aliyungo/common" ) const ( @@ -16,15 +17,29 @@ type RamClient struct { } func NewClient(accessKeyId string, accessKeySecret string) RamClientInterface { + return NewClientWithSecurityToken(accessKeyId, accessKeySecret, "") +} + +func NewClientWithSecurityToken(accessKeyId string, accessKeySecret string, securityToken string) RamClientInterface { endpoint := os.Getenv("RAM_ENDPOINT") if endpoint == "" { endpoint = RAMDefaultEndpoint } - return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret) + + return NewClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken) } func NewClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string) RamClientInterface { + return NewClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, "") +} + +func NewClientWithEndpointAndSecurityToken(endpoint string, accessKeyId string, accessKeySecret string, securityToken string) RamClientInterface { client := &RamClient{} - client.Init(endpoint, RAMAPIVersion, accessKeyId, accessKeySecret) + client.WithEndpoint(endpoint). + WithVersion(RAMAPIVersion). + WithAccessKeyId(accessKeyId). + WithAccessKeySecret(accessKeySecret). + WithSecurityToken(securityToken). + InitClient() return client } diff --git a/vendor/github.com/denverdino/aliyungo/rds/instances.go b/vendor/github.com/denverdino/aliyungo/rds/instances.go index f73e4e58e3b..55b13ba57de 100644 --- a/vendor/github.com/denverdino/aliyungo/rds/instances.go +++ b/vendor/github.com/denverdino/aliyungo/rds/instances.go @@ -14,8 +14,10 @@ type DBInstanceIPArray struct { // ref: https://help.aliyun.com/document_detail/26242.html type ModifySecurityIpsArgs struct { - DBInstanceId string - DBInstanceIPArray + DBInstanceId string + SecurityIps string + DBInstanceIPArrayName string + DBInstanceIPArrayAttribute string } func (client *Client) ModifySecurityIps(args *ModifySecurityIpsArgs) (resp common.Response, err error) { @@ -229,7 +231,6 @@ type DBInstanceAttribute struct { VpcId string } - type ReadOnlyDBInstanceIds struct { ReadOnlyDBInstanceId []ReadOnlyDBInstanceId } diff --git a/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go b/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go index d090570cee4..e30fdb53497 100644 --- a/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go +++ b/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go @@ -27,6 +27,8 @@ type CreateLoadBalancerArgs struct { InternetChargeType InternetChargeType Bandwidth int ClientToken string + MasterZoneId string + SlaveZoneId string } type CreateLoadBalancerResponse struct { diff --git a/vendor/github.com/denverdino/aliyungo/util/encoding.go b/vendor/github.com/denverdino/aliyungo/util/encoding.go index 8cb58828803..99a508f5b46 100644 --- a/vendor/github.com/denverdino/aliyungo/util/encoding.go +++ b/vendor/github.com/denverdino/aliyungo/util/encoding.go @@ -66,24 +66,26 @@ func setQueryValues(i interface{}, values *url.Values, prefix string) { // TODO Use Tag for validation // tag := typ.Field(i).Tag.Get("tagname") kind := field.Kind() + isPtr := false if (kind == reflect.Ptr || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map || kind == reflect.Chan) && field.IsNil() { continue } if kind == reflect.Ptr { field = field.Elem() kind = field.Kind() + isPtr = true } var value string //switch field.Interface().(type) { switch kind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: i := field.Int() - if i != 0 { + if i != 0 || isPtr { value = strconv.FormatInt(i, 10) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: i := field.Uint() - if i != 0 { + if i != 0 || isPtr { value = strconv.FormatUint(i, 10) } case reflect.Float32: @@ -197,12 +199,14 @@ func setQueryValuesByFlattenMethod(i interface{}, values *url.Values, prefix str // tag := typ.Field(i).Tag.Get("tagname") kind := field.Kind() + isPtr := false if (kind == reflect.Ptr || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map || kind == reflect.Chan) && field.IsNil() { continue } if kind == reflect.Ptr { field = field.Elem() kind = field.Kind() + isPtr = true } var value string @@ -210,12 +214,12 @@ func setQueryValuesByFlattenMethod(i interface{}, values *url.Values, prefix str switch kind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: i := field.Int() - if i != 0 { + if i != 0 || isPtr { value = strconv.FormatInt(i, 10) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: i := field.Uint() - if i != 0 { + if i != 0 || isPtr { value = strconv.FormatUint(i, 10) } case reflect.Float32: diff --git a/vendor/vendor.json b/vendor/vendor.json index 15fde1038ce..82c6b6c9f48 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -235,62 +235,68 @@ { "checksumSHA1": "9JK6TQ6tSG68roB1KWs3NIxw6zY=", "path": "github.com/denverdino/aliyungo/cdn", - "revision": "199fa376135951b938a53634048bb32e5dc823c1", - "revisionTime": "2017-10-16T07:52:46Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "b2m7aICkBOz9JqmnX2kdDN4wgjI=", + "checksumSHA1": "qvFutFkayxTxJN3E1AENo4kHEzA=", "path": "github.com/denverdino/aliyungo/common", - "revision": "199fa376135951b938a53634048bb32e5dc823c1", - "revisionTime": "2017-10-16T07:52:46Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" + }, + { + "checksumSHA1": "iINyIl3tdnJ1149OjjDIYM8o7gk=", + "path": "github.com/denverdino/aliyungo/cs", + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { "checksumSHA1": "zHBzMaiaG1TbhrU15Wp/7Rbl4ZY=", "path": "github.com/denverdino/aliyungo/dns", - "revision": "199fa376135951b938a53634048bb32e5dc823c1", - "revisionTime": "2017-10-16T07:52:46Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "t+oOAxTaWYS0iZVry7iaFgYX5Eo=", + "checksumSHA1": "dRCnl3Jccqs69IUO8s90ZUez/Vc=", "path": "github.com/denverdino/aliyungo/ecs", - "revision": "199fa376135951b938a53634048bb32e5dc823c1", - "revisionTime": "2017-10-16T07:52:46Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "P9SMbUnwF+sBLZc1BspupVholKc=", + "checksumSHA1": "0qcm0qCFWDyeYjG+y596rgwEcQ4=", "path": "github.com/denverdino/aliyungo/ess", - "revision": "199fa376135951b938a53634048bb32e5dc823c1", - "revisionTime": "2017-10-16T07:52:46Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { "checksumSHA1": "CPuR3s7LzQkT57Z20zMHXQM2MYQ=", "path": "github.com/denverdino/aliyungo/location", - "revision": "199fa376135951b938a53634048bb32e5dc823c1", - "revisionTime": "2017-10-16T07:52:46Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "Cc/BNEn/OKuBLhztBhg00YI23Zo=", + "checksumSHA1": "sievsBvgtVF2iZ2FjmDZppH3+Ro=", "path": "github.com/denverdino/aliyungo/ram", - "revision": "199fa376135951b938a53634048bb32e5dc823c1", - "revisionTime": "2017-10-16T07:52:46Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "EaiPS3gTXG0g4DZGeTB07SARwr0=", + "checksumSHA1": "5muc4YGlXvIK6BwWNXQppIEcEkg=", "path": "github.com/denverdino/aliyungo/rds", - "revision": "199fa376135951b938a53634048bb32e5dc823c1", - "revisionTime": "2017-10-16T07:52:46Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "pQHH9wpyS0e4wpW0erxe3D7OILM=", + "checksumSHA1": "lCoboKg3pzrdnhtP+mcu6F48+UY=", "path": "github.com/denverdino/aliyungo/slb", - "revision": "199fa376135951b938a53634048bb32e5dc823c1", - "revisionTime": "2017-10-16T07:52:46Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "piZlmhWPLGxYkXLysTrjcXllO4c=", + "checksumSHA1": "cKVBRn7GKT+0IqfGUc/NnKDWzCw=", "path": "github.com/denverdino/aliyungo/util", - "revision": "199fa376135951b938a53634048bb32e5dc823c1", - "revisionTime": "2017-10-16T07:52:46Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { "checksumSHA1": "BCv50o5pDkoSG3vYKOSai1Z8p3w=", diff --git a/website/alicloud.erb b/website/alicloud.erb index f324c6db5cd..d417d3123e1 100644 --- a/website/alicloud.erb +++ b/website/alicloud.erb @@ -10,7 +10,7 @@ Alicloud Provider - > + > Data Sources - > + > ECS Resources @@ -94,6 +118,9 @@ > alicloud_nat_gateway + > + alicloud_router_interface + > alicloud_forward_entry @@ -144,8 +171,17 @@ - - + > + RAM Resources + + diff --git a/website/docs/d/ram_policies.html.markdown b/website/docs/d/ram_policies.html.markdown new file mode 100644 index 00000000000..5552262857c --- /dev/null +++ b/website/docs/d/ram_policies.html.markdown @@ -0,0 +1,47 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_ram_policies" +sidebar_current: "docs-alicloud-datasource-ram-policies" +description: |- + Provides a list of ram policies available to the user. +--- + +# alicloud\_ram\_policies + +The Ram Policies data source provides a list of Alicloud Ram Policies in an Alicloud account according to the specified filters. + +## Example Usage + +``` +data "alicloud_ram_policies" "policy" { + output_file = "policies.txt" + user_name = "user1" + group_name = "group1" + type = "System" +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `name_regex` - (Optional) A regex string to apply to the policy list returned by Alicloud. +* `type` - (Optional) Limit search to specific the policy type. Valid items are `Custom` and `System`. +* `user_name` - (Optional) Limit search to specific the user name. Found the policies which attached with the specified user. +* `group_name` - (Optional) Limit search to specific the group name. Found the policies which attached with the specified group. +* `role_name` - (Optional) Limit search to specific the role name. Found the policies which attached with the specified role. +* `output_file` - (Optional) The name of file that can save policies data source after running `terraform plan`. + +## Attributes Reference + +A list of policies will be exported and its every element contains the following attributes: + +* `name` - Name of the policy. +* `type` - Type of the policy. +* `description` - Description of the policy. +* `default_version` - Default version of the policy. +* `create_date` - Create date of the policy. +* `update_date` - Update date of the policy. +* `attachment_count` - Attachment count of the policy. +* `document` - Policy document of the policy. \ No newline at end of file diff --git a/website/docs/r/ram_policy.html.markdown b/website/docs/r/ram_policy.html.markdown new file mode 100644 index 00000000000..877206b6a41 --- /dev/null +++ b/website/docs/r/ram_policy.html.markdown @@ -0,0 +1,63 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_ram_policy" +sidebar_current: "docs-alicloud-resource-ram-policy" +description: |- + Provides a RAM Policy resource. +--- + +# alicloud\_ram\_policy + +Provides a RAM Policy resource. + +~> **NOTE:** When you want to destroy this resource forcefully(means remove all the relationships associated with it automatically and then destroy it) without set `force` with `true` at beginning, you need add `force = true` to configuration file and run `terraform plan`, then you can delete resource forcefully. + +## Example Usage + +``` +# Create a new RAM Policy. +resource "alicloud_ram_policy" "policy" { + name = "test_policy" + statement = [ + { + effect = "Allow" + action = [ + "oss:ListObjects", + "oss:GetObject" + ] + resource = [ + "acs:oss:*:*:mybucket", + "acs:oss:*:*:mybucket/*" + ] + } + ] + description = "this is a policy test" + force = true +} +``` +## Argument Reference + +The following arguments are supported: + +* `name` - (Required, Forces new resource) Name of the RAM policy. This name can have a string of 1 to 128 characters, must contain only alphanumeric characters or hyphen "-", and must not begin with a hyphen. +* `statement` - (Optional, Type: list, Conflicts with `document`) Statements of the RAM policy document. It is required when the `document` is not specified. + * `resource` - (Required, Type: list) List of specific objects which will be authorized. The format of each item in this list is `acs:${service}:${region}:${account_id}:${relative_id}`, such as `acs:ecs:*:*:instance/inst-002` and `acs:oss:*:1234567890000:mybucket`. The `${service}` can be `ecs`, `oss`, `ots` and so on, the `${region}` is the region info which can use `*` replace when it is not supplied, the `${account_id}` refers to someone's Alicloud account id or you can use `*` to replace, the `${relative_id}` is the resource description section which related to the `${service}`. + * `action` - (Required, Type: list) List of operations for the `resource`. The format of each item in this list is `${service}:${action_name}`, such as `oss:ListBuckets` and `ecs:Describe*`. The `${service}` can be `ecs`, `oss`, `ots` and so on, the `${action_name}` refers to the name of an api interface which related to the `${service}`. + * `effect` - (Required) This parameter indicates whether or not the `action` is allowed. Valid values are `Allow` and `Deny`. +* `version` - (Optional, Conflicts with `document`) Version of the RAM policy document. Valid value is `1`. Default value is `1`. +* `document` - (Optional, Conflicts with `statement` and `version`) Document of the RAM policy. It is required when the `statement` is not specified. +* `description` - (Optional, Forces new resource) Description of the RAM policy. This name can have a string of 1 to 1024 characters. +* `force` - (Optional) This parameter is used for resource destroy. Default value is `false`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The policy ID. +* `name` - The policy name. +* `type` - The policy type. +* `description` - The policy description. +* `statement` - List of statement of the policy document. +* `document` - The policy document. +* `version` - The policy document version. +* `attachment_count` - The policy attachment count. \ No newline at end of file diff --git a/website/docs/r/ram_user_policy_attachment.html.markdown b/website/docs/r/ram_user_policy_attachment.html.markdown new file mode 100644 index 00000000000..8fcf492e2ca --- /dev/null +++ b/website/docs/r/ram_user_policy_attachment.html.markdown @@ -0,0 +1,63 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_ram_user_policy_attachment" +sidebar_current: "docs-alicloud-resource-ram-user-policy-attachment" +description: |- + Provides a RAM User Policy attachment resource. +--- + +# alicloud\_ram\_user\_policy\_attachment + +Provides a RAM User Policy attachment resource. + +## Example Usage + +``` +# Create a RAM User Policy attachment. +resource "alicloud_ram_user" "user" { + name = "user_test" + display_name = "user_display_name" + mobile = "86-18688888888" + email = "hello.uuu@aaa.com" + comments = "yoyoyo" + force = true +} + +resource "alicloud_ram_policy" "policy" { + name = "test_policy" + statement = [ + { + effect = "Allow" + action = [ + "oss:ListObjects", + "oss:GetObject"] + resource = [ + "acs:oss:*:*:mybucket", + "acs:oss:*:*:mybucket/*"] + }] + description = "this is a policy test" + force = true +} + +resource "alicloud_ram_user_policy_attachment" "attach" { + policy_name = "${alicloud_ram_policy.policy.name}" + policy_type = "${alicloud_ram_policy.policy.type}" + user_name = "${alicloud_ram_user.user.name}" +} +``` +## Argument Reference + +The following arguments are supported: + +* `user_name` - (Required, Forces new resource) Name of the RAM user. This name can have a string of 1 to 64 characters, must contain only alphanumeric characters or hyphens, such as "-",".","_", and must not begin with a hyphen. +* `policy_name` - (Required, Forces new resource) Name of the RAM policy. This name can have a string of 1 to 128 characters, must contain only alphanumeric characters or hyphen "-", and must not begin with a hyphen. +* `policy_type` - (Required, Forces new resource) Type of the RAM policy. It must be `Custom` or `System`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The attachment ID. +* `user_name` - The user name. +* `policy_name` - The policy name. +* `policy_type` - The policy type. \ No newline at end of file