Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 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
15 changes: 15 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module github.com/flant/k8s-image-availability-exporter
go 1.22.0

require (
github.com/aws/aws-sdk-go-v2/config v1.28.0
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2
github.com/gammazero/deque v0.2.1
github.com/google/go-containerregistry v0.20.2
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20240129192428-8dadbe76ff8c
Expand All @@ -17,6 +19,18 @@ require (
)

require (
github.com/aws/aws-sdk-go-v2 v1.32.2 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.41 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 // indirect
github.com/aws/smithy-go v1.22.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
Expand All @@ -37,6 +51,7 @@ require (
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect
Expand Down
32 changes: 32 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
github.com/aws/aws-sdk-go-v2 v1.32.2 h1:AkNLZEyYMLnx/Q/mSKkcMqwNFXMAvFto9bNsHqcTduI=
github.com/aws/aws-sdk-go-v2 v1.32.2/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
github.com/aws/aws-sdk-go-v2/config v1.28.0 h1:FosVYWcqEtWNxHn8gB/Vs6jOlNwSoyOCA/g/sxyySOQ=
github.com/aws/aws-sdk-go-v2/config v1.28.0/go.mod h1:pYhbtvg1siOOg8h5an77rXle9tVG8T+BWLWAo7cOukc=
github.com/aws/aws-sdk-go-v2/credentials v1.17.41 h1:7gXo+Axmp+R4Z+AK8YFQO0ZV3L0gizGINCOWxSLY9W8=
github.com/aws/aws-sdk-go-v2/credentials v1.17.41/go.mod h1:u4Eb8d3394YLubphT4jLEwN1rLNq2wFOlT6OuxFwPzU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 h1:TMH3f/SCAWdNtXXVPPu5D6wrr4G5hI1rAxbcocKfC7Q=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17/go.mod h1:1ZRXLdTpzdJb9fwTMXiLipENRxkGMTn1sfKexGllQCw=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 h1:UAsR3xA31QGf79WzpG/ixT9FZvQlh5HY1NRqSHBNOCk=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21/go.mod h1:JNr43NFf5L9YaG3eKTm7HQzls9J+A9YYcGI5Quh1r2Y=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 h1:6jZVETqmYCadGFvrYEQfC5fAQmlo80CeL5psbno6r0s=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21/go.mod h1:1SR0GbLlnN3QUmYaflZNiH1ql+1qrSiB2vwcJ+4UM60=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2 h1:VDQaVwGOokbd3VUbHF+wupiffdrbAZPdQnr5XZMJqrs=
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2/go.mod h1:lvUlMghKYmSxSfv0vU7pdU/8jSY+s0zpG8xXhaGKCw0=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 h1:s7NA1SOw8q/5c0wr8477yOPp0z+uBaXBnLE0XYb0POA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2/go.mod h1:fnjjWyAW/Pj5HYOxl9LJqWtEwS7W2qgcRLWP+uWbss0=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 h1:bSYXVyUzoTHoKalBmwaZxs97HU9DWWI3ehHSAMa7xOk=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2/go.mod h1:skMqY7JElusiOUjMJMOv1jJsP7YUg7DrhgqZZWuzu1U=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 h1:AhmO1fHINP9vFYUE0LHzCWg/LfUWUF+zFPEcY9QXb7o=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2/go.mod h1:o8aQygT2+MVP0NaV6kbdE1YnnIM8RRVQzoeUH45GOdI=
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 h1:CiS7i0+FUe+/YY1GvIBLLrR/XNGZ4CtM1Ll0XavNuVo=
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2/go.mod h1:HtaiBI8CjYoNVde8arShXb94UbQQi9L4EMr6D+xGBwo=
github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
Expand Down Expand Up @@ -54,6 +82,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
Expand Down
77 changes: 77 additions & 0 deletions pkg/providers/amazon/amazon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package amazon

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 Provider struct{}

func NewProvider() *Provider {
return &Provider{}
}

func (p Provider) GetAuthKeychain(registryStr string) (authn.Keychain, error) {
ecrClient, err := awsRegionalClient(context.TODO(), parseECRDetails(registryStr))
if err != nil {
return nil, fmt.Errorf("error loading AWS config: %w", err)
}

authTokenOutput, err := ecrClient.GetAuthorizationToken(context.TODO(), &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 parseECRDetails(registryStr string) string {
parts := strings.SplitN(registryStr, ".", 5)
if len(parts) < 3 {
return ""
}
return parts[3]
}

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
}

type customKeychain struct {
authenticator authn.Authenticator
}

func (kc *customKeychain) Resolve(_ authn.Resource) (authn.Authenticator, error) {
return kc.authenticator, nil
}
29 changes: 29 additions & 0 deletions pkg/providers/k8s/k8s.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package k8s

import (
"context"
"fmt"
kubeauth "github.com/google/go-containerregistry/pkg/authn/kubernetes"
corev1 "k8s.io/api/core/v1"

"github.com/google/go-containerregistry/pkg/authn"
)

type Provider struct {
pullSecretsGetter func(image string) []corev1.Secret
}

func NewProvider(pullSecretsGetter func(image string) []corev1.Secret) *Provider {
return &Provider{
pullSecretsGetter: pullSecretsGetter,
}
}

func (p Provider) GetAuthKeychain(registryStr string) (authn.Keychain, error) {
dereferencedPullSecrets := p.pullSecretsGetter(registryStr)
kc, err := kubeauth.NewFromPullSecrets(context.TODO(), dereferencedPullSecrets)
if err != nil {
return nil, fmt.Errorf("error while processing keychain from secrets: %w", err)
}
return kc, nil
}
35 changes: 35 additions & 0 deletions pkg/providers/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package providers

import (
corev1 "k8s.io/api/core/v1"
"strings"

"github.com/flant/k8s-image-availability-exporter/pkg/providers/amazon"
"github.com/flant/k8s-image-availability-exporter/pkg/providers/k8s"
"github.com/google/go-containerregistry/pkg/authn"
)

type Provider interface {
GetAuthKeychain(registryStr string) (authn.Keychain, error)
}

type ProviderRegistry map[string]Provider

func NewProviderChain(pullSecretsGetter func(image string) []corev1.Secret) ProviderRegistry {
return map[string]Provider{
"amazon": amazon.NewProvider(),
"k8s": k8s.NewProvider(pullSecretsGetter),
}
}

type ImagePullSecretsFunc func(image string) []corev1.Secret

func (p ProviderRegistry) GetAuthKeychain(registryStr string) (authn.Keychain, error) {
switch {
case strings.Contains(registryStr, "amazonaws.com"):
return p["amazon"].GetAuthKeychain(registryStr)

default:
return p["k8s"].GetAuthKeychain(registryStr)
}
}
28 changes: 20 additions & 8 deletions pkg/registry/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ import (
"crypto/x509"
"errors"
"fmt"
"net/http"
"os"
"regexp"
"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"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/cache"
"net/http"
"os"
"regexp"
"strings"
"time"

"github.com/prometheus/client_golang/prometheus"

Expand Down Expand Up @@ -68,6 +69,8 @@ type Checker struct {
kubeClient *kubernetes.Clientset

config registryCheckerConfig

providerRegistry providers.ProviderRegistry
}

func NewChecker(
Expand Down Expand Up @@ -253,6 +256,12 @@ func NewChecker(

rc.imageStore.RunGC(rc.controllerIndexers.GetContainerInfosForImage)

pullSecretsGetter := func(image string) []corev1.Secret {
return rc.controllerIndexers.GetImagePullSecrets(image)
}
pc := providers.NewProviderChain(pullSecretsGetter)
rc.providerRegistry = pc

return rc
}

Expand Down Expand Up @@ -290,8 +299,11 @@ imagesLoop:
}

func (rc *Checker) Check(imageName string) store.AvailabilityMode {
keyChain := rc.controllerIndexers.GetKeychainForImage(imageName)

keyChain, err := rc.providerRegistry.GetAuthKeychain(imageName)
if err != nil {
logrus.Warn("error while getting keychain for: ", err)
return store.UnknownError
}
log := logrus.WithField("image_name", imageName)
return rc.checkImageAvailability(log, imageName, keyChain)
}
Expand Down
13 changes: 2 additions & 11 deletions pkg/registry/indexers.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package registry

import (
"context"
"fmt"
"slices"
"strings"

"github.com/flant/k8s-image-availability-exporter/pkg/store"
"github.com/google/go-containerregistry/pkg/authn"
kubeauth "github.com/google/go-containerregistry/pkg/authn/kubernetes"
"github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
Expand Down Expand Up @@ -257,7 +254,7 @@ func (ci ControllerIndexers) GetContainerInfosForImage(image string) (ret []stor
return
}

func (ci ControllerIndexers) GetKeychainForImage(image string) authn.Keychain {
func (ci ControllerIndexers) GetImagePullSecrets(image string) []corev1.Secret {
objs := ci.GetObjectsByImageIndex(image)

var refSet = map[string]struct{}{}
Expand All @@ -284,11 +281,5 @@ func (ci ControllerIndexers) GetKeychainForImage(image string) authn.Keychain {
if len(dereferencedPullSecrets) == 0 {
return nil
}

kc, err := kubeauth.NewFromPullSecrets(context.TODO(), dereferencedPullSecrets)
if err != nil {
logrus.Panic(err)
}

return kc
return dereferencedPullSecrets
}
Loading