1
2
3 package ecr
4
5 import (
6 "context"
7 "errors"
8 "fmt"
9 "github.com/aws/aws-sdk-go-v2/aws"
10 awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
11 "github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn"
12 internalendpoints "github.com/aws/aws-sdk-go-v2/service/ecr/internal/endpoints"
13 smithyendpoints "github.com/aws/smithy-go/endpoints"
14 "github.com/aws/smithy-go/middleware"
15 "github.com/aws/smithy-go/ptr"
16 smithyhttp "github.com/aws/smithy-go/transport/http"
17 "net/http"
18 "net/url"
19 "strings"
20 )
21
22
23 type EndpointResolverOptions = internalendpoints.Options
24
25
26 type EndpointResolver interface {
27 ResolveEndpoint(region string, options EndpointResolverOptions) (aws.Endpoint, error)
28 }
29
30 var _ EndpointResolver = &internalendpoints.Resolver{}
31
32
33 func NewDefaultEndpointResolver() *internalendpoints.Resolver {
34 return internalendpoints.New()
35 }
36
37
38
39
40 type EndpointResolverFunc func(region string, options EndpointResolverOptions) (aws.Endpoint, error)
41
42 func (fn EndpointResolverFunc) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) {
43 return fn(region, options)
44 }
45
46
47
48
49
50
51 func EndpointResolverFromURL(url string, optFns ...func(*aws.Endpoint)) EndpointResolver {
52 e := aws.Endpoint{URL: url, Source: aws.EndpointSourceCustom}
53 for _, fn := range optFns {
54 fn(&e)
55 }
56
57 return EndpointResolverFunc(
58 func(region string, options EndpointResolverOptions) (aws.Endpoint, error) {
59 if len(e.SigningRegion) == 0 {
60 e.SigningRegion = region
61 }
62 return e, nil
63 },
64 )
65 }
66
67 type ResolveEndpoint struct {
68 Resolver EndpointResolver
69 Options EndpointResolverOptions
70 }
71
72 func (*ResolveEndpoint) ID() string {
73 return "ResolveEndpoint"
74 }
75
76 func (m *ResolveEndpoint) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) (
77 out middleware.SerializeOutput, metadata middleware.Metadata, err error,
78 ) {
79 if !awsmiddleware.GetRequiresLegacyEndpoints(ctx) {
80 return next.HandleSerialize(ctx, in)
81 }
82
83 req, ok := in.Request.(*smithyhttp.Request)
84 if !ok {
85 return out, metadata, fmt.Errorf("unknown transport type %T", in.Request)
86 }
87
88 if m.Resolver == nil {
89 return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil")
90 }
91
92 eo := m.Options
93 eo.Logger = middleware.GetLogger(ctx)
94
95 var endpoint aws.Endpoint
96 endpoint, err = m.Resolver.ResolveEndpoint(awsmiddleware.GetRegion(ctx), eo)
97 if err != nil {
98 nf := (&aws.EndpointNotFoundError{})
99 if errors.As(err, &nf) {
100 ctx = awsmiddleware.SetRequiresLegacyEndpoints(ctx, false)
101 return next.HandleSerialize(ctx, in)
102 }
103 return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err)
104 }
105
106 req.URL, err = url.Parse(endpoint.URL)
107 if err != nil {
108 return out, metadata, fmt.Errorf("failed to parse endpoint URL: %w", err)
109 }
110
111 if len(awsmiddleware.GetSigningName(ctx)) == 0 {
112 signingName := endpoint.SigningName
113 if len(signingName) == 0 {
114 signingName = "ecr"
115 }
116 ctx = awsmiddleware.SetSigningName(ctx, signingName)
117 }
118 ctx = awsmiddleware.SetEndpointSource(ctx, endpoint.Source)
119 ctx = smithyhttp.SetHostnameImmutable(ctx, endpoint.HostnameImmutable)
120 ctx = awsmiddleware.SetSigningRegion(ctx, endpoint.SigningRegion)
121 ctx = awsmiddleware.SetPartitionID(ctx, endpoint.PartitionID)
122 return next.HandleSerialize(ctx, in)
123 }
124 func addResolveEndpointMiddleware(stack *middleware.Stack, o Options) error {
125 return stack.Serialize.Insert(&ResolveEndpoint{
126 Resolver: o.EndpointResolver,
127 Options: o.EndpointOptions,
128 }, "OperationSerializer", middleware.Before)
129 }
130
131 func removeResolveEndpointMiddleware(stack *middleware.Stack) error {
132 _, err := stack.Serialize.Remove((&ResolveEndpoint{}).ID())
133 return err
134 }
135
136 type wrappedEndpointResolver struct {
137 awsResolver aws.EndpointResolverWithOptions
138 }
139
140 func (w *wrappedEndpointResolver) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) {
141 return w.awsResolver.ResolveEndpoint(ServiceID, region, options)
142 }
143
144 type awsEndpointResolverAdaptor func(service, region string) (aws.Endpoint, error)
145
146 func (a awsEndpointResolverAdaptor) ResolveEndpoint(service, region string, options ...interface{}) (aws.Endpoint, error) {
147 return a(service, region)
148 }
149
150 var _ aws.EndpointResolverWithOptions = awsEndpointResolverAdaptor(nil)
151
152
153
154
155
156
157
158 func withEndpointResolver(awsResolver aws.EndpointResolver, awsResolverWithOptions aws.EndpointResolverWithOptions) EndpointResolver {
159 var resolver aws.EndpointResolverWithOptions
160
161 if awsResolverWithOptions != nil {
162 resolver = awsResolverWithOptions
163 } else if awsResolver != nil {
164 resolver = awsEndpointResolverAdaptor(awsResolver.ResolveEndpoint)
165 }
166
167 return &wrappedEndpointResolver{
168 awsResolver: resolver,
169 }
170 }
171
172 func finalizeClientEndpointResolverOptions(options *Options) {
173 options.EndpointOptions.LogDeprecated = options.ClientLogMode.IsDeprecatedUsage()
174
175 if len(options.EndpointOptions.ResolvedRegion) == 0 {
176 const fipsInfix = "-fips-"
177 const fipsPrefix = "fips-"
178 const fipsSuffix = "-fips"
179
180 if strings.Contains(options.Region, fipsInfix) ||
181 strings.Contains(options.Region, fipsPrefix) ||
182 strings.Contains(options.Region, fipsSuffix) {
183 options.EndpointOptions.ResolvedRegion = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(
184 options.Region, fipsInfix, "-"), fipsPrefix, ""), fipsSuffix, "")
185 options.EndpointOptions.UseFIPSEndpoint = aws.FIPSEndpointStateEnabled
186 }
187 }
188
189 }
190
191 func resolveEndpointResolverV2(options *Options) {
192 if options.EndpointResolverV2 == nil {
193 options.EndpointResolverV2 = NewDefaultEndpointResolverV2()
194 }
195 }
196
197
198
199 func mapPseudoRegion(pr string) (region string, fips aws.FIPSEndpointState) {
200 const fipsInfix = "-fips-"
201 const fipsPrefix = "fips-"
202 const fipsSuffix = "-fips"
203
204 if strings.Contains(pr, fipsInfix) ||
205 strings.Contains(pr, fipsPrefix) ||
206 strings.Contains(pr, fipsSuffix) {
207 region = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(
208 pr, fipsInfix, "-"), fipsPrefix, ""), fipsSuffix, "")
209 fips = aws.FIPSEndpointStateEnabled
210 } else {
211 region = pr
212 }
213
214 return region, fips
215 }
216
217
218
219 type builtInParameterResolver interface {
220 ResolveBuiltIns(*EndpointParameters) error
221 }
222
223
224
225 type builtInResolver struct {
226
227 Region string
228
229
230 UseDualStack aws.DualStackEndpointState
231
232
233 UseFIPS aws.FIPSEndpointState
234
235
236 Endpoint *string
237 }
238
239
240
241 func (b *builtInResolver) ResolveBuiltIns(params *EndpointParameters) error {
242
243 region, _ := mapPseudoRegion(b.Region)
244 if len(region) == 0 {
245 return fmt.Errorf("Could not resolve AWS::Region")
246 } else {
247 params.Region = aws.String(region)
248 }
249 if b.UseDualStack == aws.DualStackEndpointStateEnabled {
250 params.UseDualStack = aws.Bool(true)
251 } else {
252 params.UseDualStack = aws.Bool(false)
253 }
254 if b.UseFIPS == aws.FIPSEndpointStateEnabled {
255 params.UseFIPS = aws.Bool(true)
256 } else {
257 params.UseFIPS = aws.Bool(false)
258 }
259 params.Endpoint = b.Endpoint
260 return nil
261 }
262
263
264
265 type EndpointParameters struct {
266
267
268
269
270
271
272 Region *string
273
274
275
276
277
278
279
280
281 UseDualStack *bool
282
283
284
285
286
287
288
289
290
291 UseFIPS *bool
292
293
294
295
296
297
298
299 Endpoint *string
300 }
301
302
303 func (p EndpointParameters) ValidateRequired() error {
304 if p.UseDualStack == nil {
305 return fmt.Errorf("parameter UseDualStack is required")
306 }
307
308 if p.UseFIPS == nil {
309 return fmt.Errorf("parameter UseFIPS is required")
310 }
311
312 return nil
313 }
314
315
316
317 func (p EndpointParameters) WithDefaults() EndpointParameters {
318 if p.UseDualStack == nil {
319 p.UseDualStack = ptr.Bool(false)
320 }
321
322 if p.UseFIPS == nil {
323 p.UseFIPS = ptr.Bool(false)
324 }
325 return p
326 }
327
328
329 type EndpointResolverV2 interface {
330
331
332 ResolveEndpoint(ctx context.Context, params EndpointParameters) (
333 smithyendpoints.Endpoint, error,
334 )
335 }
336
337
338 type resolver struct{}
339
340 func NewDefaultEndpointResolverV2() EndpointResolverV2 {
341 return &resolver{}
342 }
343
344
345
346 func (r *resolver) ResolveEndpoint(
347 ctx context.Context, params EndpointParameters,
348 ) (
349 endpoint smithyendpoints.Endpoint, err error,
350 ) {
351 params = params.WithDefaults()
352 if err = params.ValidateRequired(); err != nil {
353 return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err)
354 }
355 _UseDualStack := *params.UseDualStack
356 _UseFIPS := *params.UseFIPS
357
358 if exprVal := params.Endpoint; exprVal != nil {
359 _Endpoint := *exprVal
360 _ = _Endpoint
361 if _UseFIPS == true {
362 return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported")
363 }
364 if _UseDualStack == true {
365 return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported")
366 }
367 uriString := _Endpoint
368
369 uri, err := url.Parse(uriString)
370 if err != nil {
371 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
372 }
373
374 return smithyendpoints.Endpoint{
375 URI: *uri,
376 Headers: http.Header{},
377 }, nil
378 }
379 if exprVal := params.Region; exprVal != nil {
380 _Region := *exprVal
381 _ = _Region
382 if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil {
383 _PartitionResult := *exprVal
384 _ = _PartitionResult
385 if _UseFIPS == true {
386 if _UseDualStack == true {
387 if true == _PartitionResult.SupportsFIPS {
388 if true == _PartitionResult.SupportsDualStack {
389 uriString := func() string {
390 var out strings.Builder
391 out.WriteString("https://api.ecr-fips.")
392 out.WriteString(_Region)
393 out.WriteString(".")
394 out.WriteString(_PartitionResult.DualStackDnsSuffix)
395 return out.String()
396 }()
397
398 uri, err := url.Parse(uriString)
399 if err != nil {
400 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
401 }
402
403 return smithyendpoints.Endpoint{
404 URI: *uri,
405 Headers: http.Header{},
406 }, nil
407 }
408 }
409 return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both")
410 }
411 }
412 if _UseFIPS == true {
413 if true == _PartitionResult.SupportsFIPS {
414 if "aws" == _PartitionResult.Name {
415 uriString := func() string {
416 var out strings.Builder
417 out.WriteString("https://ecr-fips.")
418 out.WriteString(_Region)
419 out.WriteString(".amazonaws.com")
420 return out.String()
421 }()
422
423 uri, err := url.Parse(uriString)
424 if err != nil {
425 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
426 }
427
428 return smithyendpoints.Endpoint{
429 URI: *uri,
430 Headers: http.Header{},
431 }, nil
432 }
433 if "aws-us-gov" == _PartitionResult.Name {
434 uriString := func() string {
435 var out strings.Builder
436 out.WriteString("https://ecr-fips.")
437 out.WriteString(_Region)
438 out.WriteString(".amazonaws.com")
439 return out.String()
440 }()
441
442 uri, err := url.Parse(uriString)
443 if err != nil {
444 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
445 }
446
447 return smithyendpoints.Endpoint{
448 URI: *uri,
449 Headers: http.Header{},
450 }, nil
451 }
452 uriString := func() string {
453 var out strings.Builder
454 out.WriteString("https://api.ecr-fips.")
455 out.WriteString(_Region)
456 out.WriteString(".")
457 out.WriteString(_PartitionResult.DnsSuffix)
458 return out.String()
459 }()
460
461 uri, err := url.Parse(uriString)
462 if err != nil {
463 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
464 }
465
466 return smithyendpoints.Endpoint{
467 URI: *uri,
468 Headers: http.Header{},
469 }, nil
470 }
471 return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS")
472 }
473 if _UseDualStack == true {
474 if true == _PartitionResult.SupportsDualStack {
475 uriString := func() string {
476 var out strings.Builder
477 out.WriteString("https://api.ecr.")
478 out.WriteString(_Region)
479 out.WriteString(".")
480 out.WriteString(_PartitionResult.DualStackDnsSuffix)
481 return out.String()
482 }()
483
484 uri, err := url.Parse(uriString)
485 if err != nil {
486 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
487 }
488
489 return smithyendpoints.Endpoint{
490 URI: *uri,
491 Headers: http.Header{},
492 }, nil
493 }
494 return endpoint, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack")
495 }
496 uriString := func() string {
497 var out strings.Builder
498 out.WriteString("https://api.ecr.")
499 out.WriteString(_Region)
500 out.WriteString(".")
501 out.WriteString(_PartitionResult.DnsSuffix)
502 return out.String()
503 }()
504
505 uri, err := url.Parse(uriString)
506 if err != nil {
507 return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
508 }
509
510 return smithyendpoints.Endpoint{
511 URI: *uri,
512 Headers: http.Header{},
513 }, nil
514 }
515 return endpoint, fmt.Errorf("Endpoint resolution failed. Invalid operation or environment input.")
516 }
517 return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region")
518 }
519
View as plain text