11package main
22
33import (
4+ "context"
45 "encoding/base64"
6+ "errors"
57 "fmt"
6- "io/ioutil"
78 "log"
89 "os"
910 "os/exec"
1011 "strconv"
1112 "strings"
1213
14+ "github.com/aws/aws-sdk-go-v2/aws"
15+ "github.com/aws/aws-sdk-go-v2/config"
16+ "github.com/aws/aws-sdk-go-v2/credentials/stscreds"
17+ "github.com/aws/aws-sdk-go-v2/service/ecr"
18+ ecrtypes "github.com/aws/aws-sdk-go-v2/service/ecr/types"
19+ "github.com/aws/aws-sdk-go-v2/service/sts"
1320 "github.com/joho/godotenv"
1421 "github.com/sirupsen/logrus"
1522
16- "github.com/aws/aws-sdk-go/aws"
17- "github.com/aws/aws-sdk-go/aws/awserr"
18- "github.com/aws/aws-sdk-go/aws/credentials/stscreds"
19- "github.com/aws/aws-sdk-go/aws/session"
20- "github.com/aws/aws-sdk-go/service/ecr"
21-
2223 docker "github.com/drone-plugins/drone-docker"
2324)
2425
25- type ecrAPI interface {
26- DescribeImages (* ecr.DescribeImagesInput ) (* ecr.DescribeImagesOutput , error )
27- }
28-
2926const defaultRegion = "us-east-1"
3027
3128func main () {
32- // Load env-file if it exists first
3329 if env := os .Getenv ("PLUGIN_ENV_FILE" ); env != "" {
3430 godotenv .Load (env )
3531 }
@@ -50,7 +46,6 @@ func main() {
5046 skipPushIfTagExists = parseBoolOrDefault (false , getenv ("PLUGIN_SKIP_PUSH_IF_TAG_EXISTS" ))
5147 )
5248
53- // set the region
5449 if region == "" {
5550 region = defaultRegion
5651 }
@@ -62,13 +57,15 @@ func main() {
6257 os .Setenv ("AWS_SECRET_ACCESS_KEY" , secret )
6358 }
6459
65- sess , err := session .NewSession (& aws.Config {Region : & region })
60+ ctx := context .Background ()
61+
62+ cfg , err := config .LoadDefaultConfig (ctx , config .WithRegion (region ))
6663 if err != nil {
67- log .Fatal (fmt .Sprintf ("error creating aws session : %v" , err ))
64+ log .Fatal (fmt .Sprintf ("error creating aws config : %v" , err ))
6865 }
6966
70- svc := getECRClient (sess , assumeRole , externalId , idToken )
71- username , password , defaultRegistry , err := getAuthInfo (svc )
67+ svc := getECRClient (cfg , assumeRole , externalId , idToken )
68+ username , password , defaultRegistry , err := getAuthInfo (ctx , svc )
7269
7370 if registry == "" {
7471 registry = defaultRegistry
@@ -83,32 +80,32 @@ func main() {
8380 }
8481
8582 if create {
86- err = ensureRepoExists (svc , trimHostname (repo , registry ), scanOnPush )
83+ err = ensureRepoExists (ctx , svc , trimHostname (repo , registry ), scanOnPush )
8784 if err != nil {
8885 log .Fatal (fmt .Sprintf ("error creating ECR repo: %v" , err ))
8986 }
90- err = updateImageScannningConfig ( svc , trimHostname (repo , registry ), scanOnPush )
87+ err = updateImageScanningConfig ( ctx , svc , trimHostname (repo , registry ), scanOnPush )
9188 if err != nil {
9289 log .Fatal (fmt .Sprintf ("error updating scan on push for ECR repo: %v" , err ))
9390 }
9491 }
9592
9693 if lifecyclePolicy != "" {
97- p , err := ioutil .ReadFile (lifecyclePolicy )
94+ p , err := os .ReadFile (lifecyclePolicy )
9895 if err != nil {
9996 log .Fatal (err )
10097 }
101- if err := uploadLifeCyclePolicy (svc , string (p ), trimHostname (repo , registry )); err != nil {
98+ if err := uploadLifeCyclePolicy (ctx , svc , string (p ), trimHostname (repo , registry )); err != nil {
10299 log .Fatal (fmt .Sprintf ("error uploading ECR lifecycle policy: %v" , err ))
103100 }
104101 }
105102
106103 if repositoryPolicy != "" {
107- p , err := ioutil .ReadFile (repositoryPolicy )
104+ p , err := os .ReadFile (repositoryPolicy )
108105 if err != nil {
109106 log .Fatal (err )
110107 }
111- if err := uploadRepositoryPolicy (svc , string (p ), trimHostname (repo , registry )); err != nil {
108+ if err := uploadRepositoryPolicy (ctx , svc , string (p ), trimHostname (repo , registry )); err != nil {
112109 log .Fatal (fmt .Sprintf ("error uploading ECR repository policy. %v" , err ))
113110 }
114111 }
@@ -119,7 +116,6 @@ func main() {
119116 os .Setenv ("DOCKER_PASSWORD" , password )
120117 os .Setenv ("PLUGIN_REGISTRY_TYPE" , "ECR" )
121118
122- // Skip if tag already exits for both mutable and immutable repos
123119 if skipPushIfTagExists {
124120 tagInput := getenv ("PLUGIN_TAG" , "PLUGIN_TAGS" )
125121 var tags []string
@@ -136,7 +132,7 @@ func main() {
136132
137133 repositoryName := trimHostname (repo , registry )
138134 for _ , t := range tags {
139- exists , err := tagExists (svc , repositoryName , t )
135+ exists , err := tagExists (ctx , svc , repositoryName , t )
140136 if err != nil {
141137 logrus .Fatalf ("Error checking if image exists for tag %s: %v" , t , err )
142138 }
@@ -147,7 +143,6 @@ func main() {
147143 }
148144 }
149145
150- // invoke the base docker plugin binary
151146 cmd := exec .Command (docker .GetDroneDockerExecCmd ())
152147 cmd .Stdout = os .Stdout
153148 cmd .Stderr = os .Stderr
@@ -162,57 +157,63 @@ func trimHostname(repo, registry string) string {
162157 return repo
163158}
164159
165- func ensureRepoExists (svc * ecr.ECR , name string , scanOnPush bool ) (err error ) {
166- input := & ecr.CreateRepositoryInput {}
167- input .SetRepositoryName (name )
168- input .SetImageScanningConfiguration (& ecr.ImageScanningConfiguration {ScanOnPush : & scanOnPush })
169- _ , err = svc .CreateRepository (input )
160+ func ensureRepoExists (ctx context.Context , svc * ecr.Client , name string , scanOnPush bool ) error {
161+ _ , err := svc .CreateRepository (ctx , & ecr.CreateRepositoryInput {
162+ RepositoryName : aws .String (name ),
163+ ImageScanningConfiguration : & ecrtypes.ImageScanningConfiguration {
164+ ScanOnPush : scanOnPush ,
165+ },
166+ })
170167 if err != nil {
171- if aerr , ok := err .(awserr. Error ); ok && aerr . Code () == ecr . ErrCodeRepositoryAlreadyExistsException {
172- // eat it, we skip checking for existing to save two requests
173- err = nil
168+ var rae * ecrtypes. RepositoryAlreadyExistsException
169+ if errors . As ( err , & rae ) {
170+ return nil
174171 }
172+ return err
175173 }
176-
177- return
174+ return nil
178175}
179176
180- func updateImageScannningConfig (svc * ecr.ECR , name string , scanOnPush bool ) (err error ) {
181- input := & ecr.PutImageScanningConfigurationInput {}
182- input .SetRepositoryName (name )
183- input .SetImageScanningConfiguration (& ecr.ImageScanningConfiguration {ScanOnPush : & scanOnPush })
184- _ , err = svc .PutImageScanningConfiguration (input )
185-
177+ func updateImageScanningConfig (ctx context.Context , svc * ecr.Client , name string , scanOnPush bool ) error {
178+ _ , err := svc .PutImageScanningConfiguration (ctx , & ecr.PutImageScanningConfigurationInput {
179+ RepositoryName : aws .String (name ),
180+ ImageScanningConfiguration : & ecrtypes.ImageScanningConfiguration {
181+ ScanOnPush : scanOnPush ,
182+ },
183+ })
186184 return err
187185}
188186
189- func uploadLifeCyclePolicy (svc * ecr.ECR , lifecyclePolicy string , name string ) (err error ) {
190- input := & ecr.PutLifecyclePolicyInput {}
191- input .SetLifecyclePolicyText (lifecyclePolicy )
192- input .SetRepositoryName (name )
193- _ , err = svc .PutLifecyclePolicy (input )
194-
187+ func uploadLifeCyclePolicy (ctx context.Context , svc * ecr.Client , lifecyclePolicy string , name string ) error {
188+ _ , err := svc .PutLifecyclePolicy (ctx , & ecr.PutLifecyclePolicyInput {
189+ LifecyclePolicyText : aws .String (lifecyclePolicy ),
190+ RepositoryName : aws .String (name ),
191+ })
195192 return err
196193}
197194
198- func uploadRepositoryPolicy (svc * ecr.ECR , repositoryPolicy string , name string ) (err error ) {
199- input := & ecr.SetRepositoryPolicyInput {}
200- input .SetPolicyText (repositoryPolicy )
201- input .SetRepositoryName (name )
202- _ , err = svc .SetRepositoryPolicy (input )
203-
195+ func uploadRepositoryPolicy (ctx context.Context , svc * ecr.Client , repositoryPolicy string , name string ) error {
196+ _ , err := svc .SetRepositoryPolicy (ctx , & ecr.SetRepositoryPolicyInput {
197+ PolicyText : aws .String (repositoryPolicy ),
198+ RepositoryName : aws .String (name ),
199+ })
204200 return err
205201}
206202
207- func getAuthInfo (svc * ecr.ECR ) (username , password , registry string , err error ) {
203+ func getAuthInfo (ctx context. Context , svc * ecr.Client ) (username , password , registry string , err error ) {
208204 var result * ecr.GetAuthorizationTokenOutput
209205 var decoded []byte
210206
211- result , err = svc .GetAuthorizationToken (& ecr.GetAuthorizationTokenInput {})
207+ result , err = svc .GetAuthorizationToken (ctx , & ecr.GetAuthorizationTokenInput {})
212208 if err != nil {
213209 return
214210 }
215211
212+ if len (result .AuthorizationData ) == 0 {
213+ err = fmt .Errorf ("no authorization data returned from ECR" )
214+ return
215+ }
216+
216217 auth := result .AuthorizationData [0 ]
217218 token := * auth .AuthorizationToken
218219 decoded , err = base64 .StdEncoding .DecodeString (token )
@@ -221,7 +222,11 @@ func getAuthInfo(svc *ecr.ECR) (username, password, registry string, err error)
221222 }
222223
223224 registry = strings .TrimPrefix (* auth .ProxyEndpoint , "https://" )
224- creds := strings .Split (string (decoded ), ":" )
225+ creds := strings .SplitN (string (decoded ), ":" , 2 )
226+ if len (creds ) < 2 {
227+ err = fmt .Errorf ("invalid ECR authorization token format" )
228+ return
229+ }
225230 username = creds [0 ]
226231 password = creds [1 ]
227232 return
@@ -233,7 +238,6 @@ func parseBoolOrDefault(defaultValue bool, s string) (result bool) {
233238 if err != nil {
234239 result = defaultValue
235240 }
236-
237241 return
238242}
239243
@@ -247,55 +251,51 @@ func getenv(key ...string) (s string) {
247251 return
248252}
249253
250- func getECRClient (sess * session. Session , role string , externalId string , idToken string ) * ecr.ECR {
254+ func getECRClient (cfg aws. Config , role string , externalId string , idToken string ) * ecr.Client {
251255 if role == "" {
252- return ecr .New ( sess )
256+ return ecr .NewFromConfig ( cfg )
253257 }
254258
255- if idToken != "" {
256- tempFile , err := os .CreateTemp ("/tmp" , "idToken-*.jwt" )
257- if err != nil {
258- log .Fatalf ("Failed to create temporary file: %v" , err )
259- }
260- defer tempFile .Close ()
261-
262- if err := os .Chmod (tempFile .Name (), 0600 ); err != nil {
263- log .Fatalf ("Failed to set file permissions: %v" , err )
264- }
259+ stsSvc := sts .NewFromConfig (cfg )
265260
266- if _ , err := tempFile .WriteString (idToken ); err != nil {
267- log .Fatalf ("Failed to write ID token to temporary file: %v" , err )
268- }
261+ if idToken != "" {
262+ provider := stscreds .NewWebIdentityRoleProvider (stsSvc , role , identityToken (idToken ))
263+ cfg .Credentials = aws .NewCredentialsCache (provider )
264+ return ecr .NewFromConfig (cfg )
265+ }
269266
270- // Create credentials using the path to the ID token file
271- creds := stscreds .NewWebIdentityCredentials (sess , role , "" , tempFile .Name ())
272- return ecr .New (sess , & aws.Config {Credentials : creds })
273- } else if externalId != "" {
274- return ecr .New (sess , & aws.Config {
275- Credentials : stscreds .NewCredentials (sess , role , func (p * stscreds.AssumeRoleProvider ) {
276- p .ExternalID = & externalId
277- }),
267+ var provider * stscreds.AssumeRoleProvider
268+ if externalId != "" {
269+ provider = stscreds .NewAssumeRoleProvider (stsSvc , role , func (o * stscreds.AssumeRoleOptions ) {
270+ o .ExternalID = & externalId
278271 })
279272 } else {
280- return ecr .New (sess , & aws.Config {
281- Credentials : stscreds .NewCredentials (sess , role ),
282- })
273+ provider = stscreds .NewAssumeRoleProvider (stsSvc , role )
283274 }
275+ cfg .Credentials = aws .NewCredentialsCache (provider )
276+ return ecr .NewFromConfig (cfg )
284277}
285278
286- func tagExists (svc ecrAPI , repository , tag string ) (bool , error ) {
279+ func tagExists (ctx context. Context , svc * ecr. Client , repository , tag string ) (bool , error ) {
287280 input := & ecr.DescribeImagesInput {
288281 RepositoryName : aws .String (repository ),
289- ImageIds : []* ecr .ImageIdentifier {
282+ ImageIds : []ecrtypes .ImageIdentifier {
290283 {ImageTag : aws .String (tag )},
291284 },
292285 }
293- output , err := svc .DescribeImages (input )
286+ output , err := svc .DescribeImages (ctx , input )
294287 if err != nil {
295- if aerr , ok := err .(awserr.Error ); ok && aerr .Code () == "ImageNotFoundException" {
288+ var inf * ecrtypes.ImageNotFoundException
289+ if errors .As (err , & inf ) {
296290 return false , nil
297291 }
298292 return false , err
299293 }
300294 return len (output .ImageDetails ) > 0 , nil
301295}
296+
297+ type identityToken string
298+
299+ func (t identityToken ) GetIdentityToken () ([]byte , error ) {
300+ return []byte (t ), nil
301+ }
0 commit comments