Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
Signed-off-by: alexey.komyakov <[email protected]>
  • Loading branch information
scaps1 committed Oct 30, 2024
1 parent a9a1fc4 commit 7c2db4b
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 74 deletions.
2 changes: 0 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ func main() {
namespaceLabels := flag.String("namespace-label", "", "namespace label for checks")
insecureSkipVerify := flag.Bool("skip-registry-cert-verification", false, "whether to skip registries' certificate verification")
plainHTTP := flag.Bool("allow-plain-http", false, "whether to fallback to HTTP scheme for registries that don't support HTTPS") // named after the ctr cli flag
ecrImagesExists := flag.Bool("ecr-images-exists", false, "whether images from ECR in your cluster")
defaultRegistry := flag.String("default-registry", "", fmt.Sprintf("default registry to use in absence of a fully qualified image name, defaults to %q", name.DefaultRegistry))
flag.Var(&cp, "capath", "path to a file that contains CA certificates in the PEM format") // named after the curl cli flag
flag.Var(&mirrors, "image-mirror", "Add a mirror repository (format: original=mirror)")
Expand Down Expand Up @@ -87,7 +86,6 @@ func main() {
stopCh.Done(),
kubeClient,
*insecureSkipVerify,
*ecrImagesExists,
*plainHTTP,
cp,
forceCheckDisabledControllerKindsParser.ParsedKinds,
Expand Down
89 changes: 89 additions & 0 deletions pkg/providers/amazon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package providers

import (
"context"
"encoding/base64"
"fmt"
"strings"

"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ecr"
"github.com/google/go-containerregistry/pkg/authn"
)

type ECRDetails struct {
Region string
Domain string
}

type customKeychain struct {
authenticator authn.Authenticator
}

func (kc *customKeychain) Resolve(_ authn.Resource) (authn.Authenticator, error) {
return kc.authenticator, nil
}

func GetECRAuthKeychain(ctx context.Context, registryStr string) (authn.Keychain, error) {
ecrDetails, err := parseECRDetails(registryStr)
if err != nil {
return nil, err
}

ecrClient, err := awsRegionalClient(ctx, ecrDetails.Region)
if err != nil {
return nil, fmt.Errorf("error loading AWS config: %w", err)
}

authTokenOutput, err := ecrClient.GetAuthorizationToken(ctx, &ecr.GetAuthorizationTokenInput{})
if err != nil {
return nil, fmt.Errorf("error getting ECR authorization token: %w", err)
}

if len(authTokenOutput.AuthorizationData) == 0 {
return nil, fmt.Errorf("no authorization data received from ECR")
}

authData := authTokenOutput.AuthorizationData[0]
decodedToken, err := base64.StdEncoding.DecodeString(*authData.AuthorizationToken)
if err != nil {
return nil, fmt.Errorf("error decoding authorization token: %w", err)
}

credentials := strings.SplitN(string(decodedToken), ":", 2)
if len(credentials) != 2 {
return nil, fmt.Errorf("invalid authorization token format")
}
authConfig := authn.AuthConfig{
Username: credentials[0],
Password: credentials[1],
}
auth := authn.FromConfig(authConfig)
return &customKeychain{authenticator: auth}, nil
}

func IsEcrURL(url string) bool {
parts := strings.SplitN(url, ".", 5)
if len(parts) <= 3 || !strings.Contains(url, "amazonaws.com") {
return false
}
return true
}

func parseECRDetails(registryStr string) (ECRDetails, error) {
parts := strings.SplitN(registryStr, ".", 5)
return ECRDetails{
Region: parts[3],
Domain: registryStr,
}, nil
}

func awsRegionalClient(ctx context.Context, region string) (*ecr.Client, error) {
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(region))
if err != nil {
return nil, err
}

client := ecr.NewFromConfig(cfg)
return client, nil
}
79 changes: 7 additions & 72 deletions pkg/registry/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strings"
"time"

"github.com/flant/k8s-image-availability-exporter/pkg/providers"
"github.com/flant/k8s-image-availability-exporter/pkg/version"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
Expand All @@ -20,10 +21,6 @@ import (

"github.com/prometheus/client_golang/prometheus"

"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/ecr/types"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/sirupsen/logrus"
Expand All @@ -50,13 +47,6 @@ type registryCheckerConfig struct {
defaultRegistry string
plainHTTP bool
mirrorsMap map[string]string
ecrImagesExists bool
}

type ECRDetails struct {
AccountID string
Region string
Domain string
}

type Checker struct {
Expand Down Expand Up @@ -86,7 +76,6 @@ func NewChecker(
kubeClient *kubernetes.Clientset,
skipVerify bool,
plainHTTP bool,
ecrImagesExists bool,
caPths []string,
forceCheckDisabledControllerKinds []string,
ignoredImages []regexp.Regexp,
Expand Down Expand Up @@ -137,7 +126,6 @@ func NewChecker(
defaultRegistry: defaultRegistry,
plainHTTP: plainHTTP,
mirrorsMap: mirrorsMap,
ecrImagesExists: ecrImagesExists,
},
}

Expand Down Expand Up @@ -329,13 +317,13 @@ func (rc *Checker) checkImageAvailability(log *logrus.Entry, imageName string, k
return checkImageNameParseErr(log, err)
}

ecrDetails, err := parseECRDetails(ref.Context().RegistryStr())
if err == nil && rc.config.ecrImagesExists {
if rc.isImageInEcr(log, ref, ecrDetails) {
return store.Available
if providers.IsEcrURL(ref.Context().RegistryStr()) {
ecrAuth, err := providers.GetECRAuthKeychain(context.Background(), ref.Context().RegistryStr())
if err != nil {
log.WithError(err).Error("Error while getting token")
return store.UnknownError
}
} else if err != nil {
log.Error(err)
kc = ecrAuth
}

imgErr := wait.ExponentialBackoff(wait.Backoff{
Expand Down Expand Up @@ -392,59 +380,6 @@ func parseImageName(image string, defaultRegistry string, plainHTTP bool) (name.
return ref, nil
}

func parseECRDetails(registryStr string) (ECRDetails, error) {
parts := strings.SplitN(registryStr, ".", 5)
if len(parts) <= 3 || !strings.Contains(registryStr, "amazonaws.com") {
return ECRDetails{}, fmt.Errorf("%q doesn't match the ECR template", registryStr)
}

return ECRDetails{
AccountID: parts[0],
Region: parts[3],
Domain: registryStr,
}, nil
}

func (rc *Checker) isImageInEcr(log *logrus.Entry, ref name.Reference, details ECRDetails) bool {
ctx := context.TODO()
ecrClient, err := rc.awsRegionalClient(ctx, details.Region)
if err != nil {
log.Warningf("Failed to load aws configuration: %v", err)
return false
}

input := &ecr.BatchGetImageInput{
RegistryId: aws.String(details.AccountID),
RepositoryName: aws.String(ref.Context().RepositoryStr()),
ImageIds: []types.ImageIdentifier{
{ImageTag: aws.String(ref.Identifier())},
},
}

result, err := ecrClient.BatchGetImage(ctx, input)
if err != nil {
log.Warningf("Error retrieving image from ECR: %v", err)
return false
}

if len(result.Images) > 0 {
return true
}

log.Infof("Image %q not found in ECR.", ref.Context().Name())
return false
}

func (rc *Checker) awsRegionalClient(ctx context.Context, region string) (*ecr.Client, error) {
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(region))
if err != nil {
return nil, err
}

client := ecr.NewFromConfig(cfg)
return client, nil
}

func check(ref name.Reference, kc authn.Keychain, registryTransport http.RoundTripper) (store.AvailabilityMode, error) {
var imgErr error

Expand Down

0 comments on commit 7c2db4b

Please sign in to comment.