github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/cloudfront_distribution_configuration_structure.go (about) 1 // CloudFront DistributionConfig structure helpers. 2 // 3 // These functions assist in pulling in data from Terraform resource 4 // configuration for the aws_cloudfront_distribution resource, as there are 5 // several sub-fields that require their own data type, and do not necessarily 6 // 1-1 translate to resource configuration. 7 8 package aws 9 10 import ( 11 "bytes" 12 "fmt" 13 "reflect" 14 "sort" 15 "strconv" 16 "time" 17 18 "github.com/aws/aws-sdk-go/aws" 19 "github.com/aws/aws-sdk-go/service/cloudfront" 20 "github.com/hashicorp/terraform/flatmap" 21 "github.com/hashicorp/terraform/helper/hashcode" 22 "github.com/hashicorp/terraform/helper/schema" 23 ) 24 25 // cloudFrontRoute53ZoneID defines the route 53 zone ID for CloudFront. This 26 // is used to set the zone_id attribute. 27 const cloudFrontRoute53ZoneID = "Z2FDTNDATAQYW2" 28 29 // Define Sort interface for []*string so we can ensure the order of 30 // geo_restrictions.locations 31 type StringPtrSlice []*string 32 33 func (p StringPtrSlice) Len() int { return len(p) } 34 func (p StringPtrSlice) Less(i, j int) bool { return *p[i] < *p[j] } 35 func (p StringPtrSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 36 37 // Assemble the *cloudfront.DistributionConfig variable. Calls out to various 38 // expander functions to convert attributes and sub-attributes to the various 39 // complex structures which are necessary to properly build the 40 // DistributionConfig structure. 41 // 42 // Used by the aws_cloudfront_distribution Create and Update functions. 43 func expandDistributionConfig(d *schema.ResourceData) *cloudfront.DistributionConfig { 44 distributionConfig := &cloudfront.DistributionConfig{ 45 CacheBehaviors: expandCacheBehaviors(d.Get("cache_behavior").(*schema.Set)), 46 CustomErrorResponses: expandCustomErrorResponses(d.Get("custom_error_response").(*schema.Set)), 47 DefaultCacheBehavior: expandDefaultCacheBehavior(d.Get("default_cache_behavior").(*schema.Set).List()[0].(map[string]interface{})), 48 Enabled: aws.Bool(d.Get("enabled").(bool)), 49 IsIPV6Enabled: aws.Bool(d.Get("is_ipv6_enabled").(bool)), 50 HttpVersion: aws.String(d.Get("http_version").(string)), 51 Origins: expandOrigins(d.Get("origin").(*schema.Set)), 52 PriceClass: aws.String(d.Get("price_class").(string)), 53 } 54 // This sets CallerReference if it's still pending computation (ie: new resource) 55 if v, ok := d.GetOk("caller_reference"); ok == false { 56 distributionConfig.CallerReference = aws.String(time.Now().Format(time.RFC3339Nano)) 57 } else { 58 distributionConfig.CallerReference = aws.String(v.(string)) 59 } 60 if v, ok := d.GetOk("comment"); ok { 61 distributionConfig.Comment = aws.String(v.(string)) 62 } else { 63 distributionConfig.Comment = aws.String("") 64 } 65 if v, ok := d.GetOk("default_root_object"); ok { 66 distributionConfig.DefaultRootObject = aws.String(v.(string)) 67 } else { 68 distributionConfig.DefaultRootObject = aws.String("") 69 } 70 if v, ok := d.GetOk("logging_config"); ok { 71 distributionConfig.Logging = expandLoggingConfig(v.(*schema.Set).List()[0].(map[string]interface{})) 72 } else { 73 distributionConfig.Logging = expandLoggingConfig(nil) 74 } 75 if v, ok := d.GetOk("aliases"); ok { 76 distributionConfig.Aliases = expandAliases(v.(*schema.Set)) 77 } else { 78 distributionConfig.Aliases = expandAliases(schema.NewSet(aliasesHash, []interface{}{})) 79 } 80 if v, ok := d.GetOk("restrictions"); ok { 81 distributionConfig.Restrictions = expandRestrictions(v.(*schema.Set).List()[0].(map[string]interface{})) 82 } 83 if v, ok := d.GetOk("viewer_certificate"); ok { 84 distributionConfig.ViewerCertificate = expandViewerCertificate(v.(*schema.Set).List()[0].(map[string]interface{})) 85 } 86 if v, ok := d.GetOk("web_acl_id"); ok { 87 distributionConfig.WebACLId = aws.String(v.(string)) 88 } else { 89 distributionConfig.WebACLId = aws.String("") 90 } 91 92 return distributionConfig 93 } 94 95 // Unpack the *cloudfront.DistributionConfig variable and set resource data. 96 // Calls out to flatten functions to convert the DistributionConfig 97 // sub-structures to their respective attributes in the 98 // aws_cloudfront_distribution resource. 99 // 100 // Used by the aws_cloudfront_distribution Read function. 101 func flattenDistributionConfig(d *schema.ResourceData, distributionConfig *cloudfront.DistributionConfig) error { 102 var err error 103 104 d.Set("enabled", distributionConfig.Enabled) 105 d.Set("is_ipv6_enabled", distributionConfig.IsIPV6Enabled) 106 d.Set("price_class", distributionConfig.PriceClass) 107 d.Set("hosted_zone_id", cloudFrontRoute53ZoneID) 108 109 err = d.Set("default_cache_behavior", flattenDefaultCacheBehavior(distributionConfig.DefaultCacheBehavior)) 110 if err != nil { 111 return err 112 } 113 err = d.Set("viewer_certificate", flattenViewerCertificate(distributionConfig.ViewerCertificate)) 114 if err != nil { 115 return err 116 } 117 118 if distributionConfig.CallerReference != nil { 119 d.Set("caller_reference", distributionConfig.CallerReference) 120 } 121 if distributionConfig.Comment != nil { 122 if *distributionConfig.Comment != "" { 123 d.Set("comment", distributionConfig.Comment) 124 } 125 } 126 if distributionConfig.DefaultRootObject != nil { 127 d.Set("default_root_object", distributionConfig.DefaultRootObject) 128 } 129 if distributionConfig.HttpVersion != nil { 130 d.Set("http_version", distributionConfig.HttpVersion) 131 } 132 if distributionConfig.WebACLId != nil { 133 d.Set("web_acl_id", distributionConfig.WebACLId) 134 } 135 136 if distributionConfig.CustomErrorResponses != nil { 137 err = d.Set("custom_error_response", flattenCustomErrorResponses(distributionConfig.CustomErrorResponses)) 138 if err != nil { 139 return err 140 } 141 } 142 if distributionConfig.CacheBehaviors != nil { 143 err = d.Set("cache_behavior", flattenCacheBehaviors(distributionConfig.CacheBehaviors)) 144 if err != nil { 145 return err 146 } 147 } 148 149 if distributionConfig.Logging != nil && *distributionConfig.Logging.Enabled { 150 err = d.Set("logging_config", flattenLoggingConfig(distributionConfig.Logging)) 151 } else { 152 err = d.Set("logging_config", schema.NewSet(loggingConfigHash, []interface{}{})) 153 } 154 if err != nil { 155 return err 156 } 157 158 if distributionConfig.Aliases != nil { 159 err = d.Set("aliases", flattenAliases(distributionConfig.Aliases)) 160 if err != nil { 161 return err 162 } 163 } 164 if distributionConfig.Restrictions != nil { 165 err = d.Set("restrictions", flattenRestrictions(distributionConfig.Restrictions)) 166 if err != nil { 167 return err 168 } 169 } 170 if *distributionConfig.Origins.Quantity > 0 { 171 err = d.Set("origin", flattenOrigins(distributionConfig.Origins)) 172 if err != nil { 173 return err 174 } 175 } 176 177 return nil 178 } 179 180 func expandDefaultCacheBehavior(m map[string]interface{}) *cloudfront.DefaultCacheBehavior { 181 cb := expandCacheBehavior(m) 182 var dcb cloudfront.DefaultCacheBehavior 183 184 simpleCopyStruct(cb, &dcb) 185 return &dcb 186 } 187 188 func flattenDefaultCacheBehavior(dcb *cloudfront.DefaultCacheBehavior) *schema.Set { 189 m := make(map[string]interface{}) 190 var cb cloudfront.CacheBehavior 191 192 simpleCopyStruct(dcb, &cb) 193 m = flattenCacheBehavior(&cb) 194 return schema.NewSet(defaultCacheBehaviorHash, []interface{}{m}) 195 } 196 197 // Assemble the hash for the aws_cloudfront_distribution default_cache_behavior 198 // TypeSet attribute. 199 func defaultCacheBehaviorHash(v interface{}) int { 200 var buf bytes.Buffer 201 m := v.(map[string]interface{}) 202 buf.WriteString(fmt.Sprintf("%t-", m["compress"].(bool))) 203 buf.WriteString(fmt.Sprintf("%s-", m["viewer_protocol_policy"].(string))) 204 buf.WriteString(fmt.Sprintf("%s-", m["target_origin_id"].(string))) 205 buf.WriteString(fmt.Sprintf("%d-", forwardedValuesHash(m["forwarded_values"].(*schema.Set).List()[0].(map[string]interface{})))) 206 buf.WriteString(fmt.Sprintf("%d-", m["min_ttl"].(int))) 207 if d, ok := m["trusted_signers"]; ok { 208 for _, e := range sortInterfaceSlice(d.([]interface{})) { 209 buf.WriteString(fmt.Sprintf("%s-", e.(string))) 210 } 211 } 212 if d, ok := m["max_ttl"]; ok { 213 buf.WriteString(fmt.Sprintf("%d-", d.(int))) 214 } 215 if d, ok := m["smooth_streaming"]; ok { 216 buf.WriteString(fmt.Sprintf("%t-", d.(bool))) 217 } 218 if d, ok := m["default_ttl"]; ok { 219 buf.WriteString(fmt.Sprintf("%d-", d.(int))) 220 } 221 if d, ok := m["allowed_methods"]; ok { 222 for _, e := range sortInterfaceSlice(d.([]interface{})) { 223 buf.WriteString(fmt.Sprintf("%s-", e.(string))) 224 } 225 } 226 if d, ok := m["cached_methods"]; ok { 227 for _, e := range sortInterfaceSlice(d.([]interface{})) { 228 buf.WriteString(fmt.Sprintf("%s-", e.(string))) 229 } 230 } 231 if d, ok := m["lambda_function_association"]; ok { 232 var associations []interface{} 233 switch d.(type) { 234 case *schema.Set: 235 associations = d.(*schema.Set).List() 236 default: 237 associations = d.([]interface{}) 238 } 239 for _, lfa := range associations { 240 buf.WriteString(fmt.Sprintf("%d-", lambdaFunctionAssociationHash(lfa.(map[string]interface{})))) 241 } 242 } 243 return hashcode.String(buf.String()) 244 } 245 246 func expandCacheBehaviors(s *schema.Set) *cloudfront.CacheBehaviors { 247 var qty int64 248 var items []*cloudfront.CacheBehavior 249 for _, v := range s.List() { 250 items = append(items, expandCacheBehavior(v.(map[string]interface{}))) 251 qty++ 252 } 253 return &cloudfront.CacheBehaviors{ 254 Quantity: aws.Int64(qty), 255 Items: items, 256 } 257 } 258 259 func flattenCacheBehaviors(cbs *cloudfront.CacheBehaviors) *schema.Set { 260 s := []interface{}{} 261 for _, v := range cbs.Items { 262 s = append(s, flattenCacheBehavior(v)) 263 } 264 return schema.NewSet(cacheBehaviorHash, s) 265 } 266 267 func expandCacheBehavior(m map[string]interface{}) *cloudfront.CacheBehavior { 268 cb := &cloudfront.CacheBehavior{ 269 Compress: aws.Bool(m["compress"].(bool)), 270 ViewerProtocolPolicy: aws.String(m["viewer_protocol_policy"].(string)), 271 TargetOriginId: aws.String(m["target_origin_id"].(string)), 272 ForwardedValues: expandForwardedValues(m["forwarded_values"].(*schema.Set).List()[0].(map[string]interface{})), 273 MinTTL: aws.Int64(int64(m["min_ttl"].(int))), 274 MaxTTL: aws.Int64(int64(m["max_ttl"].(int))), 275 DefaultTTL: aws.Int64(int64(m["default_ttl"].(int))), 276 } 277 if v, ok := m["trusted_signers"]; ok { 278 cb.TrustedSigners = expandTrustedSigners(v.([]interface{})) 279 } else { 280 cb.TrustedSigners = expandTrustedSigners([]interface{}{}) 281 } 282 283 if v, ok := m["lambda_function_association"]; ok { 284 cb.LambdaFunctionAssociations = expandLambdaFunctionAssociations(v.(*schema.Set).List()) 285 } 286 287 if v, ok := m["smooth_streaming"]; ok { 288 cb.SmoothStreaming = aws.Bool(v.(bool)) 289 } 290 if v, ok := m["allowed_methods"]; ok { 291 cb.AllowedMethods = expandAllowedMethods(v.([]interface{})) 292 } 293 if v, ok := m["cached_methods"]; ok { 294 cb.AllowedMethods.CachedMethods = expandCachedMethods(v.([]interface{})) 295 } 296 if v, ok := m["path_pattern"]; ok { 297 cb.PathPattern = aws.String(v.(string)) 298 } 299 return cb 300 } 301 302 func flattenCacheBehavior(cb *cloudfront.CacheBehavior) map[string]interface{} { 303 m := make(map[string]interface{}) 304 305 m["compress"] = *cb.Compress 306 m["viewer_protocol_policy"] = *cb.ViewerProtocolPolicy 307 m["target_origin_id"] = *cb.TargetOriginId 308 m["forwarded_values"] = schema.NewSet(forwardedValuesHash, []interface{}{flattenForwardedValues(cb.ForwardedValues)}) 309 m["min_ttl"] = int(*cb.MinTTL) 310 311 if len(cb.TrustedSigners.Items) > 0 { 312 m["trusted_signers"] = flattenTrustedSigners(cb.TrustedSigners) 313 } 314 if len(cb.LambdaFunctionAssociations.Items) > 0 { 315 m["lambda_function_association"] = flattenLambdaFunctionAssociations(cb.LambdaFunctionAssociations) 316 } 317 if cb.MaxTTL != nil { 318 m["max_ttl"] = int(*cb.MaxTTL) 319 } 320 if cb.SmoothStreaming != nil { 321 m["smooth_streaming"] = *cb.SmoothStreaming 322 } 323 if cb.DefaultTTL != nil { 324 m["default_ttl"] = int(*cb.DefaultTTL) 325 } 326 if cb.AllowedMethods != nil { 327 m["allowed_methods"] = flattenAllowedMethods(cb.AllowedMethods) 328 } 329 if cb.AllowedMethods.CachedMethods != nil { 330 m["cached_methods"] = flattenCachedMethods(cb.AllowedMethods.CachedMethods) 331 } 332 if cb.PathPattern != nil { 333 m["path_pattern"] = *cb.PathPattern 334 } 335 return m 336 } 337 338 // Assemble the hash for the aws_cloudfront_distribution cache_behavior 339 // TypeSet attribute. 340 func cacheBehaviorHash(v interface{}) int { 341 var buf bytes.Buffer 342 m := v.(map[string]interface{}) 343 buf.WriteString(fmt.Sprintf("%t-", m["compress"].(bool))) 344 buf.WriteString(fmt.Sprintf("%s-", m["viewer_protocol_policy"].(string))) 345 buf.WriteString(fmt.Sprintf("%s-", m["target_origin_id"].(string))) 346 buf.WriteString(fmt.Sprintf("%d-", forwardedValuesHash(m["forwarded_values"].(*schema.Set).List()[0].(map[string]interface{})))) 347 buf.WriteString(fmt.Sprintf("%d-", m["min_ttl"].(int))) 348 if d, ok := m["trusted_signers"]; ok { 349 for _, e := range sortInterfaceSlice(d.([]interface{})) { 350 buf.WriteString(fmt.Sprintf("%s-", e.(string))) 351 } 352 } 353 if d, ok := m["max_ttl"]; ok { 354 buf.WriteString(fmt.Sprintf("%d-", d.(int))) 355 } 356 if d, ok := m["smooth_streaming"]; ok { 357 buf.WriteString(fmt.Sprintf("%t-", d.(bool))) 358 } 359 if d, ok := m["default_ttl"]; ok { 360 buf.WriteString(fmt.Sprintf("%d-", d.(int))) 361 } 362 if d, ok := m["allowed_methods"]; ok { 363 for _, e := range sortInterfaceSlice(d.([]interface{})) { 364 buf.WriteString(fmt.Sprintf("%s-", e.(string))) 365 } 366 } 367 if d, ok := m["cached_methods"]; ok { 368 for _, e := range sortInterfaceSlice(d.([]interface{})) { 369 buf.WriteString(fmt.Sprintf("%s-", e.(string))) 370 } 371 } 372 if d, ok := m["path_pattern"]; ok { 373 buf.WriteString(fmt.Sprintf("%s-", d)) 374 } 375 if d, ok := m["lambda_function_association"]; ok { 376 var associations []interface{} 377 switch d.(type) { 378 case *schema.Set: 379 associations = d.(*schema.Set).List() 380 default: 381 associations = d.([]interface{}) 382 } 383 for _, lfa := range associations { 384 buf.WriteString(fmt.Sprintf("%d-", lambdaFunctionAssociationHash(lfa.(map[string]interface{})))) 385 } 386 } 387 return hashcode.String(buf.String()) 388 } 389 390 func expandTrustedSigners(s []interface{}) *cloudfront.TrustedSigners { 391 var ts cloudfront.TrustedSigners 392 if len(s) > 0 { 393 ts.Quantity = aws.Int64(int64(len(s))) 394 ts.Items = expandStringList(s) 395 ts.Enabled = aws.Bool(true) 396 } else { 397 ts.Quantity = aws.Int64(0) 398 ts.Enabled = aws.Bool(false) 399 } 400 return &ts 401 } 402 403 func flattenTrustedSigners(ts *cloudfront.TrustedSigners) []interface{} { 404 if ts.Items != nil { 405 return flattenStringList(ts.Items) 406 } 407 return []interface{}{} 408 } 409 410 func lambdaFunctionAssociationHash(v interface{}) int { 411 var buf bytes.Buffer 412 m := v.(map[string]interface{}) 413 buf.WriteString(fmt.Sprintf("%s-", m["event_type"].(string))) 414 buf.WriteString(fmt.Sprintf("%s", m["lambda_arn"].(string))) 415 return hashcode.String(buf.String()) 416 } 417 418 func expandLambdaFunctionAssociations(v interface{}) *cloudfront.LambdaFunctionAssociations { 419 if v == nil { 420 return &cloudfront.LambdaFunctionAssociations{ 421 Quantity: aws.Int64(0), 422 } 423 } 424 425 s := v.([]interface{}) 426 var lfa cloudfront.LambdaFunctionAssociations 427 lfa.Quantity = aws.Int64(int64(len(s))) 428 lfa.Items = make([]*cloudfront.LambdaFunctionAssociation, len(s)) 429 for i, lf := range s { 430 lfa.Items[i] = expandLambdaFunctionAssociation(lf.(map[string]interface{})) 431 } 432 return &lfa 433 } 434 435 func expandLambdaFunctionAssociation(lf map[string]interface{}) *cloudfront.LambdaFunctionAssociation { 436 var lfa cloudfront.LambdaFunctionAssociation 437 if v, ok := lf["event_type"]; ok { 438 lfa.EventType = aws.String(v.(string)) 439 } 440 if v, ok := lf["lambda_arn"]; ok { 441 lfa.LambdaFunctionARN = aws.String(v.(string)) 442 } 443 return &lfa 444 } 445 446 func flattenLambdaFunctionAssociations(lfa *cloudfront.LambdaFunctionAssociations) *schema.Set { 447 s := schema.NewSet(lambdaFunctionAssociationHash, []interface{}{}) 448 for _, v := range lfa.Items { 449 s.Add(flattenLambdaFunctionAssociation(v)) 450 } 451 return s 452 } 453 454 func flattenLambdaFunctionAssociation(lfa *cloudfront.LambdaFunctionAssociation) map[string]interface{} { 455 m := map[string]interface{}{} 456 if lfa != nil { 457 m["event_type"] = *lfa.EventType 458 m["lambda_arn"] = *lfa.LambdaFunctionARN 459 } 460 return m 461 } 462 463 func expandForwardedValues(m map[string]interface{}) *cloudfront.ForwardedValues { 464 fv := &cloudfront.ForwardedValues{ 465 QueryString: aws.Bool(m["query_string"].(bool)), 466 } 467 if v, ok := m["cookies"]; ok && v.(*schema.Set).Len() > 0 { 468 fv.Cookies = expandCookiePreference(v.(*schema.Set).List()[0].(map[string]interface{})) 469 } 470 if v, ok := m["headers"]; ok { 471 fv.Headers = expandHeaders(v.([]interface{})) 472 } 473 if v, ok := m["query_string_cache_keys"]; ok { 474 fv.QueryStringCacheKeys = expandQueryStringCacheKeys(v.([]interface{})) 475 } 476 return fv 477 } 478 479 func flattenForwardedValues(fv *cloudfront.ForwardedValues) map[string]interface{} { 480 m := make(map[string]interface{}) 481 m["query_string"] = *fv.QueryString 482 if fv.Cookies != nil { 483 m["cookies"] = schema.NewSet(cookiePreferenceHash, []interface{}{flattenCookiePreference(fv.Cookies)}) 484 } 485 if fv.Headers != nil { 486 m["headers"] = flattenHeaders(fv.Headers) 487 } 488 if fv.QueryStringCacheKeys != nil { 489 m["query_string_cache_keys"] = flattenQueryStringCacheKeys(fv.QueryStringCacheKeys) 490 } 491 return m 492 } 493 494 // Assemble the hash for the aws_cloudfront_distribution forwarded_values 495 // TypeSet attribute. 496 func forwardedValuesHash(v interface{}) int { 497 var buf bytes.Buffer 498 m := v.(map[string]interface{}) 499 buf.WriteString(fmt.Sprintf("%t-", m["query_string"].(bool))) 500 if d, ok := m["cookies"]; ok && d.(*schema.Set).Len() > 0 { 501 buf.WriteString(fmt.Sprintf("%d-", cookiePreferenceHash(d.(*schema.Set).List()[0].(map[string]interface{})))) 502 } 503 if d, ok := m["headers"]; ok { 504 for _, e := range sortInterfaceSlice(d.([]interface{})) { 505 buf.WriteString(fmt.Sprintf("%s-", e.(string))) 506 } 507 } 508 if d, ok := m["query_string_cache_keys"]; ok { 509 for _, e := range sortInterfaceSlice(d.([]interface{})) { 510 buf.WriteString(fmt.Sprintf("%s-", e.(string))) 511 } 512 } 513 return hashcode.String(buf.String()) 514 } 515 516 func expandHeaders(d []interface{}) *cloudfront.Headers { 517 return &cloudfront.Headers{ 518 Quantity: aws.Int64(int64(len(d))), 519 Items: expandStringList(d), 520 } 521 } 522 523 func flattenHeaders(h *cloudfront.Headers) []interface{} { 524 if h.Items != nil { 525 return flattenStringList(h.Items) 526 } 527 return []interface{}{} 528 } 529 530 func expandQueryStringCacheKeys(d []interface{}) *cloudfront.QueryStringCacheKeys { 531 return &cloudfront.QueryStringCacheKeys{ 532 Quantity: aws.Int64(int64(len(d))), 533 Items: expandStringList(d), 534 } 535 } 536 537 func flattenQueryStringCacheKeys(k *cloudfront.QueryStringCacheKeys) []interface{} { 538 if k.Items != nil { 539 return flattenStringList(k.Items) 540 } 541 return []interface{}{} 542 } 543 544 func expandCookiePreference(m map[string]interface{}) *cloudfront.CookiePreference { 545 cp := &cloudfront.CookiePreference{ 546 Forward: aws.String(m["forward"].(string)), 547 } 548 if v, ok := m["whitelisted_names"]; ok { 549 cp.WhitelistedNames = expandCookieNames(v.([]interface{})) 550 } 551 return cp 552 } 553 554 func flattenCookiePreference(cp *cloudfront.CookiePreference) map[string]interface{} { 555 m := make(map[string]interface{}) 556 m["forward"] = *cp.Forward 557 if cp.WhitelistedNames != nil { 558 m["whitelisted_names"] = flattenCookieNames(cp.WhitelistedNames) 559 } 560 return m 561 } 562 563 // Assemble the hash for the aws_cloudfront_distribution cookies 564 // TypeSet attribute. 565 func cookiePreferenceHash(v interface{}) int { 566 var buf bytes.Buffer 567 m := v.(map[string]interface{}) 568 buf.WriteString(fmt.Sprintf("%s-", m["forward"].(string))) 569 if d, ok := m["whitelisted_names"]; ok { 570 for _, e := range sortInterfaceSlice(d.([]interface{})) { 571 buf.WriteString(fmt.Sprintf("%s-", e.(string))) 572 } 573 } 574 return hashcode.String(buf.String()) 575 } 576 577 func expandCookieNames(d []interface{}) *cloudfront.CookieNames { 578 return &cloudfront.CookieNames{ 579 Quantity: aws.Int64(int64(len(d))), 580 Items: expandStringList(d), 581 } 582 } 583 584 func flattenCookieNames(cn *cloudfront.CookieNames) []interface{} { 585 if cn.Items != nil { 586 return flattenStringList(cn.Items) 587 } 588 return []interface{}{} 589 } 590 591 func expandAllowedMethods(s []interface{}) *cloudfront.AllowedMethods { 592 return &cloudfront.AllowedMethods{ 593 Quantity: aws.Int64(int64(len(s))), 594 Items: expandStringList(s), 595 } 596 } 597 598 func flattenAllowedMethods(am *cloudfront.AllowedMethods) []interface{} { 599 if am.Items != nil { 600 return flattenStringList(am.Items) 601 } 602 return []interface{}{} 603 } 604 605 func expandCachedMethods(s []interface{}) *cloudfront.CachedMethods { 606 return &cloudfront.CachedMethods{ 607 Quantity: aws.Int64(int64(len(s))), 608 Items: expandStringList(s), 609 } 610 } 611 612 func flattenCachedMethods(cm *cloudfront.CachedMethods) []interface{} { 613 if cm.Items != nil { 614 return flattenStringList(cm.Items) 615 } 616 return []interface{}{} 617 } 618 619 func expandOrigins(s *schema.Set) *cloudfront.Origins { 620 qty := 0 621 items := []*cloudfront.Origin{} 622 for _, v := range s.List() { 623 items = append(items, expandOrigin(v.(map[string]interface{}))) 624 qty++ 625 } 626 return &cloudfront.Origins{ 627 Quantity: aws.Int64(int64(qty)), 628 Items: items, 629 } 630 } 631 632 func flattenOrigins(ors *cloudfront.Origins) *schema.Set { 633 s := []interface{}{} 634 for _, v := range ors.Items { 635 s = append(s, flattenOrigin(v)) 636 } 637 return schema.NewSet(originHash, s) 638 } 639 640 func expandOrigin(m map[string]interface{}) *cloudfront.Origin { 641 origin := &cloudfront.Origin{ 642 Id: aws.String(m["origin_id"].(string)), 643 DomainName: aws.String(m["domain_name"].(string)), 644 } 645 if v, ok := m["custom_header"]; ok { 646 origin.CustomHeaders = expandCustomHeaders(v.(*schema.Set)) 647 } 648 if v, ok := m["custom_origin_config"]; ok { 649 if s := v.(*schema.Set).List(); len(s) > 0 { 650 origin.CustomOriginConfig = expandCustomOriginConfig(s[0].(map[string]interface{})) 651 } 652 } 653 if v, ok := m["origin_path"]; ok { 654 origin.OriginPath = aws.String(v.(string)) 655 } 656 if v, ok := m["s3_origin_config"]; ok { 657 if s := v.(*schema.Set).List(); len(s) > 0 { 658 origin.S3OriginConfig = expandS3OriginConfig(s[0].(map[string]interface{})) 659 } 660 } 661 662 // if both custom and s3 origin are missing, add an empty s3 origin 663 // One or the other must be specified, but the S3 origin can be "empty" 664 if origin.S3OriginConfig == nil && origin.CustomOriginConfig == nil { 665 origin.S3OriginConfig = &cloudfront.S3OriginConfig{ 666 OriginAccessIdentity: aws.String(""), 667 } 668 } 669 670 return origin 671 } 672 673 func flattenOrigin(or *cloudfront.Origin) map[string]interface{} { 674 m := make(map[string]interface{}) 675 m["origin_id"] = *or.Id 676 m["domain_name"] = *or.DomainName 677 if or.CustomHeaders != nil { 678 m["custom_header"] = flattenCustomHeaders(or.CustomHeaders) 679 } 680 if or.CustomOriginConfig != nil { 681 m["custom_origin_config"] = schema.NewSet(customOriginConfigHash, []interface{}{flattenCustomOriginConfig(or.CustomOriginConfig)}) 682 } 683 if or.OriginPath != nil { 684 m["origin_path"] = *or.OriginPath 685 } 686 if or.S3OriginConfig != nil { 687 if or.S3OriginConfig.OriginAccessIdentity != nil && *or.S3OriginConfig.OriginAccessIdentity != "" { 688 m["s3_origin_config"] = schema.NewSet(s3OriginConfigHash, []interface{}{flattenS3OriginConfig(or.S3OriginConfig)}) 689 } 690 } 691 return m 692 } 693 694 // Assemble the hash for the aws_cloudfront_distribution origin 695 // TypeSet attribute. 696 func originHash(v interface{}) int { 697 var buf bytes.Buffer 698 m := v.(map[string]interface{}) 699 buf.WriteString(fmt.Sprintf("%s-", m["origin_id"].(string))) 700 buf.WriteString(fmt.Sprintf("%s-", m["domain_name"].(string))) 701 if v, ok := m["custom_header"]; ok { 702 buf.WriteString(fmt.Sprintf("%d-", customHeadersHash(v.(*schema.Set)))) 703 } 704 if v, ok := m["custom_origin_config"]; ok { 705 if s := v.(*schema.Set).List(); len(s) > 0 { 706 buf.WriteString(fmt.Sprintf("%d-", customOriginConfigHash((s[0].(map[string]interface{}))))) 707 } 708 } 709 if v, ok := m["origin_path"]; ok { 710 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 711 } 712 if v, ok := m["s3_origin_config"]; ok { 713 if s := v.(*schema.Set).List(); len(s) > 0 { 714 buf.WriteString(fmt.Sprintf("%d-", s3OriginConfigHash((s[0].(map[string]interface{}))))) 715 } 716 } 717 return hashcode.String(buf.String()) 718 } 719 720 func expandCustomHeaders(s *schema.Set) *cloudfront.CustomHeaders { 721 qty := 0 722 items := []*cloudfront.OriginCustomHeader{} 723 for _, v := range s.List() { 724 items = append(items, expandOriginCustomHeader(v.(map[string]interface{}))) 725 qty++ 726 } 727 return &cloudfront.CustomHeaders{ 728 Quantity: aws.Int64(int64(qty)), 729 Items: items, 730 } 731 } 732 733 func flattenCustomHeaders(chs *cloudfront.CustomHeaders) *schema.Set { 734 s := []interface{}{} 735 for _, v := range chs.Items { 736 s = append(s, flattenOriginCustomHeader(v)) 737 } 738 return schema.NewSet(originCustomHeaderHash, s) 739 } 740 741 func expandOriginCustomHeader(m map[string]interface{}) *cloudfront.OriginCustomHeader { 742 return &cloudfront.OriginCustomHeader{ 743 HeaderName: aws.String(m["name"].(string)), 744 HeaderValue: aws.String(m["value"].(string)), 745 } 746 } 747 748 func flattenOriginCustomHeader(och *cloudfront.OriginCustomHeader) map[string]interface{} { 749 return map[string]interface{}{ 750 "name": *och.HeaderName, 751 "value": *och.HeaderValue, 752 } 753 } 754 755 // Helper function used by originHash to get a composite hash for all 756 // aws_cloudfront_distribution custom_header attributes. 757 func customHeadersHash(s *schema.Set) int { 758 var buf bytes.Buffer 759 for _, v := range s.List() { 760 buf.WriteString(fmt.Sprintf("%d-", originCustomHeaderHash(v))) 761 } 762 return hashcode.String(buf.String()) 763 } 764 765 // Assemble the hash for the aws_cloudfront_distribution custom_header 766 // TypeSet attribute. 767 func originCustomHeaderHash(v interface{}) int { 768 var buf bytes.Buffer 769 m := v.(map[string]interface{}) 770 buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) 771 buf.WriteString(fmt.Sprintf("%s-", m["value"].(string))) 772 return hashcode.String(buf.String()) 773 } 774 775 func expandCustomOriginConfig(m map[string]interface{}) *cloudfront.CustomOriginConfig { 776 777 customOrigin := &cloudfront.CustomOriginConfig{ 778 OriginProtocolPolicy: aws.String(m["origin_protocol_policy"].(string)), 779 HTTPPort: aws.Int64(int64(m["http_port"].(int))), 780 HTTPSPort: aws.Int64(int64(m["https_port"].(int))), 781 OriginSslProtocols: expandCustomOriginConfigSSL(m["origin_ssl_protocols"].([]interface{})), 782 OriginReadTimeout: aws.Int64(int64(m["origin_read_timeout"].(int))), 783 OriginKeepaliveTimeout: aws.Int64(int64(m["origin_keepalive_timeout"].(int))), 784 } 785 786 return customOrigin 787 } 788 789 func flattenCustomOriginConfig(cor *cloudfront.CustomOriginConfig) map[string]interface{} { 790 791 customOrigin := map[string]interface{}{ 792 "origin_protocol_policy": *cor.OriginProtocolPolicy, 793 "http_port": int(*cor.HTTPPort), 794 "https_port": int(*cor.HTTPSPort), 795 "origin_ssl_protocols": flattenCustomOriginConfigSSL(cor.OriginSslProtocols), 796 "origin_read_timeout": int(*cor.OriginReadTimeout), 797 "origin_keepalive_timeout": int(*cor.OriginKeepaliveTimeout), 798 } 799 800 return customOrigin 801 } 802 803 // Assemble the hash for the aws_cloudfront_distribution custom_origin_config 804 // TypeSet attribute. 805 func customOriginConfigHash(v interface{}) int { 806 var buf bytes.Buffer 807 m := v.(map[string]interface{}) 808 buf.WriteString(fmt.Sprintf("%s-", m["origin_protocol_policy"].(string))) 809 buf.WriteString(fmt.Sprintf("%d-", m["http_port"].(int))) 810 buf.WriteString(fmt.Sprintf("%d-", m["https_port"].(int))) 811 for _, v := range sortInterfaceSlice(m["origin_ssl_protocols"].([]interface{})) { 812 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 813 } 814 buf.WriteString(fmt.Sprintf("%d-", m["origin_keepalive_timeout"].(int))) 815 buf.WriteString(fmt.Sprintf("%d-", m["origin_read_timeout"].(int))) 816 817 return hashcode.String(buf.String()) 818 } 819 820 func expandCustomOriginConfigSSL(s []interface{}) *cloudfront.OriginSslProtocols { 821 items := expandStringList(s) 822 return &cloudfront.OriginSslProtocols{ 823 Quantity: aws.Int64(int64(len(items))), 824 Items: items, 825 } 826 } 827 828 func flattenCustomOriginConfigSSL(osp *cloudfront.OriginSslProtocols) []interface{} { 829 return flattenStringList(osp.Items) 830 } 831 832 func expandS3OriginConfig(m map[string]interface{}) *cloudfront.S3OriginConfig { 833 return &cloudfront.S3OriginConfig{ 834 OriginAccessIdentity: aws.String(m["origin_access_identity"].(string)), 835 } 836 } 837 838 func flattenS3OriginConfig(s3o *cloudfront.S3OriginConfig) map[string]interface{} { 839 return map[string]interface{}{ 840 "origin_access_identity": *s3o.OriginAccessIdentity, 841 } 842 } 843 844 // Assemble the hash for the aws_cloudfront_distribution s3_origin_config 845 // TypeSet attribute. 846 func s3OriginConfigHash(v interface{}) int { 847 var buf bytes.Buffer 848 m := v.(map[string]interface{}) 849 buf.WriteString(fmt.Sprintf("%s-", m["origin_access_identity"].(string))) 850 return hashcode.String(buf.String()) 851 } 852 853 func expandCustomErrorResponses(s *schema.Set) *cloudfront.CustomErrorResponses { 854 qty := 0 855 items := []*cloudfront.CustomErrorResponse{} 856 for _, v := range s.List() { 857 items = append(items, expandCustomErrorResponse(v.(map[string]interface{}))) 858 qty++ 859 } 860 return &cloudfront.CustomErrorResponses{ 861 Quantity: aws.Int64(int64(qty)), 862 Items: items, 863 } 864 } 865 866 func flattenCustomErrorResponses(ers *cloudfront.CustomErrorResponses) *schema.Set { 867 s := []interface{}{} 868 for _, v := range ers.Items { 869 s = append(s, flattenCustomErrorResponse(v)) 870 } 871 return schema.NewSet(customErrorResponseHash, s) 872 } 873 874 func expandCustomErrorResponse(m map[string]interface{}) *cloudfront.CustomErrorResponse { 875 er := cloudfront.CustomErrorResponse{ 876 ErrorCode: aws.Int64(int64(m["error_code"].(int))), 877 } 878 if v, ok := m["error_caching_min_ttl"]; ok { 879 er.ErrorCachingMinTTL = aws.Int64(int64(v.(int))) 880 } 881 if v, ok := m["response_code"]; ok && v.(int) != 0 { 882 er.ResponseCode = aws.String(strconv.Itoa(v.(int))) 883 } else { 884 er.ResponseCode = aws.String("") 885 } 886 if v, ok := m["response_page_path"]; ok { 887 er.ResponsePagePath = aws.String(v.(string)) 888 } 889 890 return &er 891 } 892 893 func flattenCustomErrorResponse(er *cloudfront.CustomErrorResponse) map[string]interface{} { 894 m := make(map[string]interface{}) 895 m["error_code"] = int(*er.ErrorCode) 896 if er.ErrorCachingMinTTL != nil { 897 m["error_caching_min_ttl"] = int(*er.ErrorCachingMinTTL) 898 } 899 if er.ResponseCode != nil { 900 m["response_code"], _ = strconv.Atoi(*er.ResponseCode) 901 } 902 if er.ResponsePagePath != nil { 903 m["response_page_path"] = *er.ResponsePagePath 904 } 905 return m 906 } 907 908 // Assemble the hash for the aws_cloudfront_distribution custom_error_response 909 // TypeSet attribute. 910 func customErrorResponseHash(v interface{}) int { 911 var buf bytes.Buffer 912 m := v.(map[string]interface{}) 913 buf.WriteString(fmt.Sprintf("%d-", m["error_code"].(int))) 914 if v, ok := m["error_caching_min_ttl"]; ok { 915 buf.WriteString(fmt.Sprintf("%d-", v.(int))) 916 } 917 if v, ok := m["response_code"]; ok { 918 buf.WriteString(fmt.Sprintf("%d-", v.(int))) 919 } 920 if v, ok := m["response_page_path"]; ok { 921 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 922 } 923 return hashcode.String(buf.String()) 924 } 925 926 func expandLoggingConfig(m map[string]interface{}) *cloudfront.LoggingConfig { 927 var lc cloudfront.LoggingConfig 928 if m != nil { 929 lc.Prefix = aws.String(m["prefix"].(string)) 930 lc.Bucket = aws.String(m["bucket"].(string)) 931 lc.IncludeCookies = aws.Bool(m["include_cookies"].(bool)) 932 lc.Enabled = aws.Bool(true) 933 } else { 934 lc.Prefix = aws.String("") 935 lc.Bucket = aws.String("") 936 lc.IncludeCookies = aws.Bool(false) 937 lc.Enabled = aws.Bool(false) 938 } 939 return &lc 940 } 941 942 func flattenLoggingConfig(lc *cloudfront.LoggingConfig) *schema.Set { 943 m := make(map[string]interface{}) 944 m["prefix"] = *lc.Prefix 945 m["bucket"] = *lc.Bucket 946 m["include_cookies"] = *lc.IncludeCookies 947 return schema.NewSet(loggingConfigHash, []interface{}{m}) 948 } 949 950 // Assemble the hash for the aws_cloudfront_distribution logging_config 951 // TypeSet attribute. 952 func loggingConfigHash(v interface{}) int { 953 var buf bytes.Buffer 954 m := v.(map[string]interface{}) 955 buf.WriteString(fmt.Sprintf("%s-", m["prefix"].(string))) 956 buf.WriteString(fmt.Sprintf("%s-", m["bucket"].(string))) 957 buf.WriteString(fmt.Sprintf("%t-", m["include_cookies"].(bool))) 958 return hashcode.String(buf.String()) 959 } 960 961 func expandAliases(as *schema.Set) *cloudfront.Aliases { 962 s := as.List() 963 var aliases cloudfront.Aliases 964 if len(s) > 0 { 965 aliases.Quantity = aws.Int64(int64(len(s))) 966 aliases.Items = expandStringList(s) 967 } else { 968 aliases.Quantity = aws.Int64(0) 969 } 970 return &aliases 971 } 972 973 func flattenAliases(aliases *cloudfront.Aliases) *schema.Set { 974 if aliases.Items != nil { 975 return schema.NewSet(aliasesHash, flattenStringList(aliases.Items)) 976 } 977 return schema.NewSet(aliasesHash, []interface{}{}) 978 } 979 980 // Assemble the hash for the aws_cloudfront_distribution aliases 981 // TypeSet attribute. 982 func aliasesHash(v interface{}) int { 983 return hashcode.String(v.(string)) 984 } 985 986 func expandRestrictions(m map[string]interface{}) *cloudfront.Restrictions { 987 return &cloudfront.Restrictions{ 988 GeoRestriction: expandGeoRestriction(m["geo_restriction"].(*schema.Set).List()[0].(map[string]interface{})), 989 } 990 } 991 992 func flattenRestrictions(r *cloudfront.Restrictions) *schema.Set { 993 m := make(map[string]interface{}) 994 s := schema.NewSet(geoRestrictionHash, []interface{}{flattenGeoRestriction(r.GeoRestriction)}) 995 m["geo_restriction"] = s 996 return schema.NewSet(restrictionsHash, []interface{}{m}) 997 } 998 999 // Assemble the hash for the aws_cloudfront_distribution restrictions 1000 // TypeSet attribute. 1001 func restrictionsHash(v interface{}) int { 1002 var buf bytes.Buffer 1003 m := v.(map[string]interface{}) 1004 buf.WriteString(fmt.Sprintf("%d-", geoRestrictionHash(m["geo_restriction"].(*schema.Set).List()[0].(map[string]interface{})))) 1005 return hashcode.String(buf.String()) 1006 } 1007 1008 func expandGeoRestriction(m map[string]interface{}) *cloudfront.GeoRestriction { 1009 gr := cloudfront.GeoRestriction{ 1010 RestrictionType: aws.String(m["restriction_type"].(string)), 1011 } 1012 if v, ok := m["locations"]; ok { 1013 gr.Quantity = aws.Int64(int64(len(v.([]interface{})))) 1014 gr.Items = expandStringList(v.([]interface{})) 1015 sort.Sort(StringPtrSlice(gr.Items)) 1016 } else { 1017 gr.Quantity = aws.Int64(0) 1018 } 1019 return &gr 1020 } 1021 1022 func flattenGeoRestriction(gr *cloudfront.GeoRestriction) map[string]interface{} { 1023 m := make(map[string]interface{}) 1024 1025 m["restriction_type"] = *gr.RestrictionType 1026 if gr.Items != nil { 1027 sort.Sort(StringPtrSlice(gr.Items)) 1028 m["locations"] = flattenStringList(gr.Items) 1029 } 1030 return m 1031 } 1032 1033 // Assemble the hash for the aws_cloudfront_distribution geo_restriction 1034 // TypeSet attribute. 1035 func geoRestrictionHash(v interface{}) int { 1036 var buf bytes.Buffer 1037 m := v.(map[string]interface{}) 1038 // All keys added in alphabetical order. 1039 buf.WriteString(fmt.Sprintf("%s-", m["restriction_type"].(string))) 1040 if v, ok := m["locations"]; ok { 1041 for _, w := range sortInterfaceSlice(v.([]interface{})) { 1042 buf.WriteString(fmt.Sprintf("%s-", w.(string))) 1043 } 1044 } 1045 return hashcode.String(buf.String()) 1046 } 1047 1048 func expandViewerCertificate(m map[string]interface{}) *cloudfront.ViewerCertificate { 1049 var vc cloudfront.ViewerCertificate 1050 if v, ok := m["iam_certificate_id"]; ok && v != "" { 1051 vc.IAMCertificateId = aws.String(v.(string)) 1052 vc.SSLSupportMethod = aws.String(m["ssl_support_method"].(string)) 1053 } else if v, ok := m["acm_certificate_arn"]; ok && v != "" { 1054 vc.ACMCertificateArn = aws.String(v.(string)) 1055 vc.SSLSupportMethod = aws.String(m["ssl_support_method"].(string)) 1056 } else { 1057 vc.CloudFrontDefaultCertificate = aws.Bool(m["cloudfront_default_certificate"].(bool)) 1058 } 1059 if v, ok := m["minimum_protocol_version"]; ok && v != "" { 1060 vc.MinimumProtocolVersion = aws.String(v.(string)) 1061 } 1062 return &vc 1063 } 1064 1065 func flattenViewerCertificate(vc *cloudfront.ViewerCertificate) *schema.Set { 1066 m := make(map[string]interface{}) 1067 1068 if vc.IAMCertificateId != nil { 1069 m["iam_certificate_id"] = *vc.IAMCertificateId 1070 m["ssl_support_method"] = *vc.SSLSupportMethod 1071 } 1072 if vc.ACMCertificateArn != nil { 1073 m["acm_certificate_arn"] = *vc.ACMCertificateArn 1074 m["ssl_support_method"] = *vc.SSLSupportMethod 1075 } 1076 if vc.CloudFrontDefaultCertificate != nil { 1077 m["cloudfront_default_certificate"] = *vc.CloudFrontDefaultCertificate 1078 } 1079 if vc.MinimumProtocolVersion != nil { 1080 m["minimum_protocol_version"] = *vc.MinimumProtocolVersion 1081 } 1082 return schema.NewSet(viewerCertificateHash, []interface{}{m}) 1083 } 1084 1085 // Assemble the hash for the aws_cloudfront_distribution viewer_certificate 1086 // TypeSet attribute. 1087 func viewerCertificateHash(v interface{}) int { 1088 var buf bytes.Buffer 1089 m := v.(map[string]interface{}) 1090 if v, ok := m["iam_certificate_id"]; ok && v.(string) != "" { 1091 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 1092 buf.WriteString(fmt.Sprintf("%s-", m["ssl_support_method"].(string))) 1093 } else if v, ok := m["acm_certificate_arn"]; ok && v.(string) != "" { 1094 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 1095 buf.WriteString(fmt.Sprintf("%s-", m["ssl_support_method"].(string))) 1096 } else { 1097 buf.WriteString(fmt.Sprintf("%t-", m["cloudfront_default_certificate"].(bool))) 1098 } 1099 if v, ok := m["minimum_protocol_version"]; ok && v.(string) != "" { 1100 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 1101 } 1102 return hashcode.String(buf.String()) 1103 } 1104 1105 // Do a top-level copy of struct fields from one struct to another. Used to 1106 // copy fields between CacheBehavior and DefaultCacheBehavior structs. 1107 func simpleCopyStruct(src, dst interface{}) { 1108 s := reflect.ValueOf(src).Elem() 1109 d := reflect.ValueOf(dst).Elem() 1110 1111 for i := 0; i < s.NumField(); i++ { 1112 if s.Field(i).CanSet() == true { 1113 if s.Field(i).Interface() != nil { 1114 for j := 0; j < d.NumField(); j++ { 1115 if d.Type().Field(j).Name == s.Type().Field(i).Name { 1116 d.Field(j).Set(s.Field(i)) 1117 } 1118 } 1119 } 1120 } 1121 } 1122 } 1123 1124 // Convert *cloudfront.ActiveTrustedSigners to a flatmap.Map type, which ensures 1125 // it can probably be inserted into the schema.TypeMap type used by the 1126 // active_trusted_signers attribute. 1127 func flattenActiveTrustedSigners(ats *cloudfront.ActiveTrustedSigners) flatmap.Map { 1128 m := make(map[string]interface{}) 1129 s := []interface{}{} 1130 m["enabled"] = *ats.Enabled 1131 1132 for _, v := range ats.Items { 1133 signer := make(map[string]interface{}) 1134 signer["aws_account_number"] = *v.AwsAccountNumber 1135 signer["key_pair_ids"] = aws.StringValueSlice(v.KeyPairIds.Items) 1136 s = append(s, signer) 1137 } 1138 m["items"] = s 1139 return flatmap.Flatten(m) 1140 }