From 0bac118dcc11171cb68fd50338a593c2b0e88384 Mon Sep 17 00:00:00 2001 From: derailed Date: Fri, 16 Jul 2021 11:09:40 -0600 Subject: [PATCH] fix #186 and #178 --- internal/client/config.go | 27 ++++++++++++------ internal/client/revision.go | 32 +++++++++++++++++++++ internal/client/revision_test.go | 37 ++++++++++++++++++++++++ pkg/popeye.go | 48 ++++++++++++++++++++------------ 4 files changed, 117 insertions(+), 27 deletions(-) create mode 100644 internal/client/revision.go create mode 100644 internal/client/revision_test.go diff --git a/internal/client/config.go b/internal/client/config.go index 66717aea..ba3635f4 100644 --- a/internal/client/config.go +++ b/internal/client/config.go @@ -283,17 +283,16 @@ func (c *Config) RawConfig() (clientcmdapi.Config, error) { log.Debug().Msgf("Context switch detected... %s vs %s", c.rawConfig.CurrentContext, c.currentContext) c.currentContext = c.rawConfig.CurrentContext c.reset() + return *c.rawConfig, nil } - if c.rawConfig == nil { - c.ensureConfig() - cfg, err := c.clientConfig.RawConfig() - if err != nil { - return cfg, err - } - c.rawConfig = &cfg - c.currentContext = cfg.CurrentContext + c.ensureConfig() + cfg, err := c.clientConfig.RawConfig() + if err != nil { + return cfg, err } + c.rawConfig = &cfg + c.currentContext = cfg.CurrentContext return *c.rawConfig, nil } @@ -311,12 +310,22 @@ func (c *Config) RESTConfig() (*restclient.Config, error) { c.restConfig.QPS = defaultQPS c.restConfig.Burst = defaultBurst c.restConfig.Timeout = defaultCallTimeoutDuration - + restclient.SetDefaultWarningHandler(newLoggerHandler()) log.Debug().Msgf("Connecting to API Server %s", c.restConfig.Host) return c.restConfig, nil } +type loggerHandler struct{} + +func newLoggerHandler() loggerHandler { + return loggerHandler{} +} + +func (l loggerHandler) HandleWarningHeader(code int, agent string, text string) { + log.Warn().Msgf("[%d] (%q) -- %s", code, agent, text) +} + func (c *Config) ensureConfig() { if c.clientConfig != nil { return diff --git a/internal/client/revision.go b/internal/client/revision.go new file mode 100644 index 00000000..b59a302e --- /dev/null +++ b/internal/client/revision.go @@ -0,0 +1,32 @@ +package client + +import ( + "fmt" + "regexp" + "strconv" + + "k8s.io/apimachinery/pkg/version" +) + +type Revision struct { + Info *version.Info + Major, Minor int +} + +var minorRX = regexp.MustCompile(`(\d+)\+?`) + +func NewRevision(info *version.Info) (*Revision, error) { + major, err := strconv.Atoi(info.Major) + if err != nil { + return nil, fmt.Errorf("unable to extract major %q", info.Major) + } + minors := minorRX.FindStringSubmatch(info.Minor) + if len(minors) < 2 { + return nil, fmt.Errorf("unable to extract minor %q", info.Minor) + } + minor, err := strconv.Atoi(minors[1]) + if err != nil { + return nil, err + } + return &Revision{Info: info, Major: major, Minor: minor}, nil +} diff --git a/internal/client/revision_test.go b/internal/client/revision_test.go new file mode 100644 index 00000000..4c75c28b --- /dev/null +++ b/internal/client/revision_test.go @@ -0,0 +1,37 @@ +package client_test + +import ( + "testing" + + "github.com/derailed/popeye/internal/client" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/version" +) + +func TestNewRevision(t *testing.T) { + uu := map[string]struct { + info *version.Info + major, minor int + }{ + "plain": { + info: &version.Info{Major: "1", Minor: "18+"}, + major: 1, + minor: 18, + }, + "no-plus": { + info: &version.Info{Major: "1", Minor: "18"}, + major: 1, + minor: 18, + }, + } + + for k := range uu { + u := uu[k] + t.Run(k, func(t *testing.T) { + r, err := client.NewRevision(u.info) + assert.Nil(t, err) + assert.Equal(t, u.major, r.Major) + assert.Equal(t, u.minor, r.Minor) + }) + } +} diff --git a/pkg/popeye.go b/pkg/popeye.go index 17f2036f..812e69ee 100644 --- a/pkg/popeye.go +++ b/pkg/popeye.go @@ -17,9 +17,6 @@ import ( "strings" "time" - "github.com/prometheus/common/expfmt" - "k8s.io/apimachinery/pkg/version" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3/s3manager" @@ -30,6 +27,7 @@ import ( "github.com/derailed/popeye/internal/scrub" "github.com/derailed/popeye/pkg/config" "github.com/derailed/popeye/types" + "github.com/prometheus/common/expfmt" "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) @@ -86,7 +84,7 @@ func (p *Popeye) Init() error { return err } } - rev, err := p.factory.Client().ServerVersion() + rev, err := p.revision() if err != nil { return err } @@ -111,7 +109,7 @@ func (p *Popeye) SetFactory(f types.Factory) { p.factory = f } -func (p *Popeye) scannedGVRs(rev *version.Info) []string { +func (p *Popeye) scannedGVRs(rev *client.Revision) []string { mm := []string{ "v1/limitranges", "v1/services", @@ -137,12 +135,12 @@ func (p *Popeye) scannedGVRs(rev *version.Info) []string { "rbac.authorization.k8s.io/v1/rolebindings", } - if rev.Minor == "18" || rev.Minor == "17" { + if rev.Minor <= 18 { mm = append(mm, "networking.k8s.io/v1beta1/ingresses") } else { mm = append(mm, "networking.k8s.io/v1/ingresses") } - if rev.Minor == "21" { + if rev.Minor >= 21 { mm = append(mm, "policy/v1/poddisruptionbudgets") } else { mm = append(mm, "policy/v1beta1/poddisruptionbudgets") @@ -162,15 +160,21 @@ func (p *Popeye) initFactory() error { if p.flags.StandAlone { return nil } - ns := client.AllNamespaces - if p.flags.ConfigFlags.Namespace != nil { - ns = *p.flags.ConfigFlags.Namespace + + info, err := p.factory.Client().ServerVersion() + if err != nil { + return err } - rev, err := p.factory.Client().ServerVersion() + rev, err := client.NewRevision(info) if err != nil { return err } + ns := client.AllNamespaces + if p.flags.ConfigFlags.Namespace != nil { + ns = *p.flags.ConfigFlags.Namespace + } + f.Start(ns) for _, gvr := range p.scannedGVRs(rev) { ok, err := clt.CanI(client.AllNamespaces, gvr, types.ReadAllAccess) @@ -186,7 +190,16 @@ func (p *Popeye) initFactory() error { return nil } -func (p *Popeye) sanitizers(rev *version.Info) map[string]scrubFn { +func (p *Popeye) revision() (*client.Revision, error) { + info, err := p.factory.Client().ServerVersion() + if err != nil { + return nil, err + } + + return client.NewRevision(info) +} + +func (p *Popeye) sanitizers(rev *client.Revision) map[string]scrubFn { mm := map[string]scrubFn{ "cluster": scrub.NewCluster, "v1/configmaps": scrub.NewConfigMap, @@ -213,10 +226,10 @@ func (p *Popeye) sanitizers(rev *version.Info) map[string]scrubFn { "rbac.authorization.k8s.io/v1/rolebindings": scrub.NewRoleBinding, } - if rev.Minor == "18" || rev.Minor == "17" { + if rev.Minor <= 18 { mm["networking.k8s.io/v1beta1/ingresses"] = scrub.NewIngress } - if rev.Minor == "21" { + if rev.Minor >= 21 { mm["policy/v1/poddisruptionbudgets"] = scrub.NewPodDisruptionBudget } @@ -294,17 +307,16 @@ func (p *Popeye) sanitize() (int, int, error) { var total, errCount int var nodeGVR = client.NewGVR("v1/nodes") cache := scrub.NewCache(p.factory, p.config) - rev, err := p.factory.Client().ServerVersion() + + rev, err := p.revision() if err != nil { return 0, 0, err } - for k, fn := range p.sanitizers(rev) { gvr := client.NewGVR(k) if p.aliases.Exclude(gvr, p.config.Sections()) { continue } - // Skip node sanitizer if active namespace is set. if gvr == nodeGVR && p.factory.Client().ActiveNamespace() != client.AllNamespaces { continue @@ -330,10 +342,10 @@ func (p *Popeye) sanitize() (int, int, error) { close(c) } } - if count == 0 { return errCount, 0, nil } + return errCount, score / count, nil }