decode.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package endpoints
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "github.com/aws/aws-sdk-go/aws/awserr"
  7. )
  8. type modelDefinition map[string]json.RawMessage
  9. // A DecodeModelOptions are the options for how the endpoints model definition
  10. // are decoded.
  11. type DecodeModelOptions struct {
  12. SkipCustomizations bool
  13. }
  14. // Set combines all of the option functions together.
  15. func (d *DecodeModelOptions) Set(optFns ...func(*DecodeModelOptions)) {
  16. for _, fn := range optFns {
  17. fn(d)
  18. }
  19. }
  20. // DecodeModel unmarshals a Regions and Endpoint model definition file into
  21. // a endpoint Resolver. If the file format is not supported, or an error occurs
  22. // when unmarshaling the model an error will be returned.
  23. //
  24. // Casting the return value of this func to a EnumPartitions will
  25. // allow you to get a list of the partitions in the order the endpoints
  26. // will be resolved in.
  27. //
  28. // resolver, err := endpoints.DecodeModel(reader)
  29. //
  30. // partitions := resolver.(endpoints.EnumPartitions).Partitions()
  31. // for _, p := range partitions {
  32. // // ... inspect partitions
  33. // }
  34. func DecodeModel(r io.Reader, optFns ...func(*DecodeModelOptions)) (Resolver, error) {
  35. var opts DecodeModelOptions
  36. opts.Set(optFns...)
  37. // Get the version of the partition file to determine what
  38. // unmarshaling model to use.
  39. modelDef := modelDefinition{}
  40. if err := json.NewDecoder(r).Decode(&modelDef); err != nil {
  41. return nil, newDecodeModelError("failed to decode endpoints model", err)
  42. }
  43. var version string
  44. if b, ok := modelDef["version"]; ok {
  45. version = string(b)
  46. } else {
  47. return nil, newDecodeModelError("endpoints version not found in model", nil)
  48. }
  49. if version == "3" {
  50. return decodeV3Endpoints(modelDef, opts)
  51. }
  52. return nil, newDecodeModelError(
  53. fmt.Sprintf("endpoints version %s, not supported", version), nil)
  54. }
  55. func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resolver, error) {
  56. b, ok := modelDef["partitions"]
  57. if !ok {
  58. return nil, newDecodeModelError("endpoints model missing partitions", nil)
  59. }
  60. ps := partitions{}
  61. if err := json.Unmarshal(b, &ps); err != nil {
  62. return nil, newDecodeModelError("failed to decode endpoints model", err)
  63. }
  64. if opts.SkipCustomizations {
  65. return ps, nil
  66. }
  67. // Customization
  68. for i := 0; i < len(ps); i++ {
  69. p := &ps[i]
  70. custAddEC2Metadata(p)
  71. custAddS3DualStack(p)
  72. custRmIotDataService(p)
  73. }
  74. return ps, nil
  75. }
  76. func custAddS3DualStack(p *partition) {
  77. if p.ID != "aws" {
  78. return
  79. }
  80. s, ok := p.Services["s3"]
  81. if !ok {
  82. return
  83. }
  84. s.Defaults.HasDualStack = boxedTrue
  85. s.Defaults.DualStackHostname = "{service}.dualstack.{region}.{dnsSuffix}"
  86. p.Services["s3"] = s
  87. }
  88. func custAddEC2Metadata(p *partition) {
  89. p.Services["ec2metadata"] = service{
  90. IsRegionalized: boxedFalse,
  91. PartitionEndpoint: "aws-global",
  92. Endpoints: endpoints{
  93. "aws-global": endpoint{
  94. Hostname: "169.254.169.254/latest",
  95. Protocols: []string{"http"},
  96. },
  97. },
  98. }
  99. }
  100. func custRmIotDataService(p *partition) {
  101. delete(p.Services, "data.iot")
  102. }
  103. type decodeModelError struct {
  104. awsError
  105. }
  106. func newDecodeModelError(msg string, err error) decodeModelError {
  107. return decodeModelError{
  108. awsError: awserr.New("DecodeEndpointsModelError", msg, err),
  109. }
  110. }