Use `FieldSelector` to filter Namespace

wwarren/update-k8s-deps
valbeat 2020-01-22 19:24:44 +09:00
parent 0fe6e35217
commit 653f22aac2
6 changed files with 46 additions and 264 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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{}),
)
}

View File

@ -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) {

View File

@ -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)