diff --git a/CHANGELOG.md b/CHANGELOG.md index 871a6342e2d..de1e1d354d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ IMPROVEMENTS: +- *New Resource*: _alicloud_cdn_domain_ [GH-52] - *New Resource*: _alicloud_dns_ [GH-51] - *New Resource*: _alicloud_dns_group_ [GH-51] - *New Resource*: _alicloud_dns_record_ [GH-51] diff --git a/alicloud/provider.go b/alicloud/provider.go index 6c27de867f1..717eb0db75e 100644 --- a/alicloud/provider.go +++ b/alicloud/provider.go @@ -98,6 +98,7 @@ func Provider() terraform.ResourceProvider { "alicloud_ram_role_policy_attachment": resourceAlicloudRamRolePolicyAttachment(), "alicloud_ram_group_policy_attachment": resourceAlicloudRamGroupPolicyAtatchment(), "alicloud_container_cluster": resourceAlicloudContainerCluster(), + "alicloud_cdn_domain": resourceAlicloudCdnDomain(), "alicloud_router_interface": resourceAlicloudRouterInterface(), }, diff --git a/alicloud/resource_alicloud_cdn_domain.go b/alicloud/resource_alicloud_cdn_domain.go new file mode 100644 index 00000000000..bab13c8d723 --- /dev/null +++ b/alicloud/resource_alicloud_cdn_domain.go @@ -0,0 +1,755 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/cdn" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "strconv" + "strings" + "time" +) + +func resourceAlicloudCdnDomain() *schema.Resource { + return &schema.Resource{ + Create: resourceAlicloudCdnDomainCreate, + Read: resourceAlicloudCdnDomainRead, + Update: resourceAlicloudCdnDomainUpdate, + Delete: resourceAlicloudCdnDomainDelete, + + Schema: map[string]*schema.Schema{ + "domain_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: validateDomainName, + }, + "cdn_type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: validateCdnType, + }, + "source_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCdnSourceType, + }, + "source_port": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 80, + ValidateFunc: validateCdnSourcePort, + }, + "sources": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + MaxItems: 20, + }, + "scope": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCdnScope, + }, + + // configs + "optimize_enable": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "off", + ValidateFunc: validateCdnEnable, + }, + "page_compress_enable": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "off", + ValidateFunc: validateCdnEnable, + }, + "range_enable": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "off", + ValidateFunc: validateCdnEnable, + }, + "video_seek_enable": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "off", + ValidateFunc: validateCdnEnable, + }, + "block_ips": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "parameter_filter_config": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "off", + ValidateFunc: validateCdnEnable, + }, + "hash_key_args": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateCdnHashKeyArg, + }, + MaxItems: 10, + }, + }, + }, + MaxItems: 1, + }, + + "page_404_config": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "page_type": { + Type: schema.TypeString, + Optional: true, + Default: "default", + ValidateFunc: validateCdnPage404Type, + }, + "custom_page_url": { + Type: schema.TypeString, + Optional: true, + }, + "error_code": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + MaxItems: 1, + }, + + "refer_config": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "refer_type": { + Type: schema.TypeString, + Optional: true, + Default: "block", + ValidateFunc: validateCdnReferType, + }, + "refer_list": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "allow_empty": { + Type: schema.TypeString, + Optional: true, + Default: "on", + ValidateFunc: validateCdnEnable, + }, + }, + }, + MaxItems: 1, + }, + + "auth_config": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "auth_type": { + Type: schema.TypeString, + Optional: true, + Default: "no_auth", + ValidateFunc: validateCdnAuthType, + }, + "master_key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateCdnAuthKey, + }, + "slave_key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateCdnAuthKey, + }, + "timeout": { + Type: schema.TypeInt, + Optional: true, + Default: 1800, + }, + }, + }, + MaxItems: 1, + }, + + "http_header_config": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_key": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: validateCdnHttpHeader, + }, + "header_value": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "header_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + MaxItems: 10, + }, + + "cache_config": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cache_content": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "ttl": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + "cache_type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: validateCacheType, + }, + "weight": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 1, + ValidateFunc: validateIntegerInRange(1, 99), + }, + "cache_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func resourceAlicloudCdnDomainCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).cdnconn + + args := cdn.AddDomainRequest{ + DomainName: d.Get("domain_name").(string), + CdnType: d.Get("cdn_type").(string), + SourcePort: d.Get("source_port").(int), + } + + if v, ok := d.GetOk("scope"); ok { + args.Scope = v.(string) + } + + if args.CdnType != cdn.LiveStream { + if v, ok := d.GetOk("sources"); ok && v.(*schema.Set).Len() > 0 { + sources := expandStringList(v.(*schema.Set).List()) + args.Sources = strings.Join(sources, ",") + } else { + return fmt.Errorf("Sources is required when 'cdn_type' is not 'liveStream'.") + } + + if v, ok := d.GetOk("source_type"); ok && v.(string) != "" { + args.SourceType = v.(string) + } else { + return fmt.Errorf("SourceType is required when 'cdn_type' is not 'liveStream'.") + } + } + _, err := conn.AddCdnDomain(args) + if err != nil { + return fmt.Errorf("AddCdnDomain got an error: %#v", err) + } + + d.SetId(args.DomainName) + return resourceAlicloudCdnDomainUpdate(d, meta) +} + +func resourceAlicloudCdnDomainUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).cdnconn + + d.Partial(true) + + args := cdn.ModifyDomainRequest{ + DomainName: d.Id(), + SourceType: d.Get("source_type").(string), + } + + if !d.IsNewResource() { + attributeUpdate := false + if d.HasChange("source_type") { + d.SetPartial("source_type") + attributeUpdate = true + } + if d.HasChange("sources") { + d.SetPartial("sources") + sources := expandStringList(d.Get("sources").(*schema.Set).List()) + args.Sources = strings.Join(sources, ",") + attributeUpdate = true + } + if d.HasChange("source_port") { + d.SetPartial("source_port") + args.SourcePort = d.Get("source_port").(int) + attributeUpdate = true + } + if attributeUpdate { + _, err := conn.ModifyCdnDomain(args) + if err != nil { + return fmt.Errorf("ModifyCdnDomain got an error: %#v", err) + } + } + } + + // set optimize_enable 、range_enable、page_compress_enable and video_seek_enable + if err := enableConfigUpdate(conn, d); err != nil { + return err + } + + if d.HasChange("block_ips") { + d.SetPartial("block_ips") + blockIps := expandStringList(d.Get("block_ips").(*schema.Set).List()) + args := cdn.IpBlackRequest{DomainName: d.Id(), BlockIps: strings.Join(blockIps, ",")} + if _, err := conn.SetIpBlackListConfig(args); err != nil { + return err + } + } + + if d.HasChange("parameter_filter_config") { + if err := queryStringConfigUpdate(conn, d); err != nil { + return err + } + } + + if d.HasChange("page_404_config") { + if err := page404ConfigUpdate(conn, d); err != nil { + return err + } + } + + if d.HasChange("refer_config") { + if err := referConfigUpdate(conn, d); err != nil { + return err + } + } + + if d.HasChange("auth_config") { + if err := authConfigUpdate(conn, d); err != nil { + return err + } + } + + if d.HasChange("http_header_config") { + if err := httpHeaderConfigUpdate(conn, d); err != nil { + return err + } + } + + if d.HasChange("cache_config") { + if err := cacheConfigUpdate(conn, d); err != nil { + return err + } + } + + d.Partial(false) + return resourceAlicloudCdnDomainRead(d, meta) +} + +func resourceAlicloudCdnDomainRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).cdnconn + + args := cdn.DescribeDomainRequest{ + DomainName: d.Id(), + } + response, err := conn.DescribeCdnDomainDetail(args) + if err != nil { + return fmt.Errorf("DescribeCdnDomainDetail got an error: %#v", err) + } + + domain := response.GetDomainDetailModel + d.Set("domain_name", domain.DomainName) + d.Set("sources", domain.Sources.Source) + d.Set("cdn_type", domain.CdnType) + d.Set("source_type", domain.SourceType) + d.Set("scope", domain.Scope) + + // get domain configs + describeConfigArgs := cdn.DomainConfigRequest{ + DomainName: d.Id(), + } + resp, err := conn.DescribeDomainConfigs(describeConfigArgs) + if err != nil { + return fmt.Errorf("DescribeDomainConfigs got an error: %#v", err) + } + configs := resp.DomainConfigs + + queryStringConfig := configs.IgnoreQueryStringConfig + if _, ok := d.GetOk("parameter_filter_config"); ok { + config := make([]map[string]interface{}, 1) + config[0] = map[string]interface{}{ + "enable": queryStringConfig.Enable, + "hash_key_args": strings.Split(queryStringConfig.HashKeyArgs, ","), + } + d.Set("parameter_filter_config", config) + } + + errorPageConfig := configs.ErrorPageConfig + if _, ok := d.GetOk("page_404_config"); ok { + config := make([]map[string]interface{}, 1) + config[0] = map[string]interface{}{ + "page_type": errorPageConfig.PageType, + "error_code": errorPageConfig.ErrorCode, + "custom_page_url": errorPageConfig.CustomPageUrl, + } + if errorPageConfig.PageType == "" { + config[0]["page_type"] = "default" + } + d.Set("page_404_config", config) + } + + referConfig := configs.RefererConfig + if _, ok := d.GetOk("refer_config"); ok { + config := make([]map[string]interface{}, 1) + config[0] = map[string]interface{}{ + "refer_type": referConfig.ReferType, + "refer_list": strings.Split(referConfig.ReferList, ","), + "allow_empty": referConfig.AllowEmpty, + } + d.Set("refer_config", config) + } + + authConfig := configs.ReqAuthConfig + if _, ok := d.GetOk("auth_config"); ok { + config := make([]map[string]interface{}, 1) + timeout, _ := strconv.Atoi(authConfig.TimeOut) + config[0] = map[string]interface{}{ + "auth_type": authConfig.AuthType, + "master_key": authConfig.Key1, + "slave_key": authConfig.Key2, + "timeout": timeout, + } + d.Set("auth_config", config) + } + + headerConfigs := configs.HttpHeaderConfigs.HttpHeaderConfig + httpHeaderConfigs := make([]map[string]interface{}, 0, len(headerConfigs)) + for _, v := range headerConfigs { + val := make(map[string]interface{}) + val["header_key"] = v.HeaderKey + val["header_value"] = v.HeaderValue + val["header_id"] = v.ConfigId + httpHeaderConfigs = append(httpHeaderConfigs, val) + } + d.Set("http_header_config", httpHeaderConfigs) + + cacheConfigs := configs.CacheExpiredConfigs.CacheExpiredConfig + cacheExpiredConfigs := make([]map[string]interface{}, 0, len(cacheConfigs)) + for _, v := range cacheConfigs { + val := make(map[string]interface{}) + ttl, _ := strconv.Atoi(v.TTL) + weight, _ := strconv.Atoi(v.Weight) + val["cache_type"] = v.CacheType + val["cache_content"] = v.CacheContent + val["cache_id"] = v.ConfigId + val["weight"] = weight + val["ttl"] = ttl + cacheExpiredConfigs = append(cacheExpiredConfigs, val) + } + d.Set("cache_config", cacheExpiredConfigs) + + d.Set("optimize_enable", configs.OptimizeConfig.Enable) + d.Set("page_compress_enable", configs.PageCompressConfig.Enable) + d.Set("range_enable", configs.RangeConfig.Enable) + d.Set("video_seek_enable", configs.VideoSeekConfig.Enable) + d.Set("block_ips", strings.Split(configs.CcConfig.BlockIps, ",")) + + return nil +} + +func resourceAlicloudCdnDomainDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).cdnconn + + args := cdn.DescribeDomainRequest{ + DomainName: d.Id(), + } + return resource.Retry(5*time.Minute, func() *resource.RetryError { + if _, err := conn.DeleteCdnDomain(args); err != nil { + if IsExceptedError(err, ServiceBusy) { + return resource.RetryableError(fmt.Errorf("The specified Domain is configuring, please retry later.")) + } + return resource.NonRetryableError(fmt.Errorf("Error deleting cdn domain %s: %#v.", d.Id(), err)) + } + return nil + }) +} + +func enableConfigUpdate(conn *cdn.CdnClient, d *schema.ResourceData) error { + type configFunc func(req cdn.ConfigRequest) (cdn.CdnCommonResponse, error) + + relation := map[string]configFunc{ + "optimize_enable": conn.SetOptimizeConfig, + "range_enable": conn.SetRangeConfig, + "page_compress_enable": conn.SetPageCompressConfig, + "video_seek_enable": conn.SetVideoSeekConfig, + } + + for key, fn := range relation { + if d.HasChange(key) { + d.SetPartial(key) + args := cdn.ConfigRequest{ + DomainName: d.Id(), + Enable: d.Get(key).(string), + } + if _, err := fn(args); err != nil { + return err + } + } + } + return nil +} + +func queryStringConfigUpdate(conn *cdn.CdnClient, d *schema.ResourceData) error { + valSet := d.Get("parameter_filter_config").(*schema.Set) + args := cdn.QueryStringConfigRequest{DomainName: d.Id()} + + if valSet == nil || valSet.Len() == 0 { + args.Enable = "off" + if _, err := conn.SetIgnoreQueryStringConfig(args); err != nil { + return err + } + return nil + } + + val := valSet.List()[0].(map[string]interface{}) + d.SetPartial("parameter_filter_config") + args.Enable = val["enable"].(string) + if v, ok := val["hash_key_args"]; ok && len(v.([]interface{})) > 0 { + hashKeyArgs := expandStringList(v.([]interface{})) + args.HashKeyArgs = strings.Join(hashKeyArgs, ",") + } + if _, err := conn.SetIgnoreQueryStringConfig(args); err != nil { + return err + } + return nil +} + +func page404ConfigUpdate(conn *cdn.CdnClient, d *schema.ResourceData) error { + valSet := d.Get("page_404_config").(*schema.Set) + args := cdn.ErrorPageConfigRequest{DomainName: d.Id()} + + if valSet == nil || valSet.Len() == 0 { + args.PageType = "default" + if _, err := conn.SetErrorPageConfig(args); err != nil { + return err + } + return nil + } + + val := valSet.List()[0].(map[string]interface{}) + d.SetPartial("page_404_config") + args.PageType = val["page_type"].(string) + customPageUrl, ok := val["custom_page_url"] + if ok { + args.CustomPageUrl = customPageUrl.(string) + } + + if args.PageType == "charity" && (ok && customPageUrl.(string) != CharityPageUrl || !ok) { + return fmt.Errorf("If 'page_type' value is 'charity', you must set 'custom_page_url' with '%s'.", CharityPageUrl) + } + if args.PageType == "default" && ok && customPageUrl.(string) != "" { + return fmt.Errorf("If 'page_type' value is 'default', you can not set 'custom_page_url'.") + } + if args.PageType == "other" && (!ok || customPageUrl.(string) == "") { + return fmt.Errorf("If 'page_type' value is 'other', you must set the value of 'custom_page_url'.") + } + + if _, err := conn.SetErrorPageConfig(args); err != nil { + return err + } + return nil +} + +func referConfigUpdate(conn *cdn.CdnClient, d *schema.ResourceData) error { + valSet := d.Get("refer_config").(*schema.Set) + args := cdn.ReferConfigRequest{DomainName: d.Id()} + + if valSet == nil || valSet.Len() == 0 { + args.ReferType = "block" + args.AllowEmpty = "on" + if _, err := conn.SetRefererConfig(args); err != nil { + return err + } + return nil + } + + val := valSet.List()[0].(map[string]interface{}) + d.SetPartial("refer_config") + args.ReferType = val["refer_type"].(string) + args.AllowEmpty = val["allow_empty"].(string) + if v, ok := val["refer_list"]; ok && len(v.([]interface{})) > 0 { + referList := expandStringList(v.([]interface{})) + args.ReferList = strings.Join(referList, ",") + } + if _, err := conn.SetRefererConfig(args); err != nil { + return err + } + return nil +} + +func authConfigUpdate(conn *cdn.CdnClient, d *schema.ResourceData) error { + ov, nv := d.GetChange("auth_config") + oldConfig, newConfig := ov.(*schema.Set), nv.(*schema.Set) + args := cdn.ReqAuthConfigRequest{DomainName: d.Id()} + + if newConfig == nil || newConfig.Len() == 0 { + args.AuthType = "no_auth" + if _, err := conn.SetReqAuthConfig(args); err != nil { + return err + } + return nil + } + + val := newConfig.List()[0].(map[string]interface{}) + d.SetPartial("auth_config") + args.AuthType = val["auth_type"].(string) + args.Timeout = strconv.Itoa(val["timeout"].(int)) + + masterKey, okMasterKey := val["master_key"] + slaveKey, okSlaveKey := val["slave_key"] + if okMasterKey { + args.Key1 = masterKey.(string) + } + if okSlaveKey { + args.Key2 = slaveKey.(string) + } + + if args.AuthType == "no_auth" { + if oldConfig == nil || oldConfig.Len() == 0 { + if okMasterKey || okSlaveKey { + return fmt.Errorf("If 'auth_type' value is 'no_auth', you can not set the value of 'master_key' and 'slave_key'.") + } + } else { + oldVal := oldConfig.List()[0].(map[string]interface{}) + if oldVal["master_key"] != val["master_key"] || oldVal["slave_key"] != val["slave_key"] { + return fmt.Errorf("If 'auth_type' value is 'no_auth', you can not change the value of 'master_key' and 'slave_key'.") + } + } + } else { + if !okMasterKey || !okSlaveKey { + return fmt.Errorf("If 'auth_type' value is one of ['type_a', 'type_b', 'type_c'], you must set 'master_key' and 'slave_key' at one time.") + } + } + + if _, err := conn.SetReqAuthConfig(args); err != nil { + return err + } + return nil +} + +func httpHeaderConfigUpdate(conn *cdn.CdnClient, d *schema.ResourceData) error { + ov, nv := d.GetChange("http_header_config") + oldConfigs := ov.(*schema.Set).List() + newConfigs := nv.(*schema.Set).List() + + for _, v := range oldConfigs { + configId := v.(map[string]interface{})["header_id"].(string) + args := cdn.DeleteHttpHeaderConfigRequest{ + DomainName: d.Id(), + ConfigID: configId, + } + if _, err := conn.DeleteHttpHeaderConfig(args); err != nil { + return err + } + } + + if len(newConfigs) == 0 { + return nil + } + + for _, v := range newConfigs { + args := cdn.HttpHeaderConfigRequest{ + DomainName: d.Id(), + HeaderKey: v.(map[string]interface{})["header_key"].(string), + HeaderValue: v.(map[string]interface{})["header_value"].(string), + } + _, err := conn.SetHttpHeaderConfig(args) + if err != nil { + return fmt.Errorf("SetHttpHeaderConfig got an error: %#v", err) + } + } + + return nil +} + +func cacheConfigUpdate(conn *cdn.CdnClient, d *schema.ResourceData) error { + ov, nv := d.GetChange("cache_config") + oldConfigs := ov.(*schema.Set).List() + newConfigs := nv.(*schema.Set).List() + + for _, v := range oldConfigs { + val := v.(map[string]interface{}) + configId := val["cache_id"].(string) + args := cdn.DeleteCacheConfigRequest{ + DomainName: d.Id(), + ConfigID: configId, + CacheType: val["cache_type"].(string), + } + if _, err := conn.DeleteCacheExpiredConfig(args); err != nil { + return fmt.Errorf("DeleteCacheExpiredConfig got an error: %#v", err) + } + } + + if len(newConfigs) == 0 { + return nil + } + + for _, v := range newConfigs { + val := v.(map[string]interface{}) + args := cdn.CacheConfigRequest{ + DomainName: d.Id(), + CacheContent: val["cache_content"].(string), + TTL: strconv.Itoa(val["ttl"].(int)), + Weight: strconv.Itoa(val["weight"].(int)), + } + if err := setCacheExpiredConfig(args, val["cache_type"].(string), conn); err != nil { + return err + } + } + + return nil +} + +func setCacheExpiredConfig(req cdn.CacheConfigRequest, cacheType string, conn *cdn.CdnClient) (err error) { + if cacheType == "suffix" { + _, err = conn.SetFileCacheExpiredConfig(req) + } else { + _, err = conn.SetPathCacheExpiredConfig(req) + } + return +} diff --git a/alicloud/resource_alicloud_cdn_domain_test.go b/alicloud/resource_alicloud_cdn_domain_test.go new file mode 100644 index 00000000000..c4db1a2357b --- /dev/null +++ b/alicloud/resource_alicloud_cdn_domain_test.go @@ -0,0 +1,112 @@ +package alicloud + +import ( + "fmt" + "log" + "testing" + + "github.com/denverdino/aliyungo/cdn" + "github.com/denverdino/aliyungo/common" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAlicloudCdnDomain_basic(t *testing.T) { + var v cdn.DomainDetail + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_cdn_domain.domain", + + Providers: testAccProviders, + CheckDestroy: testAccCheckCdnDomainDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCdnDomainConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckCdnDomainExists( + "alicloud_cdn_domain.domain", &v), + resource.TestCheckResourceAttr( + "alicloud_cdn_domain.domain", + "domain_name", + "aliyun.com"), + ), + }, + }, + }) +} + +func testAccCheckCdnDomainExists(n string, domain *cdn.DomainDetail) 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 Domain ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.cdnconn + + request := cdn.DescribeDomainRequest{ + DomainName: rs.Primary.Attributes["domain_name"], + } + + response, err := conn.DescribeCdnDomainDetail(request) + log.Printf("[WARN] Domain id %#v", rs.Primary.ID) + + if err == nil { + *domain = response.GetDomainDetailModel + return nil + } + return fmt.Errorf("Error finding domain %#v", rs.Primary.ID) + } +} + +func testAccCheckCdnDomainDestroy(s *terraform.State) error { + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_cdn_domain" { + continue + } + + // Try to find the domain + client := testAccProvider.Meta().(*AliyunClient) + conn := client.cdnconn + + request := cdn.DescribeDomainRequest{ + DomainName: rs.Primary.Attributes["domain_name"], + } + + _, err := conn.DescribeCdnDomainDetail(request) + + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == "InvalidDomain.NotFound" { + return nil + } else { + return fmt.Errorf("Error Domain still exist.") + } + } + } + + return nil +} + +const testAccCdnDomainConfig = ` +resource "alicloud_cdn_domain" "domain" { + domain_name = "www.aliyun.com" + cdn_type = "web" + source_type = "domain" + sources = ["jb51.net"] + optimize_enable = "off" + page_compress_enable = "off" + range_enable = "off" + video_seek_enable = "off" +}` diff --git a/examples/cdn/main.tf b/examples/cdn/main.tf new file mode 100644 index 00000000000..afcd01049b0 --- /dev/null +++ b/examples/cdn/main.tf @@ -0,0 +1,60 @@ +resource "alicloud_cdn_domain" "domain" { + domain_name = "${var.domain_name}" + cdn_type = "${var.cdn_type}" + source_type = "${var.source_type}" + sources = "${var.sources}" + + // configs + optimize_enable = "${var.enable}" + page_compress_enable = "${var.enable}" + range_enable = "${var.enable}" + video_seek_enable = "${var.enable}" + block_ips = "${var.block_ips}" + parameter_filter_config = [ + { + enable = "${var.enable}" + hash_key_args = "${var.hash_key_args}" + }] + page_404_config = [ + { + page_type = "${var.page_type}" + custom_page_url = "http://${var.domain_name}/notfound/" + }] + refer_config = [ + { + refer_type = "${var.refer_type}" + refer_list = "${var.refer_list}" + allow_empty = "${var.enable}" + }] + auth_config = [ + { + auth_type = "${var.auth_type}" + master_key = "helloworld1" + slave_key = "helloworld2" + }] + http_header_config = [ + { + header_key = "Content-Type", + header_value = "text/plain" + }, + { + header_key = "Access-Control-Allow-Origin", + header_value = "*" + }] + cache_config = [ + { + cache_content = "/hello/world", + ttl = 1000 + cache_type = "path" + }, + { + cache_content = "/hello/world/youyou", + ttl = 1000 + cache_type = "path" + }, + { + cache_content = "txt,jpg,png", + ttl = 2000 + cache_type = "suffix" + }] +} \ No newline at end of file diff --git a/examples/cdn/outputs.tf b/examples/cdn/outputs.tf new file mode 100644 index 00000000000..91431191657 --- /dev/null +++ b/examples/cdn/outputs.tf @@ -0,0 +1,3 @@ +output "domain" { + value = "${alicloud_cdn_domain.domain.id}" +} \ No newline at end of file diff --git a/examples/cdn/variables.tf b/examples/cdn/variables.tf new file mode 100644 index 00000000000..01f649f679a --- /dev/null +++ b/examples/cdn/variables.tf @@ -0,0 +1,56 @@ +variable "domain_name" { + default = "www.xxxxxx.com" +} + +variable "cdn_type" { + default = "web" +} + +variable "sources" { + type = "list" + default = [ + "xxx.com", + "xxxx.net", + "xxxxx.cn",] +} + +variable "source_type" { + default = "domain" +} + +variable "enable" { + default = "off" +} + +variable "page_type" { + default = "other" +} + +variable "refer_type" { + default = "block" +} + +variable "auth_type" { + default = "type_a" +} + +variable "block_ips" { + type = "list" + default = [ + "1.2.3.4", + "111.222.111.111"] +} + +variable "hash_key_args" { + type = "list" + default = [ + "youyouyou", + "checkitout"] +} + +variable "refer_list" { + type = "list" + default = [ + "www.xxxx.com", + "www.xxxx.net"] +} \ No newline at end of file diff --git a/website/docs/r/cdn_domain.html.markdown b/website/docs/r/cdn_domain.html.markdown new file mode 100644 index 00000000000..f39c770d7ba --- /dev/null +++ b/website/docs/r/cdn_domain.html.markdown @@ -0,0 +1,146 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_cdn_domain" +sidebar_current: "docs-alicloud-resource-cdn-domain" +description: |- + Provides a CDN Accelerated Domain resource. +--- + +# alicloud\_cdn\_domain + +Provides a CDN Accelerated Domain resource. + +## Example Usage + +``` +# Add a CDN Accelerated Domain with configs. +resource "alicloud_cdn_domain" "domain" { + domain_name = "${your_cdn_domain_name}" + cdn_type = "web" + source_type = "domain" + sources = ["${your_cdn_domain_source1}", "${your_cdn_domain_source2}"] + + // configs + optimize_enable = "off" + page_compress_enable = "off" + range_enable = "off" + video_seek_enable = "off" + block_ips = ["1.2.3.4", "111.222.111.111"] + parameter_filter_config = [ + { + enable = "on" + hash_key_args = ["hello", "youyouyou"] + }] + page_404_config = [ + { + page_type = "other" + custom_page_url = "http://${your_cdn_domain_name}/notfound/" + }] + refer_config = [ + { + refer_type = "block" + refer_list = ["www.xxxx.com", "www.xxxx.cn"] + allow_empty = "off" + }] + auth_config = [ + { + auth_type = "type_a" + master_key = "helloworld1" + slave_key = "helloworld2" + }] + http_header_config = [ + { + header_key = "Content-Type", + header_value = "text/plain" + }, + { + header_key = "Access-Control-Allow-Origin", + header_value = "*" + }] + cache_config = [ + { + cache_content = "/hello/world", + ttl = 1000 + cache_type = "path" + }, + { + cache_content = "/hello/world/youyou", + ttl = 1000 + cache_type = "path" + }, + { + cache_content = "txt,jpg,png", + ttl = 2000 + cache_type = "suffix" + }] +} +``` +## Argument Reference + +The following arguments are supported: + +* `domain_name` - (Required) Name of the accelerated domain. This name without suffix can have a string of 1 to 63 characters, must contain only alphanumeric characters or "-", and must not begin or end with "-", and "-" must not in the 3th and 4th character positions at the same time. Suffix `.sh` and `.tel` are not supported. +* `cdn_type` - (Required) Cdn type of the accelerated domain. Valid values are `web`, `download`, `video`, `liveStream`. +* `source_type` - (Optional) Source type of the accelerated domain. Valid values are `ipaddr`, `domain`, `oss`. You must set this parameter when `cdn_type` value is not `liveStream`. +* `source_port` - (Optional) Source port of the accelerated domain. Valid values are `80` and `443`. Default value is `80`. You must use `80` when the `source_type` is `oss`. +* `sources` - (Optional, Type: list) Sources of the accelerated domain. It's a list of domain names or IP address and consists of at most 20 items. You must set this parameter when `cdn_type` value is not `liveStream`. +* `scope` - (Optional) Scope of the accelerated domain. Valid values are `domestic`, `overseas`, `global`. Default value is `domestic`. This parameter's setting is valid Only for the international users and domestic L3 and above users . + +#### Domain config + +The config supports the following: + +* `optimize_enable` - (Optional) Page Optimize config of the accelerated domain. Valid values are `on` and `off`. Default value is `off`. It can effectively remove the page redundant content, reduce the file size and improve the speed of distribution when this parameter value is `on`. +* `page_compress_enable` - (Optional) Page Compress config of the accelerated domain. Valid values are `on` and `off`. Default value is `off`. +* `range_enable` - (Optional) Range Source config of the accelerated domain. Valid values are `on` and `off`. Default value is `off`. +* `video_seek_enable` - (Optional) Video Seek config of the accelerated domain. Valid values are `on` and `off`. Default value is `off`. + +* `parameter_filter_config` - (Optional, Type: set) Parameter filter config of the accelerated domain. It's a set and consists of at most one item. + * `enable` - (Optional) This parameter indicates whether or not the `parameter_filter_config` is enable. Valid values are `on` and `off`. Default value is `off`. + * `hash_key_args` - (Optional, Type: list) Reserved parameters of `parameter_filter_config`. It's a list of string and consists of at most 10 items. + +* `page_404_config` - (Optional, Type: set) Error Page config of the accelerated domain. It's a set and consists of at most one item. + * `page_type` - (Optional) Page type of the error page. Valid values are `default`, `charity`, `other`. Default value is `default`. + * `custom_page_url` - (Optional) Custom page url of the error page. It must be the full path under the accelerated domain name. It's value must be `http://promotion.alicdn.com/help/oss/error.html` when `page_type` value is `charity` and It can not be set when `page_type` value is `default`. + +* `refer_config` - (Optional, Type: set) Refer anti-theft chain config of the accelerated domain. It's a set and consists of at most 1 item. + * `refer_type` - (Optional) Refer type of the refer config. Valid values are `block` and `allow`. Default value is `block`. + * `refer_list` - (Required, Type: list) A list of domain names of the refer config. + * `allow_empty` - (Optional) This parameter indicates whether or not to allow empty refer access. Valid values are `on` and `off`. Default value is `on`. + +* `auth_config` - (Optional, Type: set) Auth config of the accelerated domain. It's a set and consist of at most 1 item. + * `auth_type` - (Optional) Auth type of the auth config. Valid values are `no_auth`, `type_a`, `type_b` and `type_c`. Default value is `no_auth`. + * `master_key` - (Optional) Master authentication key of the auth config. This parameter can have a string of 6 to 32 characters and must contain only alphanumeric characters. + * `slave_key` - (Optional) Slave authentication key of the auth config. This parameter can have a string of 6 to 32 characters and must contain only alphanumeric characters. + * `timeout` - (Optional, Type: int) Authentication cache time of the auth config. Default value is `1800`. It's value is valid only when the `auth_type` is `type_b` or `type_c`. + +* `http_header_config` - (Optional, Type: set) Http header config of the accelerated domain. It's a set and consist of at most 8 items. The `header_key` for each item can not be repeated. + * `header_key` - (Required) Header key of the http header. Valid values are `Content-Type`, `Cache-Control`, `Content-Disposition`, `Content-Language`,`Expires`, `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods` and `Access-Control-Max-Age`. + * `header_value` - (Required) Header value of the http header. + +* `cache_config` - (Optional, Type: set) Cache config of the accelerated domain. It's a set and each item's `cache_content` can not be repeated. + * `cache_type` - (Required) Cache type of the cache config. Valid values are `suffix` and `path`. + * `cache_content` - (Required) Cache content of the cache config. It's value is a path string when the `cache_type` is `path`. When the `cache_type` is `suffix`, it's value is a string which contains multiple file suffixes separated by commas. + * `ttl` - (Required, Type: int) Cache time of the cache config. + * `weight` - (Optional, Type: int) Weight of the cache config. This parameter's value is between 1 and 99. Default value is `1`. The higher the value, the higher the priority + + +## Attributes Reference + +The following attributes are exported: +* `domain_name` - The accelerated domain name. +* `sources` - The accelerated domain sources. +* `cdn_type` - The cdn type of the accelerated domain. +* `source_type` - The source type ot the accelerated domain. +* `scope` - The accelerated domain scope. + +* `optimize_enable` - The page optimize config of the accelerated domain. +* `page_compress_enable` - The page compress config of the accelerated domain. +* `range_enable` - The range source config of the accelerated domain. +* `video_seek_enable` - The video seek config of the accelerated domain. +* `parameter_filter_config` - The parameter filter config of the accelerated domain. +* `page_404_config` - The error page config of the accelerated domain. +* `refer_config` - The refer config of the accelerated domain. +* `auth_config` - The auth config of the accelerated domain. +* `http_header_config` - The http header configs of the accelerated domain. +* `cache_config` - The cache configs of the accelerated domain.