Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ko.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
defaultBaseImage: registry.k8s.io/build-image/go-runner:v2.3.1-go1.22.7-bookworm.0
defaultBaseImage: registry.k8s.io/build-image/go-runner:v2.4.0-go1.24.9-bookworm.0
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
## BUILD ARGS ##
################################################################################
# This build arg allows the specification of a custom Golang image.
ARG GOLANG_IMAGE=golang:1.22.7
ARG GOLANG_IMAGE=golang:1.24.9

# The distroless image on which the CPI manager image is built.
#
# Please do not use "latest". Explicit tags should be used to provide
# deterministic builds. Follow what kubernetes uses to build
# kube-controller-manager, for example for 1.23.x:
# https://github.com/kubernetes/kubernetes/blob/release-1.24/build/common.sh#L94
ARG DISTROLESS_IMAGE=registry.k8s.io/build-image/go-runner:v2.3.1-go1.22.7-bookworm.0
ARG DISTROLESS_IMAGE=registry.k8s.io/build-image/go-runner:v2.4.0-go1.24.9-bookworm.0

################################################################################
## BUILD STAGE ##
Expand Down
6 changes: 3 additions & 3 deletions THIRD-PARTY-LICENSES
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ Copyright © 2015 Steve Francia <[email protected]>

-----

** aws/aws-sdk-go; version 1.15.7 -- https://github.com/aws/aws-sdk-go/
** aws/aws-sdk-go-v2; version 1.24.4 -- https://github.com/aws/aws-sdk-go-v2/
** Etcd; version v3.1.0-alpha.1 -- https://github.com/coreos/etcd/tree/v3.1.0-alpha.1
** github.com/coreos/go-semver; version 0.2 -- https://github.com/coreos/go-semver
** github.com/coreos/go-systemd/; version 10 -- https://github.com/coreos/go-systemd/
Expand Down Expand Up @@ -412,9 +412,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

* For aws/aws-sdk-go see also this required NOTICE:
* For aws/aws-sdk-go-v2 see also this required NOTICE:
AWS SDK for Go
Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copyright 2014-2015 Stripe, Inc.
* For Etcd see also this required NOTICE:
CoreOS Project
Expand Down
2 changes: 1 addition & 1 deletion cloudbuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ steps:
- --platform=linux/amd64,linux/arm64
- .
# Build cloudbuild artifacts (for attestation)
- name: 'docker.io/library/golang:1.22.7-bookworm'
- name: 'docker.io/library/golang:1.24.9-bookworm'
id: cloudbuild-artifacts
entrypoint: make
env:
Expand Down
64 changes: 30 additions & 34 deletions cmd/ecr-credential-provider/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ import (
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/aws/aws-sdk-go/service/ecrpublic"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ecr"
"github.com/aws/aws-sdk-go-v2/service/ecrpublic"
"github.com/spf13/cobra"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -46,62 +46,60 @@ var ecrPrivateHostPattern = regexp.MustCompile(`^(\d{12})\.dkr[\.\-]ecr(\-fips)?

// ECR abstracts the calls we make to aws-sdk for testing purposes
type ECR interface {
GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error)
GetAuthorizationToken(ctx context.Context, input *ecr.GetAuthorizationTokenInput, optFns ...func(*ecr.Options)) (*ecr.GetAuthorizationTokenOutput, error)
}

// ECRPublic abstracts the calls we make to aws-sdk for testing purposes
type ECRPublic interface {
GetAuthorizationToken(input *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error)
GetAuthorizationToken(ctx context.Context, input *ecrpublic.GetAuthorizationTokenInput, optFns ...func(*ecrpublic.Options)) (*ecrpublic.GetAuthorizationTokenOutput, error)
}

type ecrPlugin struct {
ecr ECR
ecrPublic ECRPublic
}

func defaultECRProvider(region string) (*ecr.ECR, error) {
sess, err := session.NewSessionWithOptions(session.Options{
Config: aws.Config{Region: aws.String(region)},
SharedConfigState: session.SharedConfigEnable,
})
func defaultECRProvider(ctx context.Context, region string) (*ecr.Client, error) {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return nil, err
}
if region != "" {
klog.Warningf("No region found in the image reference, the default region will be used. Please refer to AWS SDK documentation for configuration purpose.")
cfg.Region = region
}

return ecr.New(sess), nil
return ecr.NewFromConfig(cfg), nil
}

func publicECRProvider() (*ecrpublic.ECRPublic, error) {
func publicECRProvider(ctx context.Context) (*ecrpublic.Client, error) {
// ECR public registries are only in one region and only accessible from regions
// in the "aws" partition.
sess, err := session.NewSessionWithOptions(session.Options{
Config: aws.Config{Region: aws.String(ecrPublicRegion)},
SharedConfigState: session.SharedConfigEnable,
})
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(ecrPublicRegion))
if err != nil {
return nil, err
}

return ecrpublic.New(sess), nil
return ecrpublic.NewFromConfig(cfg), nil
}

type credsData struct {
authToken *string
expiresAt *time.Time
}

func (e *ecrPlugin) getPublicCredsData() (*credsData, error) {
func (e *ecrPlugin) getPublicCredsData(ctx context.Context) (*credsData, error) {
klog.Infof("Getting creds for public registry")
var err error

if e.ecrPublic == nil {
e.ecrPublic, err = publicECRProvider()
e.ecrPublic, err = publicECRProvider(ctx)
}
if err != nil {
return nil, err
}

output, err := e.ecrPublic.GetAuthorizationToken(&ecrpublic.GetAuthorizationTokenInput{})
output, err := e.ecrPublic.GetAuthorizationToken(ctx, &ecrpublic.GetAuthorizationTokenInput{})
if err != nil {
return nil, err
}
Expand All @@ -120,19 +118,17 @@ func (e *ecrPlugin) getPublicCredsData() (*credsData, error) {
}, nil
}

func (e *ecrPlugin) getPrivateCredsData(imageHost string, image string) (*credsData, error) {
func (e *ecrPlugin) getPrivateCredsData(ctx context.Context, imageHost string, image string) (*credsData, error) {
klog.Infof("Getting creds for private image %s", image)
region, err := parseRegionFromECRPrivateHost(imageHost)
if err != nil {
return nil, err
}
var err error
if e.ecr == nil {
e.ecr, err = defaultECRProvider(region)
region := parseRegionFromECRPrivateHost(imageHost)
e.ecr, err = defaultECRProvider(ctx, region)
if err != nil {
return nil, err
}
}
output, err := e.ecr.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{})
output, err := e.ecr.GetAuthorizationToken(ctx, &ecr.GetAuthorizationTokenInput{})
if err != nil {
return nil, err
}
Expand All @@ -158,9 +154,9 @@ func (e *ecrPlugin) GetCredentials(ctx context.Context, image string, args []str
}

if imageHost == ecrPublicHost {
creds, err = e.getPublicCredsData()
creds, err = e.getPublicCredsData(ctx)
} else {
creds, err = e.getPrivateCredsData(imageHost, image)
creds, err = e.getPrivateCredsData(ctx, imageHost, image)
}

if err != nil {
Expand All @@ -171,7 +167,7 @@ func (e *ecrPlugin) GetCredentials(ctx context.Context, image string, args []str
return nil, errors.New("authorization token in response was nil")
}

decodedToken, err := base64.StdEncoding.DecodeString(aws.StringValue(creds.authToken))
decodedToken, err := base64.StdEncoding.DecodeString(aws.ToString(creds.authToken))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -227,12 +223,12 @@ func parseHostFromImageReference(image string) (string, error) {
return parsed.Hostname(), nil
}

func parseRegionFromECRPrivateHost(host string) (string, error) {
func parseRegionFromECRPrivateHost(host string) string {
splitHost := ecrPrivateHostPattern.FindStringSubmatch(host)
if len(splitHost) != 5 {
return "", fmt.Errorf("invalid private ECR host: %s", host)
return ""
}
return splitHost[3], nil
return splitHost[3]
}

func main() {
Expand Down
32 changes: 15 additions & 17 deletions cmd/ecr-credential-provider/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ import (
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/aws/aws-sdk-go/service/ecrpublic"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecr"
ecrtypes "github.com/aws/aws-sdk-go-v2/service/ecr/types"
"github.com/aws/aws-sdk-go-v2/service/ecrpublic"
ecrpublictypes "github.com/aws/aws-sdk-go-v2/service/ecrpublic/types"
"github.com/golang/mock/gomock"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cloud-provider-aws/pkg/providers/v2/mocks"
Expand All @@ -35,13 +37,13 @@ import (

func generatePrivateGetAuthorizationTokenOutput(user string, password string, proxy string, expiration *time.Time) *ecr.GetAuthorizationTokenOutput {
creds := []byte(fmt.Sprintf("%s:%s", user, password))
data := &ecr.AuthorizationData{
data := &ecrtypes.AuthorizationData{
AuthorizationToken: aws.String(base64.StdEncoding.EncodeToString(creds)),
ExpiresAt: expiration,
ProxyEndpoint: aws.String(proxy),
}
output := &ecr.GetAuthorizationTokenOutput{
AuthorizationData: []*ecr.AuthorizationData{data},
AuthorizationData: []ecrtypes.AuthorizationData{*data},
}
return output
}
Expand Down Expand Up @@ -103,7 +105,7 @@ func Test_GetCredentials_Private(t *testing.T) {
{
name: "empty authorization token",
image: "123456789123.dkr.ecr.us-west-2.amazonaws.com",
getAuthorizationTokenOutput: &ecr.GetAuthorizationTokenOutput{AuthorizationData: []*ecr.AuthorizationData{{}}},
getAuthorizationTokenOutput: &ecr.GetAuthorizationTokenOutput{AuthorizationData: []ecrtypes.AuthorizationData{{}}},
getAuthorizationTokenError: nil,
expectedError: errors.New("authorization token in response was nil"),
},
Expand All @@ -118,7 +120,7 @@ func Test_GetCredentials_Private(t *testing.T) {
name: "invalid authorization token",
image: "123456789123.dkr.ecr.us-west-2.amazonaws.com",
getAuthorizationTokenOutput: &ecr.GetAuthorizationTokenOutput{
AuthorizationData: []*ecr.AuthorizationData{
AuthorizationData: []ecrtypes.AuthorizationData{
{AuthorizationToken: aws.String(base64.StdEncoding.EncodeToString([]byte(fmt.Sprint("foo"))))},
},
},
Expand All @@ -130,7 +132,7 @@ func Test_GetCredentials_Private(t *testing.T) {
for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
p := &ecrPlugin{ecr: mockECR}
mockECR.EXPECT().GetAuthorizationToken(gomock.Any()).Return(testcase.getAuthorizationTokenOutput, testcase.getAuthorizationTokenError)
mockECR.EXPECT().GetAuthorizationToken(gomock.Any(), gomock.Any()).Return(testcase.getAuthorizationTokenOutput, testcase.getAuthorizationTokenError)

creds, err := p.GetCredentials(context.TODO(), testcase.image, testcase.args)

Expand All @@ -157,7 +159,7 @@ func Test_GetCredentials_Private(t *testing.T) {

func generatePublicGetAuthorizationTokenOutput(user string, password string, proxy string, expiration *time.Time) *ecrpublic.GetAuthorizationTokenOutput {
creds := []byte(fmt.Sprintf("%s:%s", user, password))
data := &ecrpublic.AuthorizationData{
data := &ecrpublictypes.AuthorizationData{
AuthorizationToken: aws.String(base64.StdEncoding.EncodeToString(creds)),
ExpiresAt: expiration,
}
Expand Down Expand Up @@ -205,7 +207,7 @@ func Test_GetCredentials_Public(t *testing.T) {
{
name: "empty authorization token",
image: "public.ecr.aws",
getAuthorizationTokenOutput: &ecrpublic.GetAuthorizationTokenOutput{AuthorizationData: &ecrpublic.AuthorizationData{}},
getAuthorizationTokenOutput: &ecrpublic.GetAuthorizationTokenOutput{AuthorizationData: &ecrpublictypes.AuthorizationData{}},
getAuthorizationTokenError: nil,
expectedError: errors.New("authorization token in response was nil"),
},
Expand All @@ -220,7 +222,7 @@ func Test_GetCredentials_Public(t *testing.T) {
name: "invalid authorization token",
image: "public.ecr.aws",
getAuthorizationTokenOutput: &ecrpublic.GetAuthorizationTokenOutput{
AuthorizationData: &ecrpublic.AuthorizationData{
AuthorizationData: &ecrpublictypes.AuthorizationData{
AuthorizationToken: aws.String(base64.StdEncoding.EncodeToString([]byte(fmt.Sprint("foo")))),
},
},
Expand All @@ -232,7 +234,7 @@ func Test_GetCredentials_Public(t *testing.T) {
for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
p := &ecrPlugin{ecrPublic: mockECRPublic}
mockECRPublic.EXPECT().GetAuthorizationToken(gomock.Any()).Return(testcase.getAuthorizationTokenOutput, testcase.getAuthorizationTokenError)
mockECRPublic.EXPECT().GetAuthorizationToken(gomock.Any(), gomock.Any()).Return(testcase.getAuthorizationTokenOutput, testcase.getAuthorizationTokenError)

creds, err := p.GetCredentials(context.TODO(), testcase.image, testcase.args)

Expand Down Expand Up @@ -378,11 +380,7 @@ func Test_parseRegionFromECRPrivateHost(t *testing.T) {

for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
region, err := parseRegionFromECRPrivateHost(testcase.host)

if testcase.err != nil && (testcase.err.Error() != err.Error()) {
t.Fatalf("expected error %s, got %s", testcase.err, err)
}
region := parseRegionFromECRPrivateHost(testcase.host)

if region != testcase.region {
t.Fatalf("region mismatch. Expected %s, got %s", testcase.region, region)
Expand Down
Loading
Loading