]>
Commit | Line | Data |
---|---|---|
1 | package endpoints | |
2 | ||
3 | import ( | |
4 | "encoding/json" | |
5 | "fmt" | |
6 | "io" | |
7 | ||
8 | "github.com/aws/aws-sdk-go/aws/awserr" | |
9 | ) | |
10 | ||
11 | type modelDefinition map[string]json.RawMessage | |
12 | ||
13 | // A DecodeModelOptions are the options for how the endpoints model definition | |
14 | // are decoded. | |
15 | type DecodeModelOptions struct { | |
16 | SkipCustomizations bool | |
17 | } | |
18 | ||
19 | // Set combines all of the option functions together. | |
20 | func (d *DecodeModelOptions) Set(optFns ...func(*DecodeModelOptions)) { | |
21 | for _, fn := range optFns { | |
22 | fn(d) | |
23 | } | |
24 | } | |
25 | ||
26 | // DecodeModel unmarshals a Regions and Endpoint model definition file into | |
27 | // a endpoint Resolver. If the file format is not supported, or an error occurs | |
28 | // when unmarshaling the model an error will be returned. | |
29 | // | |
30 | // Casting the return value of this func to a EnumPartitions will | |
31 | // allow you to get a list of the partitions in the order the endpoints | |
32 | // will be resolved in. | |
33 | // | |
34 | // resolver, err := endpoints.DecodeModel(reader) | |
35 | // | |
36 | // partitions := resolver.(endpoints.EnumPartitions).Partitions() | |
37 | // for _, p := range partitions { | |
38 | // // ... inspect partitions | |
39 | // } | |
40 | func DecodeModel(r io.Reader, optFns ...func(*DecodeModelOptions)) (Resolver, error) { | |
41 | var opts DecodeModelOptions | |
42 | opts.Set(optFns...) | |
43 | ||
44 | // Get the version of the partition file to determine what | |
45 | // unmarshaling model to use. | |
46 | modelDef := modelDefinition{} | |
47 | if err := json.NewDecoder(r).Decode(&modelDef); err != nil { | |
48 | return nil, newDecodeModelError("failed to decode endpoints model", err) | |
49 | } | |
50 | ||
51 | var version string | |
52 | if b, ok := modelDef["version"]; ok { | |
53 | version = string(b) | |
54 | } else { | |
55 | return nil, newDecodeModelError("endpoints version not found in model", nil) | |
56 | } | |
57 | ||
58 | if version == "3" { | |
59 | return decodeV3Endpoints(modelDef, opts) | |
60 | } | |
61 | ||
62 | return nil, newDecodeModelError( | |
63 | fmt.Sprintf("endpoints version %s, not supported", version), nil) | |
64 | } | |
65 | ||
66 | func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resolver, error) { | |
67 | b, ok := modelDef["partitions"] | |
68 | if !ok { | |
69 | return nil, newDecodeModelError("endpoints model missing partitions", nil) | |
70 | } | |
71 | ||
72 | ps := partitions{} | |
73 | if err := json.Unmarshal(b, &ps); err != nil { | |
74 | return nil, newDecodeModelError("failed to decode endpoints model", err) | |
75 | } | |
76 | ||
77 | if opts.SkipCustomizations { | |
78 | return ps, nil | |
79 | } | |
80 | ||
81 | // Customization | |
82 | for i := 0; i < len(ps); i++ { | |
83 | p := &ps[i] | |
84 | custAddEC2Metadata(p) | |
85 | custAddS3DualStack(p) | |
86 | custRmIotDataService(p) | |
87 | custFixAppAutoscalingChina(p) | |
88 | custFixAppAutoscalingUsGov(p) | |
89 | } | |
90 | ||
91 | return ps, nil | |
92 | } | |
93 | ||
94 | func custAddS3DualStack(p *partition) { | |
95 | if p.ID != "aws" { | |
96 | return | |
97 | } | |
98 | ||
99 | custAddDualstack(p, "s3") | |
100 | custAddDualstack(p, "s3-control") | |
101 | } | |
102 | ||
103 | func custAddDualstack(p *partition, svcName string) { | |
104 | s, ok := p.Services[svcName] | |
105 | if !ok { | |
106 | return | |
107 | } | |
108 | ||
109 | s.Defaults.HasDualStack = boxedTrue | |
110 | s.Defaults.DualStackHostname = "{service}.dualstack.{region}.{dnsSuffix}" | |
111 | ||
112 | p.Services[svcName] = s | |
113 | } | |
114 | ||
115 | func custAddEC2Metadata(p *partition) { | |
116 | p.Services["ec2metadata"] = service{ | |
117 | IsRegionalized: boxedFalse, | |
118 | PartitionEndpoint: "aws-global", | |
119 | Endpoints: endpoints{ | |
120 | "aws-global": endpoint{ | |
121 | Hostname: "169.254.169.254/latest", | |
122 | Protocols: []string{"http"}, | |
123 | }, | |
124 | }, | |
125 | } | |
126 | } | |
127 | ||
128 | func custRmIotDataService(p *partition) { | |
129 | delete(p.Services, "data.iot") | |
130 | } | |
131 | ||
132 | func custFixAppAutoscalingChina(p *partition) { | |
133 | if p.ID != "aws-cn" { | |
134 | return | |
135 | } | |
136 | ||
137 | const serviceName = "application-autoscaling" | |
138 | s, ok := p.Services[serviceName] | |
139 | if !ok { | |
140 | return | |
141 | } | |
142 | ||
143 | const expectHostname = `autoscaling.{region}.amazonaws.com` | |
144 | if e, a := s.Defaults.Hostname, expectHostname; e != a { | |
145 | fmt.Printf("custFixAppAutoscalingChina: ignoring customization, expected %s, got %s\n", e, a) | |
146 | return | |
147 | } | |
148 | ||
149 | s.Defaults.Hostname = expectHostname + ".cn" | |
150 | p.Services[serviceName] = s | |
151 | } | |
152 | ||
153 | func custFixAppAutoscalingUsGov(p *partition) { | |
154 | if p.ID != "aws-us-gov" { | |
155 | return | |
156 | } | |
157 | ||
158 | const serviceName = "application-autoscaling" | |
159 | s, ok := p.Services[serviceName] | |
160 | if !ok { | |
161 | return | |
162 | } | |
163 | ||
164 | if a := s.Defaults.CredentialScope.Service; a != "" { | |
165 | fmt.Printf("custFixAppAutoscalingUsGov: ignoring customization, expected empty credential scope service, got %s\n", a) | |
166 | return | |
167 | } | |
168 | ||
169 | if a := s.Defaults.Hostname; a != "" { | |
170 | fmt.Printf("custFixAppAutoscalingUsGov: ignoring customization, expected empty hostname, got %s\n", a) | |
171 | return | |
172 | } | |
173 | ||
174 | s.Defaults.CredentialScope.Service = "application-autoscaling" | |
175 | s.Defaults.Hostname = "autoscaling.{region}.amazonaws.com" | |
176 | ||
177 | p.Services[serviceName] = s | |
178 | } | |
179 | ||
180 | type decodeModelError struct { | |
181 | awsError | |
182 | } | |
183 | ||
184 | func newDecodeModelError(msg string, err error) decodeModelError { | |
185 | return decodeModelError{ | |
186 | awsError: awserr.New("DecodeEndpointsModelError", msg, err), | |
187 | } | |
188 | } |