Migrate to AWS SDK v2

Create issue
Issue #27 new
Daniel Svensson created an issue

While v1 is working fine, other gradle plugins (S3 build cache for example) have moved to AWS SDK v2, and it would be nice to reduce the number of AWS SDK versions required to be downloaded by the build system.

https://sdk.amazonaws.com/java/api/latest/index.html?software/amazon/awssdk/services/ecr/EcrClient.html

com.amazonaws:aws-java-sdk-ecr:xxx → software.amazon.awssdk:ecr:xxx

Comments (3)

  1. Daniel Svensson reporter

    Something like:

    diff --git a/build.gradle b/build.gradle
    index 763f162..6fe0ea4 100644
    --- a/build.gradle
    +++ b/build.gradle
    @@ -52,7 +52,7 @@ dependencies {
         implementation gradleApi()
         implementation localGroovy()
         implementation 'com.bmuschko:gradle-docker-plugin:6.4.0'
    -    implementation 'com.amazonaws:aws-java-sdk-ecr:1.11.759'
    +    implementation 'software.amazon.awssdk:ecr:2.16.60'
         testImplementation('org.spockframework:spock-core:1.1-groovy-2.4') {
             exclude module: 'groovy-all'
         }
    diff --git a/src/main/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentials.groovy b/src/main/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentials.groovy
    index 3ac165d..b8c89ac 100644
    --- a/src/main/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentials.groovy
    +++ b/src/main/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentials.groovy
    @@ -1,10 +1,5 @@
     package com.patdouble.gradle.awsecr
    
    -import com.amazonaws.auth.AWSStaticCredentialsProvider
    -import com.amazonaws.auth.BasicAWSCredentials
    -import com.amazonaws.services.ecr.AmazonECRClientBuilder
    -import com.amazonaws.services.ecr.model.GetAuthorizationTokenRequest
    -import com.amazonaws.services.ecr.model.GetAuthorizationTokenResult
     import com.bmuschko.gradle.docker.DockerRegistryCredentials
     import com.bmuschko.gradle.docker.tasks.RegistryCredentialsAware
     import groovy.transform.Canonical
    @@ -16,6 +11,12 @@ import org.gradle.api.tasks.Input
     import org.gradle.api.tasks.Internal
     import org.gradle.api.tasks.Optional
     import org.gradle.api.tasks.TaskAction
    +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
    +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
    +import software.amazon.awssdk.regions.Region
    +import software.amazon.awssdk.services.ecr.EcrClient
    +import software.amazon.awssdk.services.ecr.EcrClientBuilder
    +import software.amazon.awssdk.services.ecr.model.GetAuthorizationTokenResponse
    
     import java.security.MessageDigest
    
    @@ -132,27 +133,30 @@ class PopulateECRCredentials extends DefaultTask implements RegistryCredentialsA
             //Get the region from the registry URL
             String region = registryUrl.replaceAll(/^.*ecr\.(.*)\.amazonaws.*$/, '$1')
    
    -        AmazonECRClientBuilder ecrClientBuilder = AmazonECRClientBuilder.standard().withRegion(region)
    +        EcrClientBuilder ecrClientBuilder = EcrClient.builder().region(Region.of(region))
    
             if (awsAccessKeyId && awsSecretAccessKey) {
    -            ecrClientBuilder.credentials = new AWSStaticCredentialsProvider(
    -                    new BasicAWSCredentials(awsAccessKeyId, awsSecretAccessKey))
    +            ecrClientBuilder.credentials = StaticCredentialsProvider.create(
    +                    AwsBasicCredentials.create(awsAccessKeyId, awsSecretAccessKey)
    +            )
             }
    
    -        GetAuthorizationTokenResult tokens = ecrClientBuilder.build()
    -                .getAuthorizationToken(new GetAuthorizationTokenRequest().withRegistryIds(registryId))
    +        GetAuthorizationTokenResponse tokens = ecrClientBuilder.build()
    +                .getAuthorizationToken({ request ->
    +                    request.registryIds(registryId)
    +                })
    
    -        if (!tokens.authorizationData) {
    -            throw new GradleException("Could not get ECR token: ${tokens.sdkHttpMetadata.httpStatusCode}")
    +        if (!tokens.authorizationData()) {
    +            throw new GradleException("Could not get ECR token: ${tokens.sdkHttpResponse().statusCode()}")
             }
    
    -        String[] ecrCreds = new String(tokens.authorizationData.first().authorizationToken.decodeBase64(),
    +        String[] ecrCreds = new String(tokens.authorizationData().first().authorizationToken().decodeBase64(),
                 'US-ASCII').split(COLON_SEPARATOR)
    
             new CachedCredentials(
                 username: ecrCreds[0],
                 password: ecrCreds[1],
    -            expiresAt: tokens.authorizationData.first().expiresAt.time,
    +            expiresAt: tokens.authorizationData().first().expiresAt().epochSecond,
             )
         }
    

    But as the EcrClient is no longer a concrete class, but rather an interface, I didn’t get the tests working. Tried using DefaultEcrClient, which is concrete, but it needs an argument for creation. Maybe trivial to… dunno.. I’m not native to Groovy.

    diff --git a/src/test/groovy/com/patdouble/gradle/awsecr/CredentialsFileSpec.groovy b/src/test/groovy/com/patdouble/gradle/awsecr/CredentialsFileSpec.groovy
    index 1f30886..ff9d30b 100644
    --- a/src/test/groovy/com/patdouble/gradle/awsecr/CredentialsFileSpec.groovy
    +++ b/src/test/groovy/com/patdouble/gradle/awsecr/CredentialsFileSpec.groovy
    @@ -1,18 +1,20 @@
     package com.patdouble.gradle.awsecr
    
    -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain
    -import com.amazonaws.services.ecr.AmazonECRClient
    -import com.amazonaws.services.ecr.model.AuthorizationData
    -import com.amazonaws.services.ecr.model.GetAuthorizationTokenRequest
    -import com.amazonaws.services.ecr.model.GetAuthorizationTokenResult
     import org.gradle.testfixtures.ProjectBuilder
     import org.junit.Rule
     import org.junit.rules.TemporaryFolder
    +import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain
    +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider
    +import software.amazon.awssdk.services.ecr.DefaultEcrClient
    +import software.amazon.awssdk.services.ecr.EcrClient
    +import software.amazon.awssdk.services.ecr.model.GetAuthorizationTokenRequest
    +import software.amazon.awssdk.services.ecr.model.GetAuthorizationTokenResponse
     import spock.lang.Specification
     import spock.lang.Unroll
    
     import java.lang.reflect.Field
     import java.lang.reflect.Modifier
    +import java.time.Instant
    
     /**
      * Tests usage of the AWS credentials file that is handled automatically by the default AWS client.
    @@ -50,14 +52,15 @@ class CredentialsFileSpec extends Specification {
             task.credentialFile.delete()
    
             // Set the DefaultAWSCredentialsProviderChain.INSTANCE field to get the new profile
    -        def providerChainInstance = DefaultAWSCredentialsProviderChain.getDeclaredField('INSTANCE')
    +        def providerChainInstance = DefaultCredentialsProvider.getDeclaredField('DEFAULT_CREDENTIALS_PROVIDER')
             providerChainInstance.accessible = true
             Field modifiersField = Field.class.getDeclaredField("modifiers")
             modifiersField.accessible = true
             modifiersField.setInt(providerChainInstance, providerChainInstance.getModifiers() & ~Modifier.FINAL)
    -        providerChainInstance.set(null, new DefaultAWSCredentialsProviderChain())
    +        providerChainInstance.set(null, AwsCredentialsProviderChain.builder().build())
    
    -        def amazonECRClient = GroovySpy(AmazonECRClient, global: true)
    +        // TODO: Doesn't work as it needs ClientConfiguration
    +        def amazonECRClient = GroovySpy(DefaultEcrClient, global: true)
    
             when:
             task.populateCredentials()
    @@ -65,12 +68,16 @@ class CredentialsFileSpec extends Specification {
             then:
             1 * amazonECRClient.getAuthorizationToken(_) >> { GetAuthorizationTokenRequest request ->
                 // we're changing the getAuthorizationToken(...) method to return the AWS credentials so we can verify it later
    -            AmazonECRClient thisClient = delegate.mockObject.instance
    -            Field awsCredentialsProviderField = AmazonECRClient.getDeclaredField('awsCredentialsProvider')
    +            EcrClient thisClient = delegate.mockObject.instance
    +            // TODO: This has to be something else as that method doesn't exist there.
    +            Field awsCredentialsProviderField = EcrClient.getDeclaredField('awsCredentialsProvider')
                 awsCredentialsProviderField.accessible = true
                 def awsCredentialsProvider = awsCredentialsProviderField.get(thisClient)
                 def credentials = awsCredentialsProvider.getCredentials()
    -            new GetAuthorizationTokenResult().withAuthorizationData(new AuthorizationData(authorizationToken: "${credentials.AWSAccessKeyId}:${credentials.AWSSecretKey}".bytes.encodeBase64().toString(), expiresAt: new Date() + 7))
    +            GetAuthorizationTokenResponse.builder().authorizationData({ data ->
    +                data.authorizationToken("${credentials.AWSAccessKeyId}:${credentials.AWSSecretKey}".bytes.encodeBase64().toString())
    +                data.expiresAt(Instant.now() + 7)
    +            })
             }
    
             and:
    diff --git a/src/test/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentialsNoECRSpec.groovy b/src/test/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentialsNoECRSpec.groovy
    index 803a351..a557f9f 100644
    --- a/src/test/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentialsNoECRSpec.groovy
    +++ b/src/test/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentialsNoECRSpec.groovy
    @@ -1,9 +1,10 @@
     package com.patdouble.gradle.awsecr
    
    -import com.amazonaws.services.ecr.AmazonECRClient
     import org.gradle.testfixtures.ProjectBuilder
     import org.junit.Rule
     import org.junit.rules.TemporaryFolder
    +import software.amazon.awssdk.services.ecr.DefaultEcrClient
    +import software.amazon.awssdk.services.ecr.EcrClient
     import spock.lang.Specification
    
     /**
    @@ -11,12 +12,13 @@ import spock.lang.Specification
      */
     class PopulateECRCredentialsNoECRSpec extends Specification {
         PopulateECRCredentials task
    -    AmazonECRClient amazonECRClient
    +    EcrClient amazonECRClient
    
         @Rule TemporaryFolder tmp
    
         def setup() {
    -        amazonECRClient = GroovySpy(AmazonECRClient, global: true)
    +        // TODO: Doesn't work as it needs ClientConfiguration
    +        amazonECRClient = GroovySpy(DefaultEcrClient, global: true)
    
             def project = ProjectBuilder.builder().withProjectDir(tmp.newFolder('project')).build()
             project.with {
    diff --git a/src/test/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentialsSpec.groovy b/src/test/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentialsSpec.groovy
    index f1c20e2..2158fab 100644
    --- a/src/test/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentialsSpec.groovy
    +++ b/src/test/groovy/com/patdouble/gradle/awsecr/PopulateECRCredentialsSpec.groovy
    @@ -1,27 +1,31 @@
     package com.patdouble.gradle.awsecr
    
    -import com.amazonaws.auth.AWSStaticCredentialsProvider
    -import com.amazonaws.auth.BasicAWSCredentials
    -import com.amazonaws.services.ecr.AmazonECRClient
    -import com.amazonaws.services.ecr.model.AuthorizationData
    -import com.amazonaws.services.ecr.model.GetAuthorizationTokenRequest
    -import com.amazonaws.services.ecr.model.GetAuthorizationTokenResult
     import org.gradle.testfixtures.ProjectBuilder
     import org.junit.Rule
     import org.junit.rules.TemporaryFolder
    +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
    +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
    +import software.amazon.awssdk.services.ecr.DefaultEcrClient
    +import software.amazon.awssdk.services.ecr.EcrClient
    +import software.amazon.awssdk.services.ecr.model.AuthorizationData
    +import software.amazon.awssdk.services.ecr.model.GetAuthorizationTokenRequest
    +import software.amazon.awssdk.services.ecr.model.GetAuthorizationTokenResponse
     import spock.lang.Specification
     import com.patdouble.gradle.awsecr.PopulateECRCredentials.CachedCredentials
    
    +import java.time.Instant
     import java.util.concurrent.TimeUnit
    
     class PopulateECRCredentialsSpec extends Specification {
         PopulateECRCredentials task
    -    AmazonECRClient amazonECRClient
    +    EcrClient amazonECRClient
    
         @Rule TemporaryFolder tmp
    
    +    @SuppressWarnings('GroovyAccessibility')
         def setup() {
    -        amazonECRClient = GroovySpy(AmazonECRClient, global: true)
    +        // TODO: Doesn't work as it needs ClientConfiguration
    +        amazonECRClient = GroovySpy(DefaultEcrClient, global: true)
    
             def project = ProjectBuilder.builder().withProjectDir(tmp.newFolder('project')).build()
             project.with {
    @@ -98,11 +102,16 @@ class PopulateECRCredentialsSpec extends Specification {
    
         void "should request credentials from AWS"() {
             given:
    -        def tokens = new GetAuthorizationTokenResult().withAuthorizationData(new AuthorizationData(authorizationToken: 'user1:pw'.bytes.encodeBase64().toString(), expiresAt: new Date() + 7))
    +        def tokens = GetAuthorizationTokenResponse.builder().authorizationData({ data ->
    +            data.authorizationToken('user1:pw'.bytes.encodeBase64().toString())
    +            data.expiresAt(Instant.now() + 7)
    +        }).build()
             when:
             task.populateCredentials()
             then:
    -        1 * amazonECRClient.getAuthorizationToken(new GetAuthorizationTokenRequest().withRegistryIds('123456789')) >> tokens
    +        1 * amazonECRClient.getAuthorizationToken({ request ->
    +            request.registryIds('123456789')
    +        }) >> tokens
             and:
             task.registryCredentials.username.get() == 'user1'
             task.registryCredentials.password.get() == 'pw'
    @@ -110,16 +119,23 @@ class PopulateECRCredentialsSpec extends Specification {
    
         void "should request credentials with AWS keys"() {
             given:
    -        def tokens = new GetAuthorizationTokenResult().withAuthorizationData(new AuthorizationData(authorizationToken: 'user1:pw'.bytes.encodeBase64().toString(), expiresAt: new Date() + 7))
    -        def request = new GetAuthorizationTokenRequest().withRegistryIds('123456789')
    +        def tokens = GetAuthorizationTokenResponse.builder().authorizationData({ data ->
    +            data.authorizationToken('user1:pw'.bytes.encodeBase64().toString())
    +            data.expiresAt(Instant.now() + 7)
    +        })
             task.awsAccessKeyId = 'aws_access_key'
             task.awsSecretAccessKey = 'aws_secret_key'
    -        request.setRequestCredentialsProvider(new AWSStaticCredentialsProvider(
    -                new BasicAWSCredentials('aws_access_key', 'aws_secret_key')))
             when:
             task.populateCredentials()
             then:
    -        1 * amazonECRClient.getAuthorizationToken(request) >> tokens
    +        1 * amazonECRClient.getAuthorizationToken({ request ->
    +            request.registryIds('123456789')
    +            request.overrideConfiguration( { config ->
    +                config.credentialsProvider(StaticCredentialsProvider.create(
    +                        AwsBasicCredentials.create('aws_access_key', 'aws_secret_key')
    +                ))
    +            })
    +        }) >> tokens
             and:
             task.registryCredentials.username.get() == 'user1'
             task.registryCredentials.password.get() == 'pw'
    @@ -140,7 +156,10 @@ class PopulateECRCredentialsSpec extends Specification {
    
         void "should configure refreshed credentials"() {
             given:
    -        def tokens = new GetAuthorizationTokenResult().withAuthorizationData(new AuthorizationData(authorizationToken: 'user1:pw'.bytes.encodeBase64().toString(), expiresAt: new Date() + 7))
    +        def tokens = GetAuthorizationTokenResponse.builder().authorizationData({ data ->
    +            data.authorizationToken('user1:pw'.bytes.encodeBase64().toString())
    +            data.expiresAt(Instant.now() + 7)
    +        }).build()
             def creds = new CachedCredentials(username: 'cacheduser', password: 'cachedpw', expiresAt: (new Date()-7).time)
             task.setCachedCredentials(task.getCredentialFile(), creds)
             when:
    

  2. Log in to comment