@@ -30,36 +30,24 @@ import (
3030 "github.com/aws/aws-sdk-go/aws"
3131 "github.com/aws/aws-sdk-go/aws/session"
3232 "github.com/aws/aws-sdk-go/service/ecr"
33- "github.com/aws/aws-sdk-go/service/ecrpublic"
34- "github.com/spf13/cobra"
3533
3634 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
37- "k8s.io/component-base/logs"
3835 "k8s.io/klog/v2"
3936 v1 "k8s.io/kubelet/pkg/apis/credentialprovider/v1"
4037)
4138
42- const ecrPublicRegion string = "us-east-1"
43- const ecrPublicHost string = "public.ecr.aws"
44-
45- var ecrPrivateHostPattern = regexp .MustCompile (`^(\d{12})\.dkr[\.\-]ecr(\-fips)?\.([a-zA-Z0-9][a-zA-Z0-9-_]*)\.(amazonaws\.com(?:\.cn)?|on\.(?:aws|amazonwebservices\.com\.cn)|sc2s\.sgov\.gov|c2s\.ic\.gov|cloud\.adc-e\.uk|csp\.hci\.ic\.gov)$` )
39+ var ecrPattern = regexp .MustCompile (`^(\d{12})\.dkr\.ecr(\-fips)?\.([a-zA-Z0-9][a-zA-Z0-9-_]*)\.(amazonaws\.com(\.cn)?|sc2s\.sgov\.gov|c2s\.ic\.gov|cloud\.adc-e\.uk|csp\.hci\.ic\.gov)$` )
4640
4741// ECR abstracts the calls we make to aws-sdk for testing purposes
4842type ECR interface {
4943 GetAuthorizationToken (input * ecr.GetAuthorizationTokenInput ) (* ecr.GetAuthorizationTokenOutput , error )
5044}
5145
52- // ECRPublic abstracts the calls we make to aws-sdk for testing purposes
53- type ECRPublic interface {
54- GetAuthorizationToken (input * ecrpublic.GetAuthorizationTokenInput ) (* ecrpublic.GetAuthorizationTokenOutput , error )
55- }
56-
5746type ecrPlugin struct {
58- ecr ECR
59- ecrPublic ECRPublic
47+ ecr ECR
6048}
6149
62- func defaultECRProvider (region string ) (* ecr.ECR , error ) {
50+ func defaultECRProvider (region string , registryID string ) (* ecr.ECR , error ) {
6351 sess , err := session .NewSessionWithOptions (session.Options {
6452 Config : aws.Config {Region : aws .String (region )},
6553 SharedConfigState : session .SharedConfigEnable ,
@@ -71,107 +59,40 @@ func defaultECRProvider(region string) (*ecr.ECR, error) {
7159 return ecr .New (sess ), nil
7260}
7361
74- func publicECRProvider () (* ecrpublic.ECRPublic , error ) {
75- // ECR public registries are only in one region and only accessible from regions
76- // in the "aws" partition.
77- sess , err := session .NewSessionWithOptions (session.Options {
78- Config : aws.Config {Region : aws .String (ecrPublicRegion )},
79- SharedConfigState : session .SharedConfigEnable ,
80- })
81- if err != nil {
82- return nil , err
83- }
84-
85- return ecrpublic .New (sess ), nil
86- }
87-
88- type credsData struct {
89- authToken * string
90- expiresAt * time.Time
91- }
92-
93- func (e * ecrPlugin ) getPublicCredsData () (* credsData , error ) {
94- klog .Infof ("Getting creds for public registry" )
95- var err error
96-
97- if e .ecrPublic == nil {
98- e .ecrPublic , err = publicECRProvider ()
99- }
100- if err != nil {
101- return nil , err
102- }
103-
104- output , err := e .ecrPublic .GetAuthorizationToken (& ecrpublic.GetAuthorizationTokenInput {})
62+ func (e * ecrPlugin ) GetCredentials (ctx context.Context , image string , args []string ) (* v1.CredentialProviderResponse , error ) {
63+ registryID , region , registry , err := parseRepoURL (image )
10564 if err != nil {
10665 return nil , err
10766 }
10867
109- if output == nil {
110- return nil , errors .New ("response output from ECR was nil" )
111- }
112-
113- if output .AuthorizationData == nil {
114- return nil , errors .New ("authorization data was empty" )
115- }
116-
117- return & credsData {
118- authToken : output .AuthorizationData .AuthorizationToken ,
119- expiresAt : output .AuthorizationData .ExpiresAt ,
120- }, nil
121- }
122-
123- func (e * ecrPlugin ) getPrivateCredsData (imageHost string , image string ) (* credsData , error ) {
124- klog .Infof ("Getting creds for private image %s" , image )
125- region , err := parseRegionFromECRPrivateHost (imageHost )
126- if err != nil {
127- return nil , err
128- }
12968 if e .ecr == nil {
130- e .ecr , err = defaultECRProvider (region )
69+ e .ecr , err = defaultECRProvider (region , registryID )
13170 if err != nil {
13271 return nil , err
13372 }
13473 }
135- output , err := e .ecr .GetAuthorizationToken (& ecr.GetAuthorizationTokenInput {})
74+
75+ output , err := e .ecr .GetAuthorizationToken (& ecr.GetAuthorizationTokenInput {
76+ RegistryIds : []* string {aws .String (registryID )},
77+ })
13678 if err != nil {
13779 return nil , err
13880 }
81+
13982 if output == nil {
14083 return nil , errors .New ("response output from ECR was nil" )
14184 }
85+
14286 if len (output .AuthorizationData ) == 0 {
14387 return nil , errors .New ("authorization data was empty" )
14488 }
145- return & credsData {
146- authToken : output .AuthorizationData [0 ].AuthorizationToken ,
147- expiresAt : output .AuthorizationData [0 ].ExpiresAt ,
148- }, nil
149- }
150-
151- func (e * ecrPlugin ) GetCredentials (ctx context.Context , image string , args []string ) (* v1.CredentialProviderResponse , error ) {
152- var creds * credsData
153- var err error
154-
155- imageHost , err := parseHostFromImageReference (image )
156- if err != nil {
157- return nil , err
158- }
159-
160- if imageHost == ecrPublicHost {
161- creds , err = e .getPublicCredsData ()
162- } else {
163- creds , err = e .getPrivateCredsData (imageHost , image )
164- }
165-
166- if err != nil {
167- return nil , err
168- }
16989
170- if creds .authToken == nil {
90+ data := output .AuthorizationData [0 ]
91+ if data .AuthorizationToken == nil {
17192 return nil , errors .New ("authorization token in response was nil" )
17293 }
17394
174- decodedToken , err := base64 .StdEncoding .DecodeString (aws .StringValue (creds . authToken ))
95+ decodedToken , err := base64 .StdEncoding .DecodeString (aws .StringValue (data . AuthorizationToken ))
17596 if err != nil {
17697 return nil , err
17798 }
@@ -181,13 +102,13 @@ func (e *ecrPlugin) GetCredentials(ctx context.Context, image string, args []str
181102 return nil , errors .New ("error parsing username and password from authorization token" )
182103 }
183104
184- cacheDuration := getCacheDuration (creds . expiresAt )
105+ cacheDuration := getCacheDuration (data . ExpiresAt )
185106
186107 return & v1.CredentialProviderResponse {
187108 CacheKeyType : v1 .RegistryPluginCacheKeyType ,
188109 CacheDuration : cacheDuration ,
189110 Auth : map [string ]v1.AuthConfig {
190- imageHost : {
111+ registry : {
191112 Username : parts [0 ],
192113 Password : parts [1 ],
193114 },
@@ -214,50 +135,30 @@ func getCacheDuration(expiresAt *time.Time) *metav1.Duration {
214135 return cacheDuration
215136}
216137
217- // parseHostFromImageReference parses the hostname from an image reference
218- func parseHostFromImageReference (image string ) (string , error ) {
219- // a URL needs a scheme to be parsed correctly
220- if ! strings .Contains (image , "://" ) {
138+ // parseRepoURL parses and splits the registry URL
139+ // returns (registryID, region, registry).
140+ // <registryID>.dkr.ecr(-fips).<region>.amazonaws.com(.cn)
141+ func parseRepoURL (image string ) (string , string , string , error ) {
142+ if ! strings .Contains (image , "https://" ) {
221143 image = "https://" + image
222144 }
223145 parsed , err := url .Parse (image )
224146 if err != nil {
225- return "" , fmt .Errorf ("error parsing image reference %s: %v" , image , err )
147+ return "" , "" , "" , fmt .Errorf ("error parsing image %s: %v" , image , err )
226148 }
227- return parsed .Hostname (), nil
228- }
229149
230- func parseRegionFromECRPrivateHost (host string ) (string , error ) {
231- splitHost := ecrPrivateHostPattern .FindStringSubmatch (host )
232- if len (splitHost ) != 5 {
233- return "" , fmt .Errorf ("invalid private ECR host: %s" , host )
150+ splitURL := ecrPattern .FindStringSubmatch (parsed .Hostname ())
151+ if len (splitURL ) < 4 {
152+ return "" , "" , "" , fmt .Errorf ("%s is not a valid ECR repository URL" , parsed .Hostname ())
234153 }
235- return splitHost [3 ], nil
154+
155+ return splitURL [1 ], splitURL [3 ], parsed .Hostname (), nil
236156}
237157
238158func main () {
239- logs .InitLogs ()
240- defer logs .FlushLogs ()
241-
242- if err := newCredentialProviderCommand ().Execute (); err != nil {
159+ p := NewCredentialProvider (& ecrPlugin {})
160+ if err := p .Run (context .TODO ()); err != nil {
161+ klog .Errorf ("Error running credential provider plugin: %v" , err )
243162 os .Exit (1 )
244163 }
245164}
246-
247- var gitVersion string
248-
249- func newCredentialProviderCommand () * cobra.Command {
250- cmd := & cobra.Command {
251- Use : "ecr-credential-provider" ,
252- Short : "ECR credential provider for kubelet" ,
253- Version : gitVersion ,
254- Run : func (cmd * cobra.Command , args []string ) {
255- p := NewCredentialProvider (& ecrPlugin {})
256- if err := p .Run (context .TODO ()); err != nil {
257- klog .Errorf ("Error running credential provider plugin: %v" , err )
258- os .Exit (1 )
259- }
260- },
261- }
262- return cmd
263- }
0 commit comments