Use `FieldSelector` to filter Namespace
parent
0fe6e35217
commit
653f22aac2
|
@ -28,7 +28,7 @@ import (
|
|||
|
||||
// Run applies the filters and runs the resultant check list in parallel
|
||||
func Run(ctx context.Context, client *kube.Client, checkFilter CheckFilter, diagnosticFilter DiagnosticFilter, objectFilter kube.ObjectFilter) (*CheckResult, error) {
|
||||
objects, err := client.FetchObjects(ctx)
|
||||
objects, err := client.FetchObjects(ctx,objectFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -40,8 +40,6 @@ func Run(ctx context.Context, client *kube.Client, checkFilter CheckFilter, diag
|
|||
if len(all) == 0 {
|
||||
return nil, errors.New("No checks to run. Are you sure that you provided the right names for groups and checks?")
|
||||
}
|
||||
objectFilter.Filter(objects)
|
||||
|
||||
var diagnostics []Diagnostic
|
||||
var mu sync.Mutex
|
||||
var g errgroup.Group
|
||||
|
|
|
@ -96,12 +96,12 @@ func main() {
|
|||
Name: "C, ignore-checks",
|
||||
Usage: "run a specific check",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "n, namespaces",
|
||||
cli.StringFlag{
|
||||
Name: "n, namespace",
|
||||
Usage: "run checks in specific namespace",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "N, ignore-namespaces",
|
||||
cli.StringFlag{
|
||||
Name: "N, ignore-namespace",
|
||||
Usage: "run checks not in specific namespace",
|
||||
},
|
||||
cli.StringFlag{
|
||||
|
@ -181,7 +181,7 @@ func runChecks(c *cli.Context) error {
|
|||
|
||||
diagnosticFilter := checks.DiagnosticFilter{Severity: checks.Severity(c.String("level"))}
|
||||
|
||||
objectFilter, err := kube.NewObjectFilter(c.StringSlice("n"), c.StringSlice("N"))
|
||||
objectFilter, err := kube.NewObjectFilter(c.String("n"), c.String("N"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -19,179 +19,34 @@ package kube
|
|||
import (
|
||||
// Load client-go authentication plugins
|
||||
"fmt"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
)
|
||||
// ObjectFilter stores k8s object's fields that needs to be included or excluded while running checks
|
||||
type ObjectFilter struct {
|
||||
IncludeNamespaces []string
|
||||
ExcludeNamespaces []string
|
||||
IncludeNamespace string
|
||||
ExcludeNamespace string
|
||||
}
|
||||
|
||||
|
||||
// NewObjectFilter is a constructor to initialize an instance of ObjectFilter
|
||||
func NewObjectFilter(includeNamespaces, excludeNamespaces []string) (ObjectFilter, error) {
|
||||
if len(includeNamespaces) > 0 && len(excludeNamespaces) > 0 {
|
||||
func NewObjectFilter(includeNamespace, excludeNamespace string) (ObjectFilter, error) {
|
||||
if len(includeNamespace) > 0 && len(excludeNamespace) > 0 {
|
||||
return ObjectFilter{}, fmt.Errorf("cannot specify both include and exclude namespace conditions")
|
||||
}
|
||||
return ObjectFilter{
|
||||
IncludeNamespaces: includeNamespaces,
|
||||
ExcludeNamespaces: excludeNamespaces,
|
||||
IncludeNamespace: includeNamespace,
|
||||
ExcludeNamespace: excludeNamespace,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FilterChecks filters all to return set of checks based on the ObjectFilter
|
||||
func (f ObjectFilter) Filter(objects *Objects) {
|
||||
if len(f.IncludeNamespaces) > 0 {
|
||||
var ps []corev1.Pod
|
||||
for _, p := range objects.Pods.Items {
|
||||
if contains(f.IncludeNamespaces, p.Namespace) {
|
||||
ps = append(ps, p)
|
||||
}
|
||||
}
|
||||
objects.Pods.Items = ps
|
||||
|
||||
var pts []corev1.PodTemplate
|
||||
for _, pt := range objects.PodTemplates.Items {
|
||||
if contains(f.IncludeNamespaces, pt.Namespace) {
|
||||
pts = append(pts, pt)
|
||||
}
|
||||
}
|
||||
objects.PodTemplates.Items = pts
|
||||
|
||||
var pvcs []corev1.PersistentVolumeClaim
|
||||
for _, pvc := range objects.PersistentVolumeClaims.Items {
|
||||
if contains(f.IncludeNamespaces, pvc.Namespace) {
|
||||
pvcs = append(pvcs, pvc)
|
||||
}
|
||||
}
|
||||
objects.PersistentVolumeClaims.Items = pvcs
|
||||
|
||||
var cms []corev1.ConfigMap
|
||||
for _, cm := range objects.ConfigMaps.Items {
|
||||
if contains(f.IncludeNamespaces, cm.Namespace) {
|
||||
cms = append(cms, cm)
|
||||
}
|
||||
}
|
||||
objects.ConfigMaps.Items = cms
|
||||
|
||||
var svcs []corev1.Service
|
||||
for _, svc := range objects.Services.Items {
|
||||
if contains(f.IncludeNamespaces, svc.Namespace) {
|
||||
svcs = append(svcs, svc)
|
||||
}
|
||||
}
|
||||
objects.Services.Items = svcs
|
||||
|
||||
var scrts []corev1.Secret
|
||||
for _, scrt := range objects.Secrets.Items {
|
||||
if contains(f.IncludeNamespaces, scrt.Namespace) {
|
||||
scrts = append(scrts, scrt)
|
||||
}
|
||||
}
|
||||
objects.Secrets.Items = scrts
|
||||
|
||||
var sas []corev1.ServiceAccount
|
||||
for _, sa := range objects.ServiceAccounts.Items {
|
||||
if contains(f.IncludeNamespaces, sa.Namespace) {
|
||||
sas = append(sas, sa)
|
||||
}
|
||||
}
|
||||
objects.ServiceAccounts.Items = sas
|
||||
|
||||
var rqs []corev1.ResourceQuota
|
||||
for _, rq := range objects.ResourceQuotas.Items {
|
||||
if contains(f.IncludeNamespaces, rq.Namespace) {
|
||||
rqs = append(rqs, rq)
|
||||
}
|
||||
}
|
||||
objects.ResourceQuotas.Items = rqs
|
||||
|
||||
var lrs []corev1.LimitRange
|
||||
for _, lr := range objects.LimitRanges.Items {
|
||||
if contains(f.IncludeNamespaces, lr.Namespace) {
|
||||
lrs = append(lrs, lr)
|
||||
}
|
||||
}
|
||||
objects.LimitRanges.Items = lrs
|
||||
|
||||
return
|
||||
// NamespaceOptions returns ListOptions for filtering by namespace
|
||||
func (f ObjectFilter) NamespaceOptions(opts metav1.ListOptions) metav1.ListOptions {
|
||||
if len(f.IncludeNamespace) > 0 {
|
||||
opts.FieldSelector = fields.OneTermEqualSelector("metadata.namespace",f.IncludeNamespace).String()
|
||||
}
|
||||
|
||||
if len(f.ExcludeNamespaces) > 0 {
|
||||
var ps []corev1.Pod
|
||||
for _, p := range objects.Pods.Items {
|
||||
if !contains(f.ExcludeNamespaces, p.Namespace) {
|
||||
ps = append(ps, p)
|
||||
}
|
||||
}
|
||||
objects.Pods.Items = ps
|
||||
|
||||
var pts []corev1.PodTemplate
|
||||
for _, pt := range objects.PodTemplates.Items {
|
||||
if !contains(f.ExcludeNamespaces, pt.Namespace) {
|
||||
pts = append(pts, pt)
|
||||
}
|
||||
}
|
||||
objects.PodTemplates.Items = pts
|
||||
|
||||
var pvcs []corev1.PersistentVolumeClaim
|
||||
for _, pvc := range objects.PersistentVolumeClaims.Items {
|
||||
if !contains(f.ExcludeNamespaces, pvc.Namespace) {
|
||||
pvcs = append(pvcs, pvc)
|
||||
}
|
||||
}
|
||||
objects.PersistentVolumeClaims.Items = pvcs
|
||||
|
||||
var cms []corev1.ConfigMap
|
||||
for _, cm := range objects.ConfigMaps.Items {
|
||||
if !contains(f.ExcludeNamespaces, cm.Namespace) {
|
||||
cms = append(cms, cm)
|
||||
}
|
||||
}
|
||||
objects.ConfigMaps.Items = cms
|
||||
|
||||
var svcs []corev1.Service
|
||||
for _, svc := range objects.Services.Items {
|
||||
if !contains(f.ExcludeNamespaces, svc.Namespace) {
|
||||
svcs = append(svcs, svc)
|
||||
}
|
||||
}
|
||||
objects.Services.Items = svcs
|
||||
|
||||
var scrts []corev1.Secret
|
||||
for _, scrt := range objects.Secrets.Items {
|
||||
if !contains(f.ExcludeNamespaces, scrt.Namespace) {
|
||||
scrts = append(scrts, scrt)
|
||||
}
|
||||
}
|
||||
objects.Secrets.Items = scrts
|
||||
|
||||
var sas []corev1.ServiceAccount
|
||||
for _, sa := range objects.ServiceAccounts.Items {
|
||||
if !contains(f.ExcludeNamespaces, sa.Namespace) {
|
||||
sas = append(sas, sa)
|
||||
}
|
||||
}
|
||||
objects.ServiceAccounts.Items = sas
|
||||
|
||||
var rqs []corev1.ResourceQuota
|
||||
for _, rq := range objects.ResourceQuotas.Items {
|
||||
if !contains(f.ExcludeNamespaces, rq.Namespace) {
|
||||
rqs = append(rqs, rq)
|
||||
}
|
||||
}
|
||||
objects.ResourceQuotas.Items = rqs
|
||||
|
||||
var lrs []corev1.LimitRange
|
||||
for _, lr := range objects.LimitRanges.Items {
|
||||
if !contains(f.ExcludeNamespaces, lr.Namespace) {
|
||||
lrs = append(lrs, lr)
|
||||
}
|
||||
}
|
||||
objects.LimitRanges.Items = lrs
|
||||
|
||||
return
|
||||
if len(f.ExcludeNamespace) > 0 {
|
||||
opts.FieldSelector = fields.OneTermNotEqualSelector("metadata.namespace",f.ExcludeNamespace).String()
|
||||
}
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
|
@ -18,103 +18,32 @@ package kube
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNamespaceError(t *testing.T) {
|
||||
_, err := NewObjectFilter([]string{"kube-system"}, []string{"kube-system"})
|
||||
_, err := NewObjectFilter("kube-system", "kube-system")
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, fmt.Errorf("cannot specify both include and exclude namespace conditions"), err)
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
filter, err := NewObjectFilter([]string{"namespace_1"},nil)
|
||||
func TestNamespaceOptions(t *testing.T) {
|
||||
filter, err := NewObjectFilter("namespace-1","")
|
||||
assert.NoError(t, err)
|
||||
objects := namespaceObjects()
|
||||
filter.Filter(objects)
|
||||
assert.Equal(t, namespace1Objects(), objects)
|
||||
assert.Equal(t,
|
||||
metav1.ListOptions{FieldSelector: fields.OneTermEqualSelector("metadata.namespace","namespace-1").String()},
|
||||
filter.NamespaceOptions(metav1.ListOptions{}),
|
||||
)
|
||||
|
||||
filter, err = NewObjectFilter(nil,[]string{"namespace_2"})
|
||||
filter, err = NewObjectFilter("","namespace-2")
|
||||
assert.NoError(t, err)
|
||||
objects = namespaceObjects()
|
||||
filter.Filter(objects)
|
||||
assert.Equal(t, namespace1Objects(), objects)
|
||||
}
|
||||
|
||||
func objects() *Objects {
|
||||
return &Objects{
|
||||
Pods: &corev1.PodList{},
|
||||
PodTemplates: &corev1.PodTemplateList{},
|
||||
PersistentVolumeClaims: &corev1.PersistentVolumeClaimList{},
|
||||
ConfigMaps: &corev1.ConfigMapList{},
|
||||
Services: &corev1.ServiceList{},
|
||||
Secrets: &corev1.SecretList{},
|
||||
ServiceAccounts: &corev1.ServiceAccountList{},
|
||||
ResourceQuotas: &corev1.ResourceQuotaList{},
|
||||
LimitRanges: &corev1.LimitRangeList{},
|
||||
}
|
||||
}
|
||||
|
||||
func namespaceObjects() *Objects {
|
||||
objs := objects()
|
||||
objs.Pods = &corev1.PodList{Items: []corev1.Pod{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "pod_1", Namespace: "namespace_1"}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "pod_2", Namespace: "namespace_2"}},
|
||||
}}
|
||||
objs.PodTemplates = &corev1.PodTemplateList{Items: []corev1.PodTemplate{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "template_1", Namespace: "namespace_1"}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "template_2", Namespace: "namespace_2"}},
|
||||
}}
|
||||
objs.PersistentVolumeClaims = &corev1.PersistentVolumeClaimList{Items: []corev1.PersistentVolumeClaim{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "pvc_1", Namespace: "namespace_1"}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "pvc_2", Namespace: "namespace_2"}},
|
||||
}}
|
||||
objs.ConfigMaps = &corev1.ConfigMapList{Items: []corev1.ConfigMap{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "cm_1", Namespace: "namespace_1"}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "cm_2", Namespace: "namespace_2"}},
|
||||
}}
|
||||
objs.Services = &corev1.ServiceList{Items: []corev1.Service{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "svc_1", Namespace: "namespace_1"}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "svc_2", Namespace: "namespace_2"}},
|
||||
}}
|
||||
objs.Secrets = &corev1.SecretList{Items: []corev1.Secret{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "secret_1", Namespace: "namespace_1"}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "secret_2", Namespace: "namespace_2"}},
|
||||
}}
|
||||
objs.ServiceAccounts = &corev1.ServiceAccountList{Items: []corev1.ServiceAccount{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "sa_1", Namespace: "namespace_1"}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "sa_2", Namespace: "namespace_2"}},
|
||||
}}
|
||||
return objs
|
||||
}
|
||||
|
||||
func namespace1Objects() *Objects {
|
||||
objs := objects()
|
||||
objs.Pods = &corev1.PodList{Items: []corev1.Pod{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "pod_1", Namespace: "namespace_1"}},
|
||||
}}
|
||||
objs.PodTemplates = &corev1.PodTemplateList{Items: []corev1.PodTemplate{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "template_1", Namespace: "namespace_1"}},
|
||||
}}
|
||||
objs.PersistentVolumeClaims = &corev1.PersistentVolumeClaimList{Items: []corev1.PersistentVolumeClaim{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "pvc_1", Namespace: "namespace_1"}},
|
||||
}}
|
||||
objs.ConfigMaps = &corev1.ConfigMapList{Items: []corev1.ConfigMap{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "cm_1", Namespace: "namespace_1"}},
|
||||
}}
|
||||
objs.Services = &corev1.ServiceList{Items: []corev1.Service{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "svc_1", Namespace: "namespace_1"}},
|
||||
}}
|
||||
objs.Secrets = &corev1.SecretList{Items: []corev1.Secret{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "secret_1", Namespace: "namespace_1"}},
|
||||
}}
|
||||
objs.ServiceAccounts = &corev1.ServiceAccountList{Items: []corev1.ServiceAccount{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "sa_1", Namespace: "namespace_1"}},
|
||||
}}
|
||||
return objs
|
||||
assert.Equal(t,
|
||||
metav1.ListOptions{FieldSelector: fields.OneTermNotEqualSelector("metadata.namespace","namespace-2").String()},
|
||||
filter.NamespaceOptions(metav1.ListOptions{}),
|
||||
)
|
||||
}
|
|
@ -64,7 +64,7 @@ type Client struct {
|
|||
|
||||
// FetchObjects returns the objects from a Kubernetes cluster.
|
||||
// ctx is currently unused during API calls. More info: https://github.com/kubernetes/community/pull/1166
|
||||
func (c *Client) FetchObjects(ctx context.Context) (*Objects, error) {
|
||||
func (c *Client) FetchObjects(ctx context.Context,filter ObjectFilter) (*Objects, error) {
|
||||
client := c.KubeClient.CoreV1()
|
||||
admissionControllerClient := c.KubeClient.AdmissionregistrationV1beta1()
|
||||
opts := metav1.ListOptions{}
|
||||
|
@ -85,39 +85,39 @@ func (c *Client) FetchObjects(ctx context.Context) (*Objects, error) {
|
|||
return
|
||||
})
|
||||
g.Go(func() (err error) {
|
||||
objects.Pods, err = client.Pods(corev1.NamespaceAll).List(opts)
|
||||
objects.Pods, err = client.Pods(corev1.NamespaceAll).List(filter.NamespaceOptions(opts))
|
||||
return
|
||||
})
|
||||
g.Go(func() (err error) {
|
||||
objects.PodTemplates, err = client.PodTemplates(corev1.NamespaceAll).List(opts)
|
||||
objects.PodTemplates, err = client.PodTemplates(corev1.NamespaceAll).List(filter.NamespaceOptions(opts))
|
||||
return
|
||||
})
|
||||
g.Go(func() (err error) {
|
||||
objects.PersistentVolumeClaims, err = client.PersistentVolumeClaims(corev1.NamespaceAll).List(opts)
|
||||
objects.PersistentVolumeClaims, err = client.PersistentVolumeClaims(corev1.NamespaceAll).List(filter.NamespaceOptions(opts))
|
||||
return
|
||||
})
|
||||
g.Go(func() (err error) {
|
||||
objects.ConfigMaps, err = client.ConfigMaps(corev1.NamespaceAll).List(opts)
|
||||
objects.ConfigMaps, err = client.ConfigMaps(corev1.NamespaceAll).List(filter.NamespaceOptions(opts))
|
||||
return
|
||||
})
|
||||
g.Go(func() (err error) {
|
||||
objects.Secrets, err = client.Secrets(corev1.NamespaceAll).List(opts)
|
||||
objects.Secrets, err = client.Secrets(corev1.NamespaceAll).List(filter.NamespaceOptions(opts))
|
||||
return
|
||||
})
|
||||
g.Go(func() (err error) {
|
||||
objects.Services, err = client.Services(corev1.NamespaceAll).List(opts)
|
||||
objects.Services, err = client.Services(corev1.NamespaceAll).List(filter.NamespaceOptions(opts))
|
||||
return
|
||||
})
|
||||
g.Go(func() (err error) {
|
||||
objects.ServiceAccounts, err = client.ServiceAccounts(corev1.NamespaceAll).List(opts)
|
||||
objects.ServiceAccounts, err = client.ServiceAccounts(corev1.NamespaceAll).List(filter.NamespaceOptions(opts))
|
||||
return
|
||||
})
|
||||
g.Go(func() (err error) {
|
||||
objects.ResourceQuotas, err = client.ResourceQuotas(corev1.NamespaceAll).List(opts)
|
||||
objects.ResourceQuotas, err = client.ResourceQuotas(corev1.NamespaceAll).List(filter.NamespaceOptions(opts))
|
||||
return
|
||||
})
|
||||
g.Go(func() (err error) {
|
||||
objects.LimitRanges, err = client.LimitRanges(corev1.NamespaceAll).List(opts)
|
||||
objects.LimitRanges, err = client.LimitRanges(corev1.NamespaceAll).List(filter.NamespaceOptions(opts))
|
||||
return
|
||||
})
|
||||
g.Go(func() (err error) {
|
||||
|
|
|
@ -40,7 +40,7 @@ func TestFetchObjects(t *testing.T) {
|
|||
Labels: map[string]string{"doks_key": "bar"}},
|
||||
})
|
||||
|
||||
actual, err := api.FetchObjects(context.Background())
|
||||
actual, err := api.FetchObjects(context.Background(),ObjectFilter{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NotNil(t, actual.Nodes)
|
||||
|
|
Loading…
Reference in New Issue