diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml
deleted file mode 100644
index 76ccfc89..00000000
--- a/.github/dependabot.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-version: 2
-updates:
-- package-ecosystem: "gomod"
- directory: "/"
- allow:
- - dependency-type: "all"
-- package-ecosystem: "github-actions"
- directory: "/"
diff --git a/Makefile b/Makefile
index 21f55d70..a6b085d0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
NAME := popeye
PACKAGE := github.com/derailed/$(NAME)
-VERSION := v0.21.5
+VERSION := v0.21.6
GIT := $(shell git rev-parse --short HEAD)
DATE := $(shell date +%FT%T%Z)
IMG_NAME := derailed/popeye
diff --git a/README.md b/README.md
index 8cffedd9..ed7d1033 100644
--- a/README.md
+++ b/README.md
@@ -125,10 +125,14 @@ popeye
popeye -n fred
# Run Popeye in all namespaces
popeye -A
-# Popeye uses a spinach config file of course! aka spinachyaml!
+# Run Popeye uses a spinach config file of course! aka spinachyaml!
popeye -f spinach.yaml
-# Popeye a cluster using a kubeconfig context.
+# Run Popeye a cluster using a kubeconfig context.
popeye --context olive
+# Run Popeye with specific linters and log to the console
+popeye -n ns1 -s pod,svc --logs none
+# Run Popeye for a given namespace in a given log file and debug logs
+popeye -n ns1 --logs /tmp/fred.log -v4
# Stuck?
popeye help
```
diff --git a/change_logs/release_v0.21.6.md b/change_logs/release_v0.21.6.md
new file mode 100644
index 00000000..b9612a32
--- /dev/null
+++ b/change_logs/release_v0.21.6.md
@@ -0,0 +1,32 @@
+
+
+# Release v0.21.6
+
+## Notes
+
+Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated!
+
+This project offers a GitHub Sponsor button (over here 👆). As you well know this is not pimped out by big corps with deep pockets. If you feel `Popeye` is saving you cycles diagnosing potential cluster issues please consider sponsoring this project!! It does go a long way in keeping our servers lights on and beers in our fridge.
+
+Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
+
+---
+
+## Maintenance Release
+
+---
+
+## Resolved Issues
+
+* [#370](https://github.com/derailed/popeye/issues/370) default service account check
+* [#356](https://github.com/derailed/popeye/issues/356) Allow foreground execution, with output to STDOUT/STDERR and disabling output to popeye.log file
+* [#341](https://github.com/derailed/popeye/issues/341) Linter Node : no linters matched query
+* [#337](https://github.com/derailed/popeye/issues/337) Output logs in stdout instead of file
+* [#301](https://github.com/derailed/popeye/issues/301) Allow Popeye to be used by more than one user on a host (/tmp/popeye.log permission problem)
+* [#267](https://github.com/derailed/popeye/issues/267) Allow container exclusions based on regex
+* [#200](https://github.com/derailed/popeye/issues/200) Can't filter containers in spinach.yaml for deployments
+* [#168](https://github.com/derailed/popeye/issues/168) Skip checks at container level
+
+---
+
+ © 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
diff --git a/cmd/root.go b/cmd/root.go
index 57fd9b74..d4179289 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -52,13 +52,14 @@ func Execute() {
// Doit runs the scans and lints pass over the specified cluster.
func doIt(cmd *cobra.Command, args []string) {
+ bomb(initLogs())
+
defer func() {
if err := recover(); err != nil {
pkg.BailOut(err.(error))
}
}()
- zerolog.SetGlobalLevel(zerolog.DebugLevel)
clearScreen()
bomb(flags.Validate())
flags.StandAlone = true
@@ -84,7 +85,7 @@ func bomb(err error) {
if err == nil {
return
}
- panic(fmt.Errorf("💥 %s\n", report.Colorize(err.Error(), report.ColorRed)))
+ panic(fmt.Errorf("💥 %s", report.Colorize(err.Error(), report.ColorRed)))
}
func initPopeyeFlags() {
@@ -166,6 +167,15 @@ func initPopeyeFlags() {
[]string{},
"Specify which resources to include in the scan ie -s po,svc",
)
+
+ rootCmd.Flags().IntVarP(flags.LogLevel, "log-level", "v",
+ 1,
+ "Specify log level. Use 0|1|2|3|4 for disable|info|warn|error|debug",
+ )
+ rootCmd.Flags().StringVarP(flags.LogFile, "logs", "",
+ pkg.LogFile,
+ "Specify log file location. Use `none` for stdout",
+ )
}
func initKubeConfigFlags() {
@@ -212,6 +222,49 @@ func initKubeConfigFlags() {
)
}
+func initLogs() error {
+ var logs string
+ if *flags.LogFile != "none" {
+ logs = *flags.LogFile
+ }
+
+ var file = os.Stdout
+ if logs != "" {
+ mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
+ var err error
+ file, err = os.OpenFile(logs, mod, 0644)
+ if err != nil {
+ return fmt.Errorf("unable to create Popeye log file: %w", err)
+ }
+ }
+ log.Logger = log.Output(zerolog.ConsoleWriter{Out: file})
+
+ if flags.LogLevel == nil {
+ zerolog.SetGlobalLevel(zerolog.InfoLevel)
+ } else {
+ zerolog.SetGlobalLevel(toLogLevel(*flags.LogLevel))
+ }
+
+ return nil
+}
+
+func toLogLevel(level int) zerolog.Level {
+ switch level {
+ case -1:
+ return zerolog.TraceLevel
+ case 0:
+ return zerolog.Disabled
+ case 1:
+ return zerolog.InfoLevel
+ case 2:
+ return zerolog.WarnLevel
+ case 3:
+ return zerolog.ErrorLevel
+ default:
+ return zerolog.DebugLevel
+ }
+}
+
func initFlags() {
initPopeyeFlags()
initKubeConfigFlags()
diff --git a/go.mod b/go.mod
index bcdb2daa..872e5ad9 100644
--- a/go.mod
+++ b/go.mod
@@ -15,6 +15,7 @@ require (
github.com/stretchr/testify v1.10.0
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/net v0.31.0
+ golang.org/x/text v0.21.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.32.0
@@ -122,7 +123,6 @@ require (
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
- golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
diff --git a/internal/cilium/cache/cep.go b/internal/cilium/cache/cep.go
index 92b046af..72f75e19 100644
--- a/internal/cilium/cache/cep.go
+++ b/internal/cilium/cache/cep.go
@@ -27,7 +27,7 @@ func NewCiliumEndpoint(dba *db.DB) *CiliumEndpoint {
return &CiliumEndpoint{db: dba}
}
-// CiliumEndpointRefs computes all CiliumEndpoints external references.
+// CEPRefs computes all CiliumEndpoints external references.
func (p *CiliumEndpoint) CEPRefs(refs *sync.Map) error {
txn, it := p.db.MustITFor(internal.Glossary[cilium.CEP])
defer txn.Abort()
diff --git a/internal/cilium/lint/ccnp.go b/internal/cilium/lint/ccnp.go
index 993ede6b..cd3e5cf5 100644
--- a/internal/cilium/lint/ccnp.go
+++ b/internal/cilium/lint/ccnp.go
@@ -57,20 +57,18 @@ func (s *CiliumClusterwideNetworkPolicy) Lint(ctx context.Context) error {
}
func (s *CiliumClusterwideNetworkPolicy) checkRule(ctx context.Context, r *api.Rule) error {
- if r.EndpointSelector.Size() > 0 {
- if ok, err := s.checkEPSel(r.EndpointSelector); err != nil {
- return err
- } else if !ok {
- s.AddCode(ctx, 1700, "endpoint")
- }
+ if ok, err := s.checkEPSel(r.EndpointSelector); err != nil {
+ return err
+ } else if !ok {
+ s.AddCode(ctx, 1700, "endpoint")
}
- if r.NodeSelector.Size() > 0 {
- if ok, err := s.checkNodeSel(r.NodeSelector); err != nil {
- return err
- } else if !ok {
- s.AddCode(ctx, 1701)
- }
+
+ if ok, err := s.checkNodeSel(r.NodeSelector); err != nil {
+ return err
+ } else if !ok {
+ s.AddCode(ctx, 1701)
}
+
for _, ing := range r.Ingress {
for _, sel := range ing.FromEndpoints {
if ok, err := s.checkEPSel(sel); err != nil {
@@ -94,6 +92,10 @@ func (s *CiliumClusterwideNetworkPolicy) checkRule(ctx context.Context, r *api.R
}
func (s *CiliumClusterwideNetworkPolicy) checkEPSel(sel api.EndpointSelector) (bool, error) {
+ if sel.Size() == 0 {
+ return true, nil
+ }
+
mm, err := s.matchCEPsBySel(sel)
if err != nil {
return false, err
@@ -103,6 +105,10 @@ func (s *CiliumClusterwideNetworkPolicy) checkEPSel(sel api.EndpointSelector) (b
}
func (s *CiliumClusterwideNetworkPolicy) checkNodeSel(sel api.EndpointSelector) (bool, error) {
+ if sel.Size() == 0 {
+ return true, nil
+ }
+
mm, err := s.matchNodesBySel(sel)
if err != nil {
return false, err
diff --git a/internal/cilium/lint/cep.go b/internal/cilium/lint/cep.go
index 7a276340..c27ac9f0 100644
--- a/internal/cilium/lint/cep.go
+++ b/internal/cilium/lint/cep.go
@@ -100,9 +100,9 @@ func (s *CiliumEndpoint) checkNode(ctx context.Context, cep *v2.CiliumEndpoint)
if err != nil {
return err
}
+ nodeIP := cep.Status.Networking.NodeIP
for _, n := range nn {
- ip, _ := getIPs(n.Status.Addresses)
- if ip != "" && ip == cep.Status.Networking.NodeIP {
+ if matchIP(n.Status.Addresses, nodeIP) {
return nil
}
}
@@ -113,15 +113,15 @@ func (s *CiliumEndpoint) checkNode(ctx context.Context, cep *v2.CiliumEndpoint)
// Helpers...
-func getIPs(addrs []v1.NodeAddress) (iIP, eIP string) {
+func matchIP(addrs []v1.NodeAddress, ip string) bool {
for _, a := range addrs {
- switch a.Type {
- case v1.NodeExternalIP:
- eIP = a.Address
- case v1.NodeInternalIP:
- iIP = a.Address
+ if a.Type != v1.NodeInternalIP {
+ continue
+ }
+ if a.Address == ip {
+ return true
}
}
- return
+ return false
}
diff --git a/internal/cilium/lint/cid.go b/internal/cilium/lint/cid.go
index 04a4055e..50f178bf 100644
--- a/internal/cilium/lint/cid.go
+++ b/internal/cilium/lint/cid.go
@@ -61,8 +61,7 @@ func (s *CiliumIdentity) Lint(ctx context.Context) error {
}
func (s *CiliumIdentity) checkStale(ctx context.Context, fqn string, refs *sync.Map) error {
- _, ok := refs.Load(icache.ResFqn(cache.CIDKey, fqn))
- if !ok {
+ if _, ok := refs.Load(icache.ResFqn(cache.CIDKey, fqn)); !ok {
s.AddCode(ctx, 1600)
}
diff --git a/internal/cilium/lint/cnp.go b/internal/cilium/lint/cnp.go
index 9a99a0c1..12d3ca43 100644
--- a/internal/cilium/lint/cnp.go
+++ b/internal/cilium/lint/cnp.go
@@ -88,6 +88,10 @@ func (s *CiliumNetworkPolicy) checkRule(ctx context.Context, ns string, r *api.R
}
func (s *CiliumNetworkPolicy) checkEPSel(ns string, sel api.EndpointSelector) (bool, error) {
+ if sel.Size() == 0 {
+ return true, nil
+ }
+
mm, err := s.matchCEPsBySel(ns, sel)
if err != nil {
return false, err
diff --git a/internal/client/config.go b/internal/client/config.go
index a4c2f40d..57325f73 100644
--- a/internal/client/config.go
+++ b/internal/client/config.go
@@ -249,7 +249,7 @@ func (c *Config) CurrentNamespaceName() (string, error) {
return ct.Namespace, nil
}
- return DefaultNamespace, nil
+ return DefaultNamespace, fmt.Errorf("invalid context specified: %q", cfg.CurrentContext)
}
// NamespaceNames fetch all available namespaces on current cluster.
diff --git a/internal/dao/generic.go b/internal/dao/generic.go
index ed214779..67512158 100644
--- a/internal/dao/generic.go
+++ b/internal/dao/generic.go
@@ -5,6 +5,7 @@ package dao
import (
"context"
+ "fmt"
"github.com/derailed/popeye/internal"
"github.com/derailed/popeye/internal/client"
@@ -22,7 +23,10 @@ type Generic struct {
// List returns a collection of resources.
func (g *Generic) List(ctx context.Context) ([]runtime.Object, error) {
labelSel, _ := ctx.Value(internal.KeyLabels).(string)
- ns, _ := ctx.Value(internal.KeyNamespace).(string)
+ ns, ok := ctx.Value(internal.KeyNamespace).(string)
+ if !ok {
+ return nil, fmt.Errorf("BOOM!! no namespace found in context %s", g.gvr)
+ }
if client.IsAllNamespace(ns) {
ns = client.AllNamespaces
}
diff --git a/internal/dao/resource.go b/internal/dao/resource.go
index 9e6394da..0963b4ab 100644
--- a/internal/dao/resource.go
+++ b/internal/dao/resource.go
@@ -29,7 +29,7 @@ func (r *Resource) List(ctx context.Context) ([]runtime.Object, error) {
}
ns, ok := ctx.Value(internal.KeyNamespace).(string)
if !ok {
- panic(fmt.Sprintf("BOOM no namespace in context %s", r.gvr))
+ return nil, fmt.Errorf("BOOM!! no namespace found in context %s", r.gvr)
}
if r.gvr == internal.Glossary[internal.NS] {
ns = client.AllNamespaces
diff --git a/internal/issues/assets/codes.yaml b/internal/issues/assets/codes.yaml
index 80c9729a..42a52b82 100644
--- a/internal/issues/assets/codes.yaml
+++ b/internal/issues/assets/codes.yaml
@@ -100,6 +100,9 @@ codes:
307:
message: "%s references a non existing ServiceAccount: %q"
severity: 2
+ 308:
+ message: Uses "default" bound ServiceAccount. Could be a security risk
+ severity: 3
# General
400:
diff --git a/internal/issues/codes_test.go b/internal/issues/codes_test.go
index 393cc48d..97d8a273 100644
--- a/internal/issues/codes_test.go
+++ b/internal/issues/codes_test.go
@@ -15,7 +15,7 @@ func TestCodesLoad(t *testing.T) {
cc, err := issues.LoadCodes()
assert.Nil(t, err)
- assert.Equal(t, 116, len(cc.Glossary))
+ assert.Equal(t, 117, len(cc.Glossary))
assert.Equal(t, "No liveness probe", cc.Glossary[103].Message)
assert.Equal(t, rules.WarnLevel, cc.Glossary[103].Severity)
}
diff --git a/internal/lint/cronjob.go b/internal/lint/cronjob.go
index 802b57ed..8785b40f 100644
--- a/internal/lint/cronjob.go
+++ b/internal/lint/cronjob.go
@@ -40,7 +40,7 @@ func (s *CronJob) Lint(ctx context.Context) error {
cj := o.(*batchv1.CronJob)
fqn := client.FQN(cj.Namespace, cj.Name)
s.InitOutcome(fqn)
- ctx = internal.WithSpec(ctx, SpecFor(fqn, cj))
+ ctx = internal.WithSpec(ctx, coSpecFor(fqn, cj, cj.Spec.JobTemplate.Spec.Template.Spec))
s.checkCronJob(ctx, fqn, cj)
s.checkContainers(ctx, fqn, cj.Spec.JobTemplate.Spec.Template.Spec)
s.checkUtilization(ctx, over, fqn)
diff --git a/internal/lint/dp.go b/internal/lint/dp.go
index c35d02c1..3e884cc5 100644
--- a/internal/lint/dp.go
+++ b/internal/lint/dp.go
@@ -39,7 +39,7 @@ func (s *Deployment) Lint(ctx context.Context) error {
dp := o.(*appsv1.Deployment)
fqn := client.FQN(dp.Namespace, dp.Name)
s.InitOutcome(fqn)
- ctx = internal.WithSpec(ctx, SpecFor(fqn, dp))
+ ctx = internal.WithSpec(ctx, coSpecFor(fqn, dp, dp.Spec.Template.Spec))
s.checkDeployment(ctx, dp)
s.checkContainers(ctx, fqn, dp.Spec.Template.Spec)
s.checkUtilization(ctx, over, dp)
diff --git a/internal/lint/ds.go b/internal/lint/ds.go
index 523d9904..725fca07 100644
--- a/internal/lint/ds.go
+++ b/internal/lint/ds.go
@@ -38,7 +38,7 @@ func (s *DaemonSet) Lint(ctx context.Context) error {
ds := o.(*appsv1.DaemonSet)
fqn := client.FQN(ds.Namespace, ds.Name)
s.InitOutcome(fqn)
- ctx = internal.WithSpec(ctx, SpecFor(fqn, ds))
+ ctx = internal.WithSpec(ctx, coSpecFor(fqn, ds, ds.Spec.Template.Spec))
s.checkDaemonSet(ctx, ds)
s.checkContainers(ctx, fqn, ds.Spec.Template.Spec)
diff --git a/internal/lint/helper.go b/internal/lint/helper.go
index bd74f488..36a7a5ee 100644
--- a/internal/lint/helper.go
+++ b/internal/lint/helper.go
@@ -28,6 +28,25 @@ const (
type qos = int
+func coSpecFor(fqn string, o metav1.ObjectMetaAccessor, spec v1.PodSpec) rules.Spec {
+ rule := SpecFor(fqn, o)
+ rule.Containers = fetchContainers(spec)
+
+ return rule
+}
+
+func fetchContainers(podTemplate v1.PodSpec) []string {
+ containers := make([]string, 0, len(podTemplate.InitContainers)+len(podTemplate.Containers))
+ for _, co := range podTemplate.InitContainers {
+ containers = append(containers, co.Name)
+ }
+ for _, co := range podTemplate.Containers {
+ containers = append(containers, co.Name)
+ }
+
+ return containers
+}
+
// SpecFor construct a new run spec for a given resource.
func SpecFor(fqn string, o metav1.ObjectMetaAccessor) rules.Spec {
spec := rules.Spec{
@@ -146,7 +165,7 @@ func asMB(q resource.Quantity) string {
return fmt.Sprintf("%vMi", toMB(q))
}
-// PodResources computes pod resouces as sum of containers allocations.
+// PodResources computes pod resources as sum of containers allocations.
func podResources(spec v1.PodSpec) (cpu, mem resource.Quantity) {
for _, co := range spec.InitContainers {
c, m, _ := containerResources(co)
diff --git a/internal/lint/job.go b/internal/lint/job.go
index 84b25e2b..72e7dfa1 100644
--- a/internal/lint/job.go
+++ b/internal/lint/job.go
@@ -39,7 +39,7 @@ func (s *Job) Lint(ctx context.Context) error {
j := o.(*batchv1.Job)
fqn := client.FQN(j.Namespace, j.Name)
s.InitOutcome(fqn)
- ctx = internal.WithSpec(ctx, SpecFor(fqn, j))
+ ctx = internal.WithSpec(ctx, coSpecFor(fqn, j, j.Spec.Template.Spec))
s.checkJob(ctx, fqn, j)
s.checkContainers(ctx, fqn, j.Spec.Template.Spec)
s.checkUtilization(ctx, over, fqn)
diff --git a/internal/lint/np.go b/internal/lint/np.go
index 7abbb6a2..090cebe0 100644
--- a/internal/lint/np.go
+++ b/internal/lint/np.go
@@ -23,11 +23,13 @@ type direction string
const (
dirIn direction = "Ingress"
dirOut direction = "Egress"
- bothPols = "All"
+ bothPols = "all"
noPols = ""
+ ingress = "ingress"
+ egress = "egress"
)
-// NetworkPolicy tracks NetworkPolicy sanitizatios.
+// NetworkPolicy tracks NetworkPolicy linting.
type NetworkPolicy struct {
*issues.Collector
@@ -57,13 +59,13 @@ func (s *NetworkPolicy) Lint(ctx context.Context) error {
s.checkSelector(ctx, fqn, np.Spec.PodSelector)
s.checkIngresses(ctx, fqn, np.Spec.Ingress)
s.checkEgresses(ctx, fqn, np.Spec.Egress)
- s.checkRuleType(ctx, fqn, &np.Spec)
+ s.checkRuleType(ctx, &np.Spec)
}
return nil
}
-func (s *NetworkPolicy) checkRuleType(ctx context.Context, fqn string, spec *netv1.NetworkPolicySpec) {
+func (s *NetworkPolicy) checkRuleType(ctx context.Context, spec *netv1.NetworkPolicySpec) {
if spec.PodSelector.Size() > 0 {
return
}
@@ -72,15 +74,15 @@ func (s *NetworkPolicy) checkRuleType(ctx context.Context, fqn string, spec *net
case isAllowAll(spec):
s.AddCode(ctx, 1203, "Allow", bothPols)
case isAllowAllIngress(spec):
- s.AddCode(ctx, 1203, "Allow All", dirIn)
+ s.AddCode(ctx, 1203, "Allow all", ingress)
case isAllowAllEgress(spec):
- s.AddCode(ctx, 1203, "Allow All", dirOut)
+ s.AddCode(ctx, 1203, "Allow all", egress)
case isDenyAll(spec):
s.AddCode(ctx, 1203, "Deny", bothPols)
case isDenyAllIngress(spec):
- s.AddCode(ctx, 1203, "Deny All", dirIn)
+ s.AddCode(ctx, 1203, "Deny all", ingress)
case isDenyAllEgress(spec):
- s.AddCode(ctx, 1203, "Deny All", dirOut)
+ s.AddCode(ctx, 1203, "Deny all", egress)
}
}
@@ -157,7 +159,7 @@ func (s *NetworkPolicy) checkIPBlocks(ctx context.Context, fqn string, b *netv1.
s.AddErr(ctx, err)
}
if !s.matchPips(ns, ipnet) {
- s.AddCode(ctx, 1206, d, b.CIDR)
+ s.AddCode(ctx, 1206, strings.ToLower(string(d)), b.CIDR)
}
for _, ex := range b.Except {
_, ipnet, err := net.ParseCIDR(ex)
@@ -166,7 +168,7 @@ func (s *NetworkPolicy) checkIPBlocks(ctx context.Context, fqn string, b *netv1.
continue
}
if !s.matchPips(ns, ipnet) {
- s.AddCode(ctx, 1207, d, ex)
+ s.AddCode(ctx, 1207, strings.ToLower(string(d)), ex)
}
}
}
@@ -210,16 +212,16 @@ func (s *NetworkPolicy) checkPodSelector(ctx context.Context, nss []*v1.Namespac
}
if !found {
if len(nn) > 0 {
- s.AddCode(ctx, 1208, d, dumpSel(sel), strings.Join(nn, ","))
+ s.AddCode(ctx, 1208, strings.ToLower(string(d)), dumpSel(sel), strings.Join(nn, ","))
} else {
- s.AddCode(ctx, 1202, d, dumpSel(sel))
+ s.AddCode(ctx, 1202, strings.ToLower(string(d)), dumpSel(sel))
}
}
}
func (s *NetworkPolicy) checkNSSelector(ctx context.Context, sel *metav1.LabelSelector, nss []*v1.Namespace, d direction) bool {
if len(nss) == 0 {
- s.AddCode(ctx, 1201, d, dumpSel(sel))
+ s.AddCode(ctx, 1201, strings.ToLower(string(d)), dumpSel(sel))
return false
}
diff --git a/internal/lint/np_test.go b/internal/lint/np_test.go
index 67d99889..617f5bcd 100644
--- a/internal/lint/np_test.go
+++ b/internal/lint/np_test.go
@@ -31,46 +31,46 @@ func TestNPLintDenyAll(t *testing.T) {
ii := np.Outcome()["default/deny-all"]
assert.Equal(t, 1, len(ii))
- assert.Equal(t, `[POP-1203] Deny All policy in effect`, ii[0].Message)
+ assert.Equal(t, `[POP-1203] Deny all policy in effect`, ii[0].Message)
assert.Equal(t, rules.InfoLevel, ii[0].Level)
ii = np.Outcome()["default/deny-all-ing"]
assert.Equal(t, 1, len(ii))
- assert.Equal(t, `[POP-1203] Deny All Ingress policy in effect`, ii[0].Message)
+ assert.Equal(t, `[POP-1203] Deny all ingress policy in effect`, ii[0].Message)
assert.Equal(t, rules.InfoLevel, ii[0].Level)
ii = np.Outcome()["default/deny-all-eg"]
assert.Equal(t, 1, len(ii))
- assert.Equal(t, `[POP-1203] Deny All Egress policy in effect`, ii[0].Message)
+ assert.Equal(t, `[POP-1203] Deny all egress policy in effect`, ii[0].Message)
assert.Equal(t, rules.InfoLevel, ii[0].Level)
ii = np.Outcome()["default/allow-all"]
assert.Equal(t, 1, len(ii))
- assert.Equal(t, `[POP-1203] Allow All policy in effect`, ii[0].Message)
+ assert.Equal(t, `[POP-1203] Allow all policy in effect`, ii[0].Message)
assert.Equal(t, rules.InfoLevel, ii[0].Level)
ii = np.Outcome()["default/allow-all-ing"]
assert.Equal(t, 1, len(ii))
- assert.Equal(t, `[POP-1203] Allow All Ingress policy in effect`, ii[0].Message)
+ assert.Equal(t, `[POP-1203] Allow all ingress policy in effect`, ii[0].Message)
assert.Equal(t, rules.InfoLevel, ii[0].Level)
ii = np.Outcome()["default/allow-all-eg"]
assert.Equal(t, 1, len(ii))
- assert.Equal(t, `[POP-1203] Allow All Egress policy in effect`, ii[0].Message)
+ assert.Equal(t, `[POP-1203] Allow all egress policy in effect`, ii[0].Message)
assert.Equal(t, rules.InfoLevel, ii[0].Level)
ii = np.Outcome()["default/ip-block-all-ing"]
assert.Equal(t, 2, len(ii))
- assert.Equal(t, `[POP-1206] No pods matched Egress IPBlock 172.2.0.0/24`, ii[0].Message)
+ assert.Equal(t, `[POP-1206] No pods matched egress IPBlock 172.2.0.0/24`, ii[0].Message)
assert.Equal(t, rules.WarnLevel, ii[0].Level)
- assert.Equal(t, `[POP-1203] Deny All Ingress policy in effect`, ii[1].Message)
+ assert.Equal(t, `[POP-1203] Deny all ingress policy in effect`, ii[1].Message)
assert.Equal(t, rules.InfoLevel, ii[1].Level)
ii = np.Outcome()["default/ip-block-all-eg"]
assert.Equal(t, 2, len(ii))
- assert.Equal(t, `[POP-1206] No pods matched Ingress IPBlock 172.2.0.0/24`, ii[0].Message)
+ assert.Equal(t, `[POP-1206] No pods matched ingress IPBlock 172.2.0.0/24`, ii[0].Message)
assert.Equal(t, rules.WarnLevel, ii[0].Level)
- assert.Equal(t, `[POP-1203] Deny All Egress policy in effect`, ii[1].Message)
+ assert.Equal(t, `[POP-1203] Deny all egress policy in effect`, ii[1].Message)
assert.Equal(t, rules.InfoLevel, ii[1].Level)
}
@@ -93,26 +93,26 @@ func TestNPLint(t *testing.T) {
ii = np.Outcome()["default/np2"]
assert.Equal(t, 3, len(ii))
- assert.Equal(t, `[POP-1207] No pods matched except Ingress IPBlock 172.1.1.0/24`, ii[0].Message)
+ assert.Equal(t, `[POP-1207] No pods matched except ingress IPBlock 172.1.1.0/24`, ii[0].Message)
assert.Equal(t, rules.WarnLevel, ii[0].Level)
- assert.Equal(t, `[POP-1208] No pods match Ingress pod selector: app=p2 in namespace: ns2`, ii[1].Message)
+ assert.Equal(t, `[POP-1208] No pods match ingress pod selector: app=p2 in namespace: ns2`, ii[1].Message)
assert.Equal(t, rules.WarnLevel, ii[1].Level)
- assert.Equal(t, `[POP-1206] No pods matched Egress IPBlock 172.0.0.0/24`, ii[2].Message)
+ assert.Equal(t, `[POP-1206] No pods matched egress IPBlock 172.0.0.0/24`, ii[2].Message)
assert.Equal(t, rules.WarnLevel, ii[2].Level)
ii = np.Outcome()["default/np3"]
assert.Equal(t, 6, len(ii))
assert.Equal(t, `[POP-1200] No pods match pod selector: app=p-bozo`, ii[0].Message)
assert.Equal(t, rules.WarnLevel, ii[0].Level)
- assert.Equal(t, `[POP-1206] No pods matched Ingress IPBlock 172.2.0.0/16`, ii[1].Message)
+ assert.Equal(t, `[POP-1206] No pods matched ingress IPBlock 172.2.0.0/16`, ii[1].Message)
assert.Equal(t, rules.WarnLevel, ii[1].Level)
- assert.Equal(t, `[POP-1207] No pods matched except Ingress IPBlock 172.2.1.0/24`, ii[2].Message)
+ assert.Equal(t, `[POP-1207] No pods matched except ingress IPBlock 172.2.1.0/24`, ii[2].Message)
assert.Equal(t, rules.WarnLevel, ii[2].Level)
- assert.Equal(t, `[POP-1201] No namespaces match Ingress namespace selector: app-In-ns-bozo`, ii[3].Message)
+ assert.Equal(t, `[POP-1201] No namespaces match ingress namespace selector: app-In-ns-bozo`, ii[3].Message)
assert.Equal(t, rules.WarnLevel, ii[3].Level)
- assert.Equal(t, `[POP-1202] No pods match Ingress pod selector: app=pod-bozo`, ii[4].Message)
+ assert.Equal(t, `[POP-1202] No pods match ingress pod selector: app=pod-bozo`, ii[4].Message)
assert.Equal(t, rules.WarnLevel, ii[4].Level)
- assert.Equal(t, `[POP-1208] No pods match Egress pod selector: app=p1-missing in namespace: default`, ii[5].Message)
+ assert.Equal(t, `[POP-1208] No pods match egress pod selector: app=p1-missing in namespace: default`, ii[5].Message)
assert.Equal(t, rules.WarnLevel, ii[5].Level)
}
diff --git a/internal/lint/pod.go b/internal/lint/pod.go
index 22cdbc9e..507e47ef 100644
--- a/internal/lint/pod.go
+++ b/internal/lint/pod.go
@@ -51,6 +51,7 @@ func NewPod(co *issues.Collector, db *db.DB) *Pod {
// Lint cleanse the resource..
func (s *Pod) Lint(ctx context.Context) error {
+ boundSA := boundDefaultSA(s.db)
txn, it := s.db.MustITFor(internal.Glossary[internal.PO])
defer txn.Abort()
for o := it.Next(); o != nil; o = it.Next() {
@@ -59,7 +60,7 @@ func (s *Pod) Lint(ctx context.Context) error {
s.InitOutcome(fqn)
defer s.CloseOutcome(ctx, fqn, nil)
- ctx = internal.WithSpec(ctx, SpecFor(fqn, po))
+ ctx = internal.WithSpec(ctx, coSpecFor(fqn, po, po.Spec))
s.checkStatus(ctx, po)
s.checkContainerStatus(ctx, fqn, po)
s.checkContainers(ctx, fqn, po)
@@ -69,7 +70,7 @@ func (s *Pod) Lint(ctx context.Context) error {
s.checkPdb(ctx, po.ObjectMeta.Labels)
}
s.checkForMultiplePdbMatches(ctx, po.Namespace, po.ObjectMeta.Labels)
- s.checkSecure(ctx, fqn, po.Spec)
+ s.checkSecure(ctx, fqn, po.Spec, boundSA)
pmx, err := s.db.FindPMX(fqn)
if err != nil {
@@ -96,6 +97,10 @@ func (s *Pod) checkNPs(ctx context.Context, pod *v1.Pod) {
txn, it := s.db.MustITForNS(internal.Glossary[internal.NP], pod.Namespace)
defer txn.Abort()
+ const (
+ in = 0
+ out = 1
+ )
matches := [2]int{}
for o := it.Next(); o != nil; o = it.Next() {
np := o.(*netv1.NetworkPolicy)
@@ -103,34 +108,41 @@ func (s *Pod) checkNPs(ctx context.Context, pod *v1.Pod) {
return
}
if isDenyAllIngress(&np.Spec) || isAllowAllIngress(&np.Spec) {
- matches[0]++
+ matches[in]++
if s.checkEgresses(ctx, pod, np.Spec.Egress) {
- matches[1]++
+ matches[out]++
}
continue
}
if isDenyAllEgress(&np.Spec) || isAllowAllEgress(&np.Spec) {
- matches[1]++
+ matches[out]++
if s.checkIngresses(ctx, pod, np.Spec.Ingress) {
- matches[0]++
+ matches[in]++
}
continue
}
if labelsMatch(&np.Spec.PodSelector, pod.Labels) {
+ if polInclude(np.Spec.PolicyTypes, dirIn) {
+ matches[in]++
+ }
+ if polInclude(np.Spec.PolicyTypes, dirOut) {
+ matches[out]++
+ }
+ } else {
if s.checkIngresses(ctx, pod, np.Spec.Ingress) {
- matches[0]++
+ matches[out]++
}
if s.checkEgresses(ctx, pod, np.Spec.Egress) {
- matches[1]++
+ matches[in]++
}
}
}
- if matches[0] == 0 {
- s.AddCode(ctx, 1204, dirIn)
+ if matches[in] == 0 {
+ s.AddCode(ctx, 1204, ingress)
}
- if matches[1] == 0 {
- s.AddCode(ctx, 1204, dirOut)
+ if matches[out] == 0 {
+ s.AddCode(ctx, 1204, egress)
}
}
@@ -285,17 +297,21 @@ func (s *Pod) checkUtilization(ctx context.Context, fqn string, po *v1.Pod, cmx
}
}
-func (s *Pod) checkSecure(ctx context.Context, fqn string, spec v1.PodSpec) {
- if err := s.checkSA(ctx, fqn, spec); err != nil {
+func (s *Pod) checkSecure(ctx context.Context, fqn string, spec v1.PodSpec, boundSA bool) {
+ if err := s.checkSA(ctx, fqn, spec, boundSA); err != nil {
s.AddErr(ctx, err)
}
s.checkSecContext(ctx, fqn, spec)
}
-func (s *Pod) checkSA(ctx context.Context, fqn string, spec v1.PodSpec) error {
+func (s *Pod) checkSA(ctx context.Context, fqn string, spec v1.PodSpec, boundSA bool) error {
ns, _ := namespaced(fqn)
if spec.ServiceAccountName == "default" {
- s.AddCode(ctx, 300)
+ if boundSA {
+ s.AddCode(ctx, 308)
+ } else {
+ s.AddCode(ctx, 300)
+ }
}
txn := s.db.Txn(false)
diff --git a/internal/lint/pod_test.go b/internal/lint/pod_test.go
index 3c0d0d0f..c53f54ed 100644
--- a/internal/lint/pod_test.go
+++ b/internal/lint/pod_test.go
@@ -5,12 +5,18 @@ package lint
import (
"context"
+ "os"
+ "path/filepath"
"testing"
"github.com/derailed/popeye/internal"
"github.com/derailed/popeye/internal/db"
+ "github.com/derailed/popeye/internal/issues"
"github.com/derailed/popeye/internal/test"
+ "github.com/derailed/popeye/pkg/config"
+ "github.com/derailed/popeye/pkg/config/json"
"github.com/stretchr/testify/assert"
+ "gopkg.in/yaml.v2"
v1 "k8s.io/api/core/v1"
netv1 "k8s.io/api/networking/v1"
polv1 "k8s.io/api/policy/v1"
@@ -37,7 +43,7 @@ func TestPodNPLint(t *testing.T) {
ii := po.Outcome()["ns1/p1"]
assert.Equal(t, 1, len(ii))
- assert.Equal(t, `[POP-1204] Pod Egress is not secured by a network policy`, ii[0].Message)
+ assert.Equal(t, `[POP-1204] Pod egress is not secured by a network policy`, ii[0].Message)
ii = po.Outcome()["ns2/p2"]
assert.Equal(t, 0, len(ii))
@@ -106,7 +112,7 @@ func TestPodCheckSecure(t *testing.T) {
u := uu[k]
t.Run(k, func(t *testing.T) {
p := NewPod(test.MakeCollector(t), dba)
- p.checkSecure(ctx, "default/p1", u.pod.Spec)
+ p.checkSecure(ctx, "default/p1", u.pod.Spec, true)
assert.Equal(t, u.issues, len(p.Outcome()["default/p1"]))
})
}
@@ -136,8 +142,8 @@ func TestPodLint(t *testing.T) {
assert.Equal(t, 6, len(ii))
assert.Equal(t, `[POP-207] Pod is in an unhappy phase ()`, ii[0].Message)
assert.Equal(t, `[POP-208] Unmanaged pod detected. Best to use a controller`, ii[1].Message)
- assert.Equal(t, `[POP-1204] Pod Ingress is not secured by a network policy`, ii[2].Message)
- assert.Equal(t, `[POP-1204] Pod Egress is not secured by a network policy`, ii[3].Message)
+ assert.Equal(t, `[POP-1204] Pod ingress is not secured by a network policy`, ii[2].Message)
+ assert.Equal(t, `[POP-1204] Pod egress is not secured by a network policy`, ii[3].Message)
assert.Equal(t, `[POP-206] Pod has no associated PodDisruptionBudget`, ii[4].Message)
assert.Equal(t, `[POP-301] Connects to API Server? ServiceAccount token is mounted`, ii[5].Message)
@@ -145,8 +151,8 @@ func TestPodLint(t *testing.T) {
assert.Equal(t, 6, len(ii))
assert.Equal(t, `[POP-105] Liveness uses a port#, prefer a named port`, ii[0].Message)
assert.Equal(t, `[POP-105] Readiness uses a port#, prefer a named port`, ii[1].Message)
- assert.Equal(t, `[POP-1204] Pod Ingress is not secured by a network policy`, ii[2].Message)
- assert.Equal(t, `[POP-1204] Pod Egress is not secured by a network policy`, ii[3].Message)
+ assert.Equal(t, `[POP-1204] Pod ingress is not secured by a network policy`, ii[2].Message)
+ assert.Equal(t, `[POP-1204] Pod egress is not secured by a network policy`, ii[3].Message)
assert.Equal(t, `[POP-301] Connects to API Server? ServiceAccount token is mounted`, ii[4].Message)
assert.Equal(t, `[POP-109] CPU Current/Request (2000m/1000m) reached user 80% threshold (200%)`, ii[5].Message)
@@ -163,8 +169,8 @@ func TestPodLint(t *testing.T) {
assert.Equal(t, `[POP-113] Container image "zorg:latest" is not hosted on an allowed docker registry`, ii[8].Message)
assert.Equal(t, `[POP-107] No resource limits defined`, ii[9].Message)
assert.Equal(t, `[POP-208] Unmanaged pod detected. Best to use a controller`, ii[10].Message)
- assert.Equal(t, `[POP-1204] Pod Ingress is not secured by a network policy`, ii[11].Message)
- assert.Equal(t, `[POP-1204] Pod Egress is not secured by a network policy`, ii[12].Message)
+ assert.Equal(t, `[POP-1204] Pod ingress is not secured by a network policy`, ii[11].Message)
+ assert.Equal(t, `[POP-1204] Pod egress is not secured by a network policy`, ii[12].Message)
assert.Equal(t, `[POP-300] Uses "default" ServiceAccount`, ii[13].Message)
assert.Equal(t, `[POP-301] Connects to API Server? ServiceAccount token is mounted`, ii[14].Message)
@@ -173,12 +179,50 @@ func TestPodLint(t *testing.T) {
assert.Equal(t, `[POP-113] Container image "blee:v1.2" is not hosted on an allowed docker registry`, ii[0].Message)
assert.Equal(t, `[POP-106] No resources requests/limits defined`, ii[1].Message)
assert.Equal(t, `[POP-102] No probes defined`, ii[2].Message)
- assert.Equal(t, `[POP-1204] Pod Ingress is not secured by a network policy`, ii[3].Message)
- assert.Equal(t, `[POP-1204] Pod Egress is not secured by a network policy`, ii[4].Message)
+ assert.Equal(t, `[POP-1204] Pod ingress is not secured by a network policy`, ii[3].Message)
+ assert.Equal(t, `[POP-1204] Pod egress is not secured by a network policy`, ii[4].Message)
assert.Equal(t, `[POP-209] Pod is managed by multiple PodDisruptionBudgets (pdb4, pdb4-1)`, ii[5].Message)
assert.Equal(t, `[POP-301] Connects to API Server? ServiceAccount token is mounted`, ii[6].Message)
}
+func TestPodLintExcludes(t *testing.T) {
+ dba, err := test.NewTestDB()
+ assert.NoError(t, err)
+ l := db.NewLoader(dba)
+
+ ctx := test.MakeCtx(t)
+ assert.NoError(t, test.LoadDB[*v1.Pod](ctx, l.DB, "core/pod/2.yaml", internal.Glossary[internal.PO]))
+ assert.NoError(t, test.LoadDB[*v1.ServiceAccount](ctx, l.DB, "core/sa/1.yaml", internal.Glossary[internal.SA]))
+ assert.NoError(t, test.LoadDB[*polv1.PodDisruptionBudget](ctx, l.DB, "pol/pdb/1.yaml", internal.Glossary[internal.PDB]))
+ assert.NoError(t, test.LoadDB[*netv1.NetworkPolicy](ctx, l.DB, "net/np/1.yaml", internal.Glossary[internal.NP]))
+ assert.NoError(t, test.LoadDB[*mv1beta1.PodMetrics](ctx, l.DB, "mx/pod/1.yaml", internal.Glossary[internal.PMX]))
+
+ bb, err := os.ReadFile(filepath.Join("testdata", "config", "1.yaml"))
+ assert.NoError(t, err)
+ assert.NoError(t, json.NewValidator().Validate(json.SpinachSchema, bb))
+ var cfg config.Config
+ assert.NoError(t, yaml.Unmarshal(bb, &cfg))
+
+ codes, err := issues.LoadCodes()
+ assert.NoError(t, err)
+ cc := issues.NewCollector(codes, &cfg)
+ po := NewPod(cc, dba)
+ po.Collector.Config.Registries = []string{"dorker.io"}
+ assert.Nil(t, po.Lint(test.MakeContext("v1/pods", "pods")))
+ assert.Equal(t, 5, len(po.Outcome()))
+
+ ii := po.Outcome()["default/p4"]
+
+ assert.Equal(t, 7, len(ii))
+ assert.Equal(t, `[POP-101] Image tagged "latest" in use`, ii[0].Message)
+ assert.Equal(t, `[POP-107] No resource limits defined`, ii[1].Message)
+ assert.Equal(t, `[POP-208] Unmanaged pod detected. Best to use a controller`, ii[2].Message)
+ assert.Equal(t, `[POP-1204] Pod ingress is not secured by a network policy`, ii[3].Message)
+ assert.Equal(t, `[POP-1204] Pod egress is not secured by a network policy`, ii[4].Message)
+ assert.Equal(t, `[POP-300] Uses "default" ServiceAccount`, ii[5].Message)
+ assert.Equal(t, `[POP-301] Connects to API Server? ServiceAccount token is mounted`, ii[6].Message)
+}
+
// ----------------------------------------------------------------------------
// Helpers...
diff --git a/internal/lint/rb.go b/internal/lint/rb.go
index 28492a34..b13d46ec 100644
--- a/internal/lint/rb.go
+++ b/internal/lint/rb.go
@@ -60,3 +60,31 @@ func (r *RoleBinding) checkInUse(ctx context.Context) {
}
}
}
+
+func boundDefaultSA(db *db.DB) bool {
+ txn, it := db.MustITFor(internal.Glossary[internal.ROB])
+ defer txn.Abort()
+ for o := it.Next(); o != nil; o = it.Next() {
+ rb := o.(*rbacv1.RoleBinding)
+ if rb.Namespace != client.DefaultNamespace || rb.RoleRef.Kind == "ClusterRole" {
+ continue
+ }
+ if rb.RoleRef.APIGroup == "" && rb.RoleRef.Kind == "ServiceAccount" && rb.RoleRef.Name == "default" {
+ return true
+ }
+ }
+
+ txn, it = db.MustITFor(internal.Glossary[internal.CRB])
+ defer txn.Abort()
+ for o := it.Next(); o != nil; o = it.Next() {
+ rb := o.(*rbacv1.ClusterRoleBinding)
+ if rb.RoleRef.Kind == "ClusterRole" {
+ continue
+ }
+ if rb.RoleRef.APIGroup == "" && rb.RoleRef.Kind == "ServiceAccount" && rb.RoleRef.Name == "default" {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/internal/lint/rb_test.go b/internal/lint/rb_test.go
index 086a405b..1bd43960 100644
--- a/internal/lint/rb_test.go
+++ b/internal/lint/rb_test.go
@@ -44,3 +44,47 @@ func TestRBLint(t *testing.T) {
assert.Equal(t, `[POP-1300] References a ClusterRole (cr-bozo) which does not exist`, ii[0].Message)
assert.Equal(t, rules.WarnLevel, ii[0].Level)
}
+
+func TestRB_boundDefaultSA(t *testing.T) {
+ uu := map[string]struct {
+ roPath, robPath string
+ crPath, crbPath string
+ e bool
+ }{
+ "happy": {
+ roPath: "auth/ro/1.yaml",
+ robPath: "auth/rob/1.yaml",
+ crPath: "auth/cr/1.yaml",
+ crbPath: "auth/crb/1.yaml",
+ },
+ "role-bound": {
+ roPath: "auth/ro/1.yaml",
+ robPath: "auth/rob/2.yaml",
+ crPath: "auth/cr/1.yaml",
+ crbPath: "auth/crb/1.yaml",
+ },
+ "cluster-role-bound": {
+ roPath: "auth/ro/1.yaml",
+ robPath: "auth/rob/1.yaml",
+ crPath: "auth/cr/1.yaml",
+ crbPath: "auth/crb/2.yaml",
+ },
+ }
+
+ for k, u := range uu {
+ t.Run(k, func(t *testing.T) {
+ dba, err := test.NewTestDB()
+ assert.NoError(t, err)
+ l := db.NewLoader(dba)
+
+ ctx := test.MakeCtx(t)
+ assert.NoError(t, test.LoadDB[*rbacv1.RoleBinding](ctx, l.DB, u.robPath, internal.Glossary[internal.ROB]))
+ assert.NoError(t, test.LoadDB[*rbacv1.Role](ctx, l.DB, u.roPath, internal.Glossary[internal.RO]))
+ assert.NoError(t, test.LoadDB[*rbacv1.ClusterRole](ctx, l.DB, u.crPath, internal.Glossary[internal.CR]))
+ assert.NoError(t, test.LoadDB[*rbacv1.ClusterRoleBinding](ctx, l.DB, u.crbPath, internal.Glossary[internal.CRB]))
+ assert.NoError(t, test.LoadDB[*v1.ServiceAccount](ctx, l.DB, "core/sa/1.yaml", internal.Glossary[internal.SA]))
+
+ assert.Equal(t, u.e, boundDefaultSA(dba))
+ })
+ }
+}
diff --git a/internal/lint/rs.go b/internal/lint/rs.go
index 2e9763bd..dca34c11 100644
--- a/internal/lint/rs.go
+++ b/internal/lint/rs.go
@@ -36,7 +36,7 @@ func (s *ReplicaSet) Lint(ctx context.Context) error {
rs := o.(*appsv1.ReplicaSet)
fqn := client.FQN(rs.Namespace, rs.Name)
s.InitOutcome(fqn)
- ctx = internal.WithSpec(ctx, SpecFor(fqn, rs))
+ ctx = internal.WithSpec(ctx, coSpecFor(fqn, rs, rs.Spec.Template.Spec))
s.checkHealth(ctx, rs)
}
diff --git a/internal/lint/sts.go b/internal/lint/sts.go
index 6d94ea5d..cf34cdf1 100644
--- a/internal/lint/sts.go
+++ b/internal/lint/sts.go
@@ -48,7 +48,7 @@ func (s *StatefulSet) Lint(ctx context.Context) error {
sts := o.(*appsv1.StatefulSet)
fqn := client.FQN(sts.Namespace, sts.Name)
s.InitOutcome(fqn)
- ctx = internal.WithSpec(ctx, SpecFor(fqn, sts))
+ ctx = internal.WithSpec(ctx, coSpecFor(fqn, sts, sts.Spec.Template.Spec))
s.checkStatefulSet(ctx, sts)
s.checkContainers(ctx, fqn, sts)
diff --git a/internal/lint/testdata/auth/crb/2.yaml b/internal/lint/testdata/auth/crb/2.yaml
new file mode 100644
index 00000000..df2abcf3
--- /dev/null
+++ b/internal/lint/testdata/auth/crb/2.yaml
@@ -0,0 +1,43 @@
+---
+apiVersion: v1
+kind: List
+items:
+ - apiVersion: rbac.authorization.k8s.io/v1
+ kind: ClusterRoleBinding
+ metadata:
+ name: crb1
+ subjects:
+ - kind: ServiceAccount
+ name: default
+ namespace: default
+ apiGroup: rbac.authorization.k8s.io
+ roleRef:
+ kind: ClusterRole
+ name: cr1
+ apiGroup: rbac.authorization.k8s.io
+ - apiVersion: rbac.authorization.k8s.io/v1
+ kind: ClusterRoleBinding
+ metadata:
+ name: crb2
+ subjects:
+ - kind: ServiceAccount
+ name: sa2
+ namespace: default
+ apiGroup: rbac.authorization.k8s.io
+ roleRef:
+ kind: ClusterRole
+ name: cr-bozo
+ apiGroup: rbac.authorization.k8s.io
+ - apiVersion: rbac.authorization.k8s.io/v1
+ kind: ClusterRoleBinding
+ metadata:
+ name: crb3
+ subjects:
+ - kind: ServiceAccount
+ name: sa-bozo
+ namespace: default
+ apiGroup: rbac.authorization.k8s.io
+ roleRef:
+ kind: Role
+ name: r-bozo
+ apiGroup: rbac.authorization.k8s.io
\ No newline at end of file
diff --git a/internal/lint/testdata/auth/rob/2.yaml b/internal/lint/testdata/auth/rob/2.yaml
new file mode 100644
index 00000000..4663a1cf
--- /dev/null
+++ b/internal/lint/testdata/auth/rob/2.yaml
@@ -0,0 +1,45 @@
+---
+apiVersion: v1
+kind: List
+items:
+ - apiVersion: rbac.authorization.k8s.io/v1
+ kind: RoleBinding
+ metadata:
+ name: rb1
+ namespace: default
+ subjects:
+ - kind: ServiceAccount
+ name: default
+ apiGroup: rbac.authorization.k8s.io
+ roleRef:
+ kind: Role
+ name: r1
+ apiGroup: rbac.authorization.k8s.io
+
+ - apiVersion: rbac.authorization.k8s.io/v1
+ kind: RoleBinding
+ metadata:
+ name: rb2
+ namespace: default
+ subjects:
+ - kind: ServiceAccount
+ name: sa-bozo
+ apiGroup: rbac.authorization.k8s.io
+ roleRef:
+ kind: Role
+ name: r-bozo
+ apiGroup: rbac.authorization.k8s.io
+
+ - apiVersion: rbac.authorization.k8s.io/v1
+ kind: RoleBinding
+ metadata:
+ name: rb3
+ namespace: default
+ subjects:
+ - kind: ServiceAccount
+ name: sa-bozo
+ apiGroup: rbac.authorization.k8s.io
+ roleRef:
+ kind: ClusterRole
+ name: cr-bozo
+ apiGroup: rbac.authorization.k8s.io
\ No newline at end of file
diff --git a/internal/lint/testdata/config/1.yaml b/internal/lint/testdata/config/1.yaml
new file mode 100644
index 00000000..d36ab81b
--- /dev/null
+++ b/internal/lint/testdata/config/1.yaml
@@ -0,0 +1,12 @@
+popeye:
+ excludes:
+ linters:
+ pods:
+ instances:
+ - codes:
+ - 100
+ - 106
+ - 113
+ - 204
+ containers:
+ - "rx:c2*"
\ No newline at end of file
diff --git a/internal/lint/testdata/core/pod/3.yaml b/internal/lint/testdata/core/pod/3.yaml
index 49b1419f..86e6d5e6 100644
--- a/internal/lint/testdata/core/pod/3.yaml
+++ b/internal/lint/testdata/core/pod/3.yaml
@@ -1,7 +1,8 @@
---
apiVersion: v1
-kind: List
+kind: PodList
items:
+
- apiVersion: v1
kind: Pod
metadata:
@@ -62,6 +63,7 @@ items:
phase: Running
podIPs:
- ip: 172.1.0.3
+
- apiVersion: v1
kind: Pod
metadata:
diff --git a/internal/lint/testdata/net/np/1.yaml b/internal/lint/testdata/net/np/1.yaml
index e25a10c4..e0c05859 100644
--- a/internal/lint/testdata/net/np/1.yaml
+++ b/internal/lint/testdata/net/np/1.yaml
@@ -1,6 +1,7 @@
-apiVersion: v1
-kind: List
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicyList
items:
+
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
@@ -35,12 +36,14 @@ items:
ports:
- protocol: TCP
port: 5978
+
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: np2
namespace: default
spec:
+ podSelector: {}
ingress:
- from:
- ipBlock:
@@ -66,6 +69,7 @@ items:
ports:
- protocol: TCP
port: 5978
+
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
diff --git a/internal/lint/testdata/net/np/2.yaml b/internal/lint/testdata/net/np/2.yaml
index 0d088d36..7ecea392 100644
--- a/internal/lint/testdata/net/np/2.yaml
+++ b/internal/lint/testdata/net/np/2.yaml
@@ -1,6 +1,7 @@
-apiVersion: v1
-kind: List
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicyList
items:
+
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
@@ -29,6 +30,7 @@ items:
podSelector: {}
policyTypes:
- Egress
+
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
@@ -54,6 +56,7 @@ items:
- {}
policyTypes:
- Ingress
+
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
@@ -82,6 +85,7 @@ items:
policyTypes:
- Ingress
- Egress
+
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
diff --git a/internal/lint/testdata/net/np/3.yaml b/internal/lint/testdata/net/np/3.yaml
index 3ae4bad3..10548950 100644
--- a/internal/lint/testdata/net/np/3.yaml
+++ b/internal/lint/testdata/net/np/3.yaml
@@ -1,6 +1,7 @@
-apiVersion: v1
-kind: List
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicyList
items:
+
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
@@ -10,15 +11,14 @@ items:
podSelector: {}
policyTypes:
- Ingress
+
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
namespace: ns2
spec:
- # podSelector:
- # matchLabels:
- # app: p2
+ podSelector: {}
ingress:
- from:
- namespaceSelector:
diff --git a/internal/lint/testdata/net/np/blee.yml b/internal/lint/testdata/net/np/blee.yml
new file mode 100644
index 00000000..86a03d3d
--- /dev/null
+++ b/internal/lint/testdata/net/np/blee.yml
@@ -0,0 +1,34 @@
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ name: np1
+ namespace: default
+spec:
+ podSelector:
+ matchLabels:
+ app: p1
+ policyTypes:
+ - Ingress
+ - Egress
+ ingress:
+ - from:
+ - ipBlock:
+ cidr: 172.1.0.0/16
+ except:
+ - 172.1.0.0/24
+ - namespaceSelector:
+ matchLabels:
+ ns: default
+ podSelector:
+ matchLabels:
+ app: p1
+ ports:
+ - protocol: TCP
+ port: 6379
+ egress:
+ - to:
+ - ipBlock:
+ cidr: 172.1.0.0/16
+ ports:
+ - protocol: TCP
+ port: 5978
diff --git a/internal/scrub/cjob.go b/internal/scrub/cjob.go
index 453d957c..37ab1436 100644
--- a/internal/scrub/cjob.go
+++ b/internal/scrub/cjob.go
@@ -22,7 +22,7 @@ type CronJob struct {
}
// NewCronJob return a new instance.
-func NewCronJob(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewCronJob(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &CronJob{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/cm.go b/internal/scrub/cm.go
index fca6edec..e08f192f 100644
--- a/internal/scrub/cm.go
+++ b/internal/scrub/cm.go
@@ -20,7 +20,7 @@ type ConfigMap struct {
}
// NewConfigMap returns a new instance.
-func NewConfigMap(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewConfigMap(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &ConfigMap{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/cr.go b/internal/scrub/cr.go
index 7b2de24e..dba2179e 100644
--- a/internal/scrub/cr.go
+++ b/internal/scrub/cr.go
@@ -21,7 +21,7 @@ type ClusterRole struct {
}
// NewClusterRole returns a new instance.
-func NewClusterRole(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewClusterRole(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &ClusterRole{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/crb.go b/internal/scrub/crb.go
index c0453463..af07f26c 100644
--- a/internal/scrub/crb.go
+++ b/internal/scrub/crb.go
@@ -21,7 +21,7 @@ type ClusterRoleBinding struct {
}
// NewClusterRoleBinding returns a new instance.
-func NewClusterRoleBinding(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewClusterRoleBinding(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &ClusterRoleBinding{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/dp.go b/internal/scrub/dp.go
index f31d2094..ce8ea36a 100644
--- a/internal/scrub/dp.go
+++ b/internal/scrub/dp.go
@@ -22,7 +22,7 @@ type Deployment struct {
}
// NewDeployment returns a new instance.
-func NewDeployment(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewDeployment(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &Deployment{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/ds.go b/internal/scrub/ds.go
index 1237ee26..e00a4ebd 100644
--- a/internal/scrub/ds.go
+++ b/internal/scrub/ds.go
@@ -22,7 +22,7 @@ type DaemonSet struct {
}
// NewDaemonSet return a new instance.
-func NewDaemonSet(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewDaemonSet(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &DaemonSet{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/gw-route.go b/internal/scrub/gw-route.go
index b2d82cb6..910bd807 100644
--- a/internal/scrub/gw-route.go
+++ b/internal/scrub/gw-route.go
@@ -20,7 +20,7 @@ type HTTPRoute struct {
}
// NewHTTPRoute return a new instance.
-func NewHTTPRoute(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewHTTPRoute(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &HTTPRoute{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/gw.go b/internal/scrub/gw.go
index 12882cab..d1a81f31 100644
--- a/internal/scrub/gw.go
+++ b/internal/scrub/gw.go
@@ -20,7 +20,7 @@ type Gateway struct {
}
// NewGateway return a new instance.
-func NewGateway(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewGateway(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &Gateway{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/gwc.go b/internal/scrub/gwc.go
index f1773695..3a187f8f 100644
--- a/internal/scrub/gwc.go
+++ b/internal/scrub/gwc.go
@@ -20,7 +20,7 @@ type GatewayClass struct {
}
// NewGatewayClass return a new instance.
-func NewGatewayClass(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewGatewayClass(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &GatewayClass{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/hpa.go b/internal/scrub/hpa.go
index f37242be..c289c588 100644
--- a/internal/scrub/hpa.go
+++ b/internal/scrub/hpa.go
@@ -23,7 +23,7 @@ type HorizontalPodAutoscaler struct {
}
// NewHorizontalPodAutoscaler returns a new instance.
-func NewHorizontalPodAutoscaler(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewHorizontalPodAutoscaler(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &HorizontalPodAutoscaler{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/ing.go b/internal/scrub/ing.go
index 599b0d46..922f2114 100644
--- a/internal/scrub/ing.go
+++ b/internal/scrub/ing.go
@@ -21,7 +21,7 @@ type Ingress struct {
}
// NewIngress return a new instance.
-func NewIngress(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewIngress(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &Ingress{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/job.go b/internal/scrub/job.go
index 880b9937..c841484e 100644
--- a/internal/scrub/job.go
+++ b/internal/scrub/job.go
@@ -22,7 +22,7 @@ type Job struct {
}
// NewJob return a new instance.
-func NewJob(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewJob(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &Job{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/no.go b/internal/scrub/no.go
index 9bd9d8c6..bd12a78a 100644
--- a/internal/scrub/no.go
+++ b/internal/scrub/no.go
@@ -21,7 +21,7 @@ type Node struct {
}
// NewNode return a new instance.
-func NewNode(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewNode(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &Node{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/np.go b/internal/scrub/np.go
index 53191a8d..9ac08106 100644
--- a/internal/scrub/np.go
+++ b/internal/scrub/np.go
@@ -21,7 +21,7 @@ type NetworkPolicy struct {
}
// NewNetworkPolicy return a new instance.
-func NewNetworkPolicy(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewNetworkPolicy(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &NetworkPolicy{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/ns.go b/internal/scrub/ns.go
index 60a7650f..68ea1bd6 100644
--- a/internal/scrub/ns.go
+++ b/internal/scrub/ns.go
@@ -20,7 +20,7 @@ type Namespace struct {
}
// NewNamespace returns a new instance.
-func NewNamespace(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewNamespace(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &Namespace{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/pdb.go b/internal/scrub/pdb.go
index c94a3835..08ca82d5 100644
--- a/internal/scrub/pdb.go
+++ b/internal/scrub/pdb.go
@@ -21,7 +21,7 @@ type PodDisruptionBudget struct {
}
// NewPodDisruptionBudget return a new PodDisruptionBudget scruber.
-func NewPodDisruptionBudget(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewPodDisruptionBudget(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &PodDisruptionBudget{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/pod.go b/internal/scrub/pod.go
index 03586930..1f4685d9 100644
--- a/internal/scrub/pod.go
+++ b/internal/scrub/pod.go
@@ -23,7 +23,7 @@ type Pod struct {
}
// NewPod return a new instance.
-func NewPod(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewPod(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &Pod{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/pv.go b/internal/scrub/pv.go
index 047eabe7..17c90dd6 100644
--- a/internal/scrub/pv.go
+++ b/internal/scrub/pv.go
@@ -20,7 +20,7 @@ type PersistentVolume struct {
}
// NewPersistentVolume return a new instance.
-func NewPersistentVolume(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewPersistentVolume(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &PersistentVolume{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/pvc.go b/internal/scrub/pvc.go
index 5b39362d..86fc6151 100644
--- a/internal/scrub/pvc.go
+++ b/internal/scrub/pvc.go
@@ -20,7 +20,7 @@ type PersistentVolumeClaim struct {
}
// NewPersistentVolumeClaim returns a new instance.
-func NewPersistentVolumeClaim(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewPersistentVolumeClaim(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &PersistentVolumeClaim{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/rb.go b/internal/scrub/rb.go
index acf8cfa2..681d5506 100644
--- a/internal/scrub/rb.go
+++ b/internal/scrub/rb.go
@@ -21,7 +21,7 @@ type RoleBinding struct {
}
// NewRoleBinding returns a new instance.
-func NewRoleBinding(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewRoleBinding(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &RoleBinding{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/ro.go b/internal/scrub/ro.go
index 103bd571..727fc7b9 100644
--- a/internal/scrub/ro.go
+++ b/internal/scrub/ro.go
@@ -20,7 +20,7 @@ type Role struct {
}
// NewRole returns a new instance.
-func NewRole(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewRole(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &Role{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/rs.go b/internal/scrub/rs.go
index 495113b4..fe9d4d15 100644
--- a/internal/scrub/rs.go
+++ b/internal/scrub/rs.go
@@ -21,7 +21,7 @@ type ReplicaSet struct {
}
// NewReplicaSet returns a new instance.
-func NewReplicaSet(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewReplicaSet(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &ReplicaSet{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/sa.go b/internal/scrub/sa.go
index 6b0daf7f..069a0bd0 100644
--- a/internal/scrub/sa.go
+++ b/internal/scrub/sa.go
@@ -22,7 +22,7 @@ type ServiceAccount struct {
}
// NewServiceAccount returns a new instance.
-func NewServiceAccount(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewServiceAccount(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &ServiceAccount{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/sec.go b/internal/scrub/sec.go
index dab300fb..18b49482 100644
--- a/internal/scrub/sec.go
+++ b/internal/scrub/sec.go
@@ -21,7 +21,7 @@ type Secret struct {
}
// NewSecret return a new Secret scruber.
-func NewSecret(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewSecret(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &Secret{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/sts.go b/internal/scrub/sts.go
index 7b90b13f..da8a3941 100644
--- a/internal/scrub/sts.go
+++ b/internal/scrub/sts.go
@@ -22,7 +22,7 @@ type StatefulSet struct {
}
// NewStatefulSet return a new StatefulSet scruber.
-func NewStatefulSet(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewStatefulSet(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &StatefulSet{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/internal/scrub/svc.go b/internal/scrub/svc.go
index fde05b49..e0b92c85 100644
--- a/internal/scrub/svc.go
+++ b/internal/scrub/svc.go
@@ -20,7 +20,7 @@ type Service struct {
}
// NewService return a new instance.
-func NewService(ctx context.Context, c *Cache, codes *issues.Codes) Linter {
+func NewService(_ context.Context, c *Cache, codes *issues.Codes) Linter {
return &Service{
Collector: issues.NewCollector(codes, c.Config),
Cache: c,
diff --git a/main.go b/main.go
index 31fe7521..56646c6c 100644
--- a/main.go
+++ b/main.go
@@ -4,26 +4,10 @@
package main
import (
- "fmt"
- "os"
-
"github.com/derailed/popeye/cmd"
- "github.com/derailed/popeye/pkg"
- "github.com/rs/zerolog"
- "github.com/rs/zerolog/log"
_ "k8s.io/client-go/plugin/pkg/client/auth"
)
-func init() {
- mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
- if file, err := os.OpenFile(pkg.LogFile, mod, 0644); err == nil {
- log.Logger = log.Output(zerolog.ConsoleWriter{Out: file})
- } else {
- fmt.Printf("Unable to create Popeye log file %v. Exiting...", err)
- os.Exit(1)
- }
-}
-
func main() {
cmd.Execute()
}
diff --git a/pkg/config/flags.go b/pkg/config/flags.go
index 9212be11..7fbabfc5 100644
--- a/pkg/config/flags.go
+++ b/pkg/config/flags.go
@@ -42,6 +42,8 @@ type Flags struct {
ActiveNamespace *string
ForceExitZero *bool
MinScore *int
+ LogLevel *int
+ LogFile *string
}
// NewFlags returns new configuration flags.
@@ -62,15 +64,17 @@ func NewFlags() *Flags {
PushGateway: newPushGateway(),
ForceExitZero: boolPtr(false),
MinScore: intPtr(0),
+ LogLevel: intPtr(0),
+ LogFile: strPtr(""),
}
}
func (f *Flags) Validate() error {
if !IsBoolSet(f.Save) && IsStrSet(f.OutputFile) {
- return errors.New("'--save' must be used in conjunction with 'output-file'.")
+ return errors.New("'--save' must be used in conjunction with 'output-file'")
}
if IsBoolSet(f.Save) && IsStrSet(f.S3.Bucket) {
- return errors.New("'--save' cannot be used in conjunction with 's3-bucket'.")
+ return errors.New("'--save' cannot be used in conjunction with 's3-bucket'")
}
if !in(outputs, f.Output) {
diff --git a/pkg/popeye.go b/pkg/popeye.go
index 2fec49ea..8d5e7307 100644
--- a/pkg/popeye.go
+++ b/pkg/popeye.go
@@ -202,11 +202,11 @@ func (p *Popeye) buildCtx(ctx context.Context) context.Context {
}
ns, err := p.client().Config().CurrentNamespaceName()
if err != nil {
+ log.Warn().Msgf("Unable to determine current namespace: %v. Using `default` namespace", err)
ns = client.DefaultNamespace
}
- ctx = context.WithValue(ctx, internal.KeyNamespace, ns)
- return ctx
+ return context.WithValue(ctx, internal.KeyNamespace, ns)
}
func (p *Popeye) validateSpinach(ss scrub.Scrubs) error {
@@ -226,10 +226,6 @@ func (p *Popeye) lint() (int, int, error) {
log.Debug().Msgf("Lint %v", time.Since(t))
}(time.Now())
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
- ctx = p.buildCtx(ctx)
-
codes, err := issues.LoadCodes()
if err != nil {
return 0, 0, err
@@ -250,22 +246,18 @@ func (p *Popeye) lint() (int, int, error) {
if err := p.validateSpinach(scrubers); err != nil {
return 0, 0, err
}
+
+ ctx := p.buildCtx(context.Background())
+ sections, ans := p.config.Sections(), p.client().ActiveNamespace()
for k, fn := range scrubers {
gvr, ok := internal.Glossary[k]
if !ok || gvr == types.BlankGVR {
continue
}
-
- if p.aliases.Exclude(gvr, p.config.Sections()) {
+ if client.IsNamespaced(ans) && p.aliases.IsNamespaced(gvr) || p.aliases.Exclude(gvr, sections) {
continue
}
- // Skip node linter if active namespace is set.
- if gvr == internal.Glossary[internal.NO] && p.client().ActiveNamespace() != client.AllNamespaces {
- continue
- }
-
runners[gvr] = fn(ctx, cache, codes)
-
}
total, errCount := len(runners), 0
@@ -304,12 +296,10 @@ func (p *Popeye) runLinter(ctx context.Context, gvr types.GVR, l scrub.Linter, c
}
}()
- callCtx := ctx
if !p.aliases.IsNamespaced(gvr) {
- callCtx = context.WithValue(ctx, internal.KeyNamespace, client.ClusterScope)
+ ctx = context.WithValue(ctx, internal.KeyNamespace, client.ClusterScope)
}
-
- if err := l.Lint(callCtx); err != nil {
+ if err := l.Lint(ctx); err != nil {
p.builder.AddError(err)
}
o := l.Outcome().Filter(rules.Level(p.config.LintLevel))