diff --git a/README.md b/README.md index b449e32..a13d56f 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,8 @@ Usage of k8s-image-availability-exporter: namespace label for checks -skip-registry-cert-verification whether to skip registries' certificate verification + -mirror-scheme value + Add a mirror scheme (format: mirror=scheme). Beware allow-plain-http is a overriding option. ``` ## Metrics diff --git a/main.go b/main.go index 1c55640..d13577c 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ import ( func main() { cp := newCaPaths() mirrors := newMirrorMap() + mirrorSchemes := newMirrorSchemeMap() forceCheckDisabledControllerKindsParser := cli.NewForceCheckDisabledControllerKindsParser() imageCheckInterval := flag.Duration("check-interval", time.Minute, "image re-check interval") @@ -43,6 +44,7 @@ func main() { 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)") + flag.Var(&mirrorSchemes, "mirror-scheme", "Add a mirror scheme (format: mirror=scheme). Beware: allow-plain-http is a overriding option.") flag.Func("force-check-disabled-controllers", `comma-separated list of controller kinds for which image is forcibly checked, even when workloads are disabled or suspended. Acceptable values include "Deployment", "StatefulSet", "DaemonSet", "Cronjob" or "*" for all kinds (this option is case-insensitive)`, forceCheckDisabledControllerKindsParser.Parse) flag.Parse() @@ -103,6 +105,7 @@ func main() { *defaultRegistry, *namespaceLabels, mirrors, + mirrorSchemes, ) prometheus.MustRegister(registryChecker) @@ -125,6 +128,7 @@ func main() { var ( _ flag.Value = (*caPaths)(nil) _ flag.Value = (*mirrorMap)(nil) + _ flag.Value = (*mirrorSchemeMap)(nil) ) // caPaths is a custom flag type for a list of paths to CA certificates @@ -162,3 +166,29 @@ func (m *mirrorMap) Set(value string) error { (*m)[result[0]] = result[1] return nil } + +type mirrorSchemeMap map[string]string + +func newMirrorSchemeMap() mirrorSchemeMap { + return make(mirrorSchemeMap) +} + +func (m *mirrorSchemeMap) String() string { + return fmt.Sprintf("%v", *m) +} + +func (m *mirrorSchemeMap) Set(value string) error { + parts := strings.Split(value, "=") + if len(parts) != 2 { + return errors.New("invalid format for mirror scheme, must be mirror=scheme") + } + + mirror := parts[0] + scheme := strings.ToUpper(parts[1]) + if scheme != "HTTP" && scheme != "HTTPS" { + return errors.New("invalid scheme, must be HTTP or HTTPS") + } + + (*m)[mirror] = scheme + return nil +} diff --git a/pkg/registry/checker.go b/pkg/registry/checker.go index 84b0fd0..53f428f 100644 --- a/pkg/registry/checker.go +++ b/pkg/registry/checker.go @@ -6,6 +6,12 @@ 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/providers/amazon" "github.com/flant/k8s-image-availability-exporter/pkg/providers/k8s" @@ -14,11 +20,6 @@ import ( "github.com/google/go-containerregistry/pkg/v1/remote/transport" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/cache" - "net/http" - "os" - "regexp" - "strings" - "time" "github.com/prometheus/client_golang/prometheus" @@ -45,9 +46,10 @@ const ( ) type registryCheckerConfig struct { - defaultRegistry string - plainHTTP bool - mirrorsMap map[string]string + defaultRegistry string + plainHTTP bool + mirrorsMap map[string]string + mirrorsSchemeMap map[string]string } type Checker struct { @@ -87,6 +89,7 @@ func NewChecker( defaultRegistry string, namespaceLabel string, mirrorsMap map[string]string, + mirrorsSchemeMap map[string]string, ) *Checker { informerFactory := informers.NewSharedInformerFactory(kubeClient, time.Hour) @@ -129,9 +132,10 @@ func NewChecker( kubeClient: kubeClient, config: registryCheckerConfig{ - defaultRegistry: defaultRegistry, - plainHTTP: plainHTTP, - mirrorsMap: mirrorsMap, + defaultRegistry: defaultRegistry, + plainHTTP: plainHTTP, + mirrorsMap: mirrorsMap, + mirrorsSchemeMap: mirrorsSchemeMap, }, } @@ -317,22 +321,35 @@ func (rc *Checker) Check(imageName string) store.AvailabilityMode { return rc.checkImageAvailability(log, imageName, keyChain) } -func getImageWithMirror(originalImage string, mirrors map[string]string) string { +func getImageWithMirror(originalImage string, mirrors map[string]string, mirrorsSchemes map[string]string) (image string, allowPlainHTTP bool) { for originalRepo, mirrorRepo := range mirrors { if strings.HasPrefix(originalImage, originalRepo) { - return strings.Replace(originalImage, originalRepo, mirrorRepo, 1) + mirroredImage := strings.Replace(originalImage, originalRepo, mirrorRepo, 1) + + if scheme, ok := mirrorsSchemes[mirrorRepo]; ok && scheme != "" { + allowPlainHTTP = (scheme == "HTTP") + } + + return mirroredImage, allowPlainHTTP } } - return originalImage + return originalImage, false } func (rc *Checker) checkImageAvailability(log *logrus.Entry, imageName string, kc authn.Keychain) (availMode store.AvailabilityMode) { + allowPlainHTTP := rc.config.plainHTTP + if len(rc.config.mirrorsMap) > 0 { - imageName = getImageWithMirror(imageName, rc.config.mirrorsMap) + var mirrorAllowsPlainHTTP bool + imageName, mirrorAllowsPlainHTTP = getImageWithMirror(imageName, rc.config.mirrorsMap, rc.config.mirrorsSchemeMap) + + if !rc.config.plainHTTP && mirrorAllowsPlainHTTP { + allowPlainHTTP = true + } } - ref, err := parseImageName(imageName, rc.config.defaultRegistry, rc.config.plainHTTP) + ref, err := parseImageName(imageName, rc.config.defaultRegistry, allowPlainHTTP) if err != nil { return checkImageNameParseErr(log, err) }