@@ -17,11 +17,18 @@ import (
1717 "github.com/aws/aws-sdk-go/aws"
1818 awsecr "github.com/aws/aws-sdk-go/service/ecr"
1919 "github.com/aws/aws-sdk-go/service/lambda"
20+ "github.com/hashicorp/waypoint-plugin-sdk/component"
2021 "github.com/hashicorp/waypoint-plugin-sdk/docs"
2122 "github.com/hashicorp/waypoint-plugin-sdk/terminal"
2223 "github.com/hashicorp/waypoint/builtin/aws/ecr"
2324 "github.com/hashicorp/waypoint/builtin/aws/utils"
2425
26+ "encoding/base64"
27+ "encoding/json"
28+
29+ wpdocker "github.com/hashicorp/waypoint/builtin/docker"
30+ wpdockerpull "github.com/hashicorp/waypoint/builtin/docker/pull"
31+
2532 validation "github.com/go-ozzo/ozzo-validation/v4"
2633)
2734
@@ -35,12 +42,22 @@ func (b *Builder) BuildFunc() interface{} {
3542 return b .Build
3643}
3744
45+ // BuildFunc implements component.BuilderODR
46+ func (b * Builder ) BuildODRFunc () interface {} {
47+ return b .BuildODR
48+ }
49+
3850// Config is the configuration structure for the registry.
3951type Config struct {
4052 Region string `hcl:"region,optional"`
4153 Repository string `hcl:"repository,attr"`
4254 Tag string `hcl:"tag,attr"`
4355 ForceArchitecture string `hcl:"force_architecture,optional"`
56+ DisableCEB bool `hcl:"disable_entrypoint,optional"`
57+ }
58+
59+ type authInfo struct {
60+ Base64Token * string
4461}
4562
4663func (b * Builder ) Documentation () (* docs.Documentation , error ) {
@@ -150,7 +167,125 @@ func (b *Builder) Config() (interface{}, error) {
150167
151168// Build
152169func (b * Builder ) Build (ctx context.Context , ui terminal.UI , log hclog.Logger ) (* ecr.Image , error ) {
170+ sg := ui .StepGroup ()
171+ ecrImage , buildAuthInfo , err := b .getErcImage (ctx , ui , log , sg )
172+ if err != nil {
173+ return nil , err
174+ }
175+
176+ if b .config .DisableCEB {
177+ return ecrImage , nil
178+ }
179+
180+ repoUser , repoPass , err := credentialsFromEcr (* buildAuthInfo .Base64Token )
181+ if err != nil {
182+ return nil , err
183+ }
184+
185+ // Use the authorization token to create the base64 package that
186+ // Docker requires to perform authentication.
187+ authInfo := map [string ]string {
188+ "username" : repoUser ,
189+ "password" : repoPass ,
190+ }
191+
192+ authData , err := json .Marshal (authInfo )
193+ if err != nil {
194+ return nil , err
195+ }
196+ encodedAuth := base64 .StdEncoding .EncodeToString (authData )
197+
198+ pullBuilder := & wpdockerpull.Builder {}
199+ raw , err := pullBuilder .Config ()
200+ if err != nil {
201+ return nil , err
202+ }
203+ pullConfig := raw .(* wpdockerpull.BuilderConfig )
204+ pullConfig .EncodedAuth = encodedAuth
205+ pullConfig .Image = ecrImage .Image
206+ pullConfig .Tag = ecrImage .Tag
207+ pullConfig .DisableCEB = b .config .DisableCEB
208+
209+ img , err := pullBuilder .Build (wpdockerpull.BuildArgs {
210+ Ctx : ctx ,
211+ UI : ui ,
212+ Log : log ,
213+ HasRegistry : true ,
214+ })
215+
216+ if err != nil {
217+ return nil , err
218+ }
219+ return ecr .DockerToEcrImageMapper (img ), nil
220+ }
221+
222+ // Build
223+ func (b * Builder ) BuildODR (ctx context.Context , ui terminal.UI , log hclog.Logger , src * component.Source , ai * wpdocker.AccessInfo ) (* ecr.Image , error ) {
224+ sg := ui .StepGroup ()
225+ ecrImage , buildAuthInfo , err := b .getErcImage (ctx , ui , log , sg )
226+ if err != nil {
227+ return nil , err
228+ }
229+
230+ if b .config .DisableCEB {
231+ return ecrImage , nil
232+ }
233+
234+ repoUser , repoPass , err := credentialsFromEcr (* buildAuthInfo .Base64Token )
235+ if err != nil {
236+ return nil , err
237+ }
238+
239+ // Use the authorization token to create the base64 package that
240+ // Docker requires to perform authentication.
241+ authInfo := map [string ]string {
242+ "username" : repoUser ,
243+ "password" : repoPass ,
244+ }
245+
246+ authData , err := json .Marshal (authInfo )
247+ if err != nil {
248+ return nil , err
249+ }
250+ encodedAuth := base64 .StdEncoding .EncodeToString (authData )
251+
252+ pullBuilder := & wpdockerpull.Builder {}
253+ raw , err := pullBuilder .Config ()
254+ if err != nil {
255+ return nil , err
256+ }
257+ pullConfig := raw .(* wpdockerpull.BuilderConfig )
258+ pullConfig .EncodedAuth = encodedAuth
259+ pullConfig .Image = ecrImage .Image
260+ pullConfig .Tag = ecrImage .Tag
261+ pullConfig .DisableCEB = b .config .DisableCEB
153262
263+ img , err := pullBuilder .BuildODR (ctx , ui , src , log , ai )
264+ if err != nil {
265+ return nil , err
266+ }
267+ return ecr .DockerToEcrImageMapper (img ), nil
268+ }
269+
270+ // CredentialsFromEcr returns the username and password present in the encoded
271+ // auth string. This encoded auth string is one that users can pass as authentication
272+ // information to registry.
273+ func credentialsFromEcr (encodedAuth string ) (string , string , error ) {
274+ // Create a reader that base64 decodes our encoded auth and then splits off USER:PASS
275+ dec , err := base64 .StdEncoding .DecodeString (encodedAuth )
276+ if err != nil {
277+ return "" , "" , fmt .Errorf ("invalid encoded auth string: %s" , encodedAuth [:5 ])
278+ }
279+
280+ userPassSplit := strings .SplitN (string (dec ), ":" , 2 )
281+ if len (userPassSplit ) != 2 {
282+ return "" , "" , fmt .Errorf ("ecr credentials were invalid or did not container user:password. Number of elements in split: %d" , len (userPassSplit ))
283+ }
284+
285+ return userPassSplit [0 ], userPassSplit [1 ], nil
286+ }
287+
288+ func (b * Builder ) getErcImage (ctx context.Context , ui terminal.UI , log hclog.Logger , sg terminal.StepGroup ) (* ecr.Image , * authInfo , error ) {
154289 // If there is no region setup. Try and load it from environment variables.
155290 if b .config .Region == "" {
156291 b .config .Region = os .Getenv ("AWS_REGION" )
@@ -161,12 +296,11 @@ func (b *Builder) Build(ctx context.Context, ui terminal.UI, log hclog.Logger) (
161296 }
162297
163298 if b .config .Region == "" {
164- return nil , status .Error (
299+ return nil , nil , status .Error (
165300 codes .FailedPrecondition ,
166301 "Please set your aws region in the deployment config, or set the environment variable 'AWS_REGION' or 'AWS_DEFAULT_REGION'" )
167302 }
168303
169- sg := ui .StepGroup ()
170304 step := sg .Add ("" )
171305 defer func () {
172306 if step != nil {
@@ -183,7 +317,7 @@ func (b *Builder) Build(ctx context.Context, ui terminal.UI, log hclog.Logger) (
183317
184318 if err != nil {
185319 log .Error ("error connecting to AWS" , "error" , err )
186- return nil , err
320+ return nil , nil , err
187321 }
188322
189323 step .Done ()
@@ -195,6 +329,15 @@ func (b *Builder) Build(ctx context.Context, ui terminal.UI, log hclog.Logger) (
195329 cfgTag := b .config .Tag
196330 cfgRepository := b .config .Repository
197331
332+ tokenResp , err := ecrsvc .GetAuthorizationToken (& awsecr.GetAuthorizationTokenInput {})
333+ if err != nil {
334+ log .Error ("error getting authorization token" , "error" , err )
335+ return nil , nil , err
336+ }
337+ // docs say the token is good for all registries the user has access to. so just grab the first token
338+ token := tokenResp .AuthorizationData [0 ].AuthorizationToken
339+ log .Debug ("successfully retrieved authorization token" )
340+
198341 // should be acceptable to filter images by TAGGED status
199342 imgs , err := ecrsvc .DescribeImages (& awsecr.DescribeImagesInput {
200343 RepositoryName : aws .String (cfgRepository ),
@@ -204,12 +347,12 @@ func (b *Builder) Build(ctx context.Context, ui terminal.UI, log hclog.Logger) (
204347 })
205348 if err != nil {
206349 log .Error ("error describing images" , "error" , err , "repository" , cfgRepository )
207- return nil , err
350+ return nil , nil , err
208351 }
209352
210353 if len (imgs .ImageDetails ) == 0 {
211354 log .Error ("no tagged images found" , "repository" , cfgRepository )
212- return nil , status .Error (codes .FailedPrecondition , "No images found" )
355+ return nil , nil , status .Error (codes .FailedPrecondition , "No images found" )
213356 }
214357 log .Debug ("found images" , "image count" , len (imgs .ImageDetails ))
215358
@@ -248,11 +391,11 @@ func (b *Builder) Build(ctx context.Context, ui terminal.UI, log hclog.Logger) (
248391 // if no image was found, return an error
249392 if output .Image == "" {
250393 log .Error ("no matching image was found" , "tag" , cfgTag , "repository" , cfgRepository )
251- return nil , status .Error (codes .FailedPrecondition , "No matching tags found" )
394+ return nil , nil , status .Error (codes .FailedPrecondition , "No matching tags found" )
252395 }
253396
254397 step .Update ("Using image: " + output .Image + ":" + output .Tag )
255398 step .Done ()
256399
257- return & output , nil
400+ return & output , & authInfo { Base64Token : token }, nil
258401}
0 commit comments