feat: add label selector (#1201)

* feat: fix the custom-analysis printing (#1195)

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

* feat: add label selector

Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

* feat: add label selector on analyzers

Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

* chore(deps): pin goreleaser/goreleaser-action action to 286f3b1 (#1171)

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

* fix(deps): update module buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go to v1.4.0-20240715142657-3785f0a44aae.2 (#1196)

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

* chore(deps): update actions/upload-artifact digest to 0b2256b (#1175)

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

* chore: update proto pkg version

Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

* chore: fix typo

Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

* feat: add label string to LabelSelector util func

Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

* feat: add test using 2 label selector

Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

---------

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
Signed-off-by: JuHyung-Son <sonju0427@gmail.com>
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Signed-off-by: JuHyung Son <sonju0427@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This commit is contained in:
JuHyung Son
2024-07-18 22:31:41 +09:00
committed by GitHub
parent 5176759bd0
commit eb3b81f176
41 changed files with 1000 additions and 28 deletions

View File

@@ -33,6 +33,7 @@ var (
language string
nocache bool
namespace string
labelSelector string
anonymize bool
maxConcurrency int
withDoc bool
@@ -55,6 +56,7 @@ var AnalyzeCmd = &cobra.Command{
language,
filters,
namespace,
labelSelector,
nocache,
explain,
maxConcurrency,
@@ -142,4 +144,6 @@ func init() {
AnalyzeCmd.Flags().BoolVarP(&customAnalysis, "custom-analysis", "z", false, "Enable custom analyzers")
// add custom headers flag
AnalyzeCmd.Flags().StringSliceVarP(&customHeaders, "custom-headers", "r", []string{}, "Custom Headers, <key>:<value> (e.g CustomHeaderKey:CustomHeaderValue AnotherHeader:AnotherValue)")
// label selector flag
AnalyzeCmd.Flags().StringVarP(&labelSelector, "selector", "L", "", "Label selector (label query) to filter on, supports '=', '==', and '!='. (e.g. -L key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints.")
}

4
go.mod
View File

@@ -29,8 +29,8 @@ require github.com/adrg/xdg v0.4.0
require (
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.20.0-20240406062209-1cc152efbf5c.1
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.4.0-20240715142657-3785f0a44aae.2
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.2-20240715142657-3785f0a44aae.2
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240406062209-1cc152efbf5c.3
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.2-20240717144446-c4efcc29ff16.2
cloud.google.com/go/storage v1.40.0
cloud.google.com/go/vertexai v0.7.1
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0

16
go.sum
View File

@@ -8,13 +8,20 @@ atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.20.0-20240406062209-1cc152efbf5c.1 h1:0x36l6ETxg5YDlfFTxSR+4SpL0bwLezTCUfGdcPUN44=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.20.0-20240406062209-1cc152efbf5c.1/go.mod h1:/n44w/baTCuEmDgCBgSxQ2GEiO7N645eKxLKbygzW4s=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.20.0-20240717144446-c4efcc29ff16.2 h1:68+/5HyHF3WAm5PtNvRwuzSqTD/im9JlylgyneHCPVY=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.20.0-20240717144446-c4efcc29ff16.2/go.mod h1:eYww1zlm6K5Tfwo3AfcVNMhnGJXR32t/PbZiPbvzv4s=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240406062209-1cc152efbf5c.3 h1:EiautHLlnNmBZdh1wFpmrSDvV4t8sucXGwV6vaE8Xuc=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240406062209-1cc152efbf5c.3/go.mod h1:4QVX5iWdNcwSFhpXXIXwVH7qT/g9LKvxiqa0SvYJ9hE=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.4.0-20240715142657-3785f0a44aae.2 h1:cUwK1ZqeW7HBL/kVxp16XHz7zXzVsuwXZQBKqAgh35k=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.4.0-20240715142657-3785f0a44aae.2/go.mod h1:/amuehNhXZEybzOmSYq4ghCe+4j9IoMaQldDOOPsPL4=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240717144446-c4efcc29ff16.3 h1:3HaNXtw/bEPWPXSqwLgEtBvqh39HLPYnFzG5UmZ00hs=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240717144446-c4efcc29ff16.3/go.mod h1:2FnWdzuB/BRUpuEQ71s/EviXJvVzzlHv8BvTlFFYgmQ=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.0-20240406062209-1cc152efbf5c.1/go.mod h1:qFzoT6sNuRF9vPeDFmxd9KZ1YgU2vnnno5E5I0OUjOc=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.0-20240717144446-c4efcc29ff16.1/go.mod h1:qFzoT6sNuRF9vPeDFmxd9KZ1YgU2vnnno5E5I0OUjOc=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.1-20240406062209-1cc152efbf5c.1 h1:DLKuL/RwZg0bRweSS18Bi67GzKOW3F6YnVU0nZYXZBU=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.1-20240406062209-1cc152efbf5c.1/go.mod h1:qEarbrHjaZEQ5GeUH6XqSqqJMvtPwAGFpAc0nkSBzrQ=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.2-20240715142657-3785f0a44aae.2 h1:rH3TEq0JwQ0AzP3dGkPMwzrjOg3F+548ph7RgSvw2aQ=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.2-20240715142657-3785f0a44aae.2/go.mod h1:1wq1qVxvJkTEUQsF5/XjmhQYXYhbVoLSGhKnzS3ie54=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.1-20240717144446-c4efcc29ff16.1 h1:TJj+JSRc64sitUGL1XaCPY/OU090/5lNi4ABLG9jTSQ=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.1-20240717144446-c4efcc29ff16.1/go.mod h1:qEarbrHjaZEQ5GeUH6XqSqqJMvtPwAGFpAc0nkSBzrQ=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.2-20240717144446-c4efcc29ff16.2 h1:INIYy743CJ4MEZu+O4by7oeC/m+a/l4HBk79FshPGBI=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.2-20240717144446-c4efcc29ff16.2/go.mod h1:1wq1qVxvJkTEUQsF5/XjmhQYXYhbVoLSGhKnzS3ie54=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -3315,6 +3322,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
helm.sh/helm/v3 v3.15.2 h1:/3XINUFinJOBjQplGnjw92eLGpgXXp1L8chWPkCkDuw=

View File

@@ -44,6 +44,7 @@ type Analysis struct {
Results []common.Result
Errors []string
Namespace string
LabelSelector string
Cache cache.ICache
Explain bool
MaxConcurrency int
@@ -74,6 +75,7 @@ func NewAnalysis(
language string,
filters []string,
namespace string,
labelSelector string,
noCache bool,
explain bool,
maxConcurrency int,
@@ -105,6 +107,7 @@ func NewAnalysis(
Client: client,
Language: language,
Namespace: namespace,
LabelSelector: labelSelector,
Cache: cache,
Explain: explain,
MaxConcurrency: maxConcurrency,
@@ -200,6 +203,7 @@ func (a *Analysis) RunAnalysis() {
Client: a.Client,
Context: a.Context,
Namespace: a.Namespace,
LabelSelector: a.LabelSelector,
AIClient: a.AIClient,
OpenapiSchema: openapiSchema,
}

View File

@@ -43,7 +43,7 @@ func (analyzer CronJobAnalyzer) Analyze(a common.Analyzer) ([]common.Result, err
"analyzer_name": kind,
})
cronJobList, err := a.Client.GetClient().BatchV1().CronJobs(a.Namespace).List(a.Context, v1.ListOptions{})
cronJobList, err := a.Client.GetClient().BatchV1().CronJobs(a.Namespace).List(a.Context, v1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -156,3 +156,45 @@ func TestCronJobAnalyzer(t *testing.T) {
require.Equal(t, expectations[i], result.Name)
}
}
func TestCronJobAnalyzerLabelSelectorFiltering(t *testing.T) {
suspend := new(bool)
*suspend = true
invalidStartingDeadline := new(int64)
*invalidStartingDeadline = -7
validStartingDeadline := new(int64)
*validStartingDeadline = 7
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "CJ1",
Namespace: "default",
Labels: map[string]string{
"app": "cronjob",
},
},
},
&batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "CJ2",
Namespace: "default",
},
},
),
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=cronjob",
}
cjAnalyzer := CronJobAnalyzer{}
results, err := cjAnalyzer.Analyze(config)
require.NoError(t, err)
require.Equal(t, 1, len(results))
require.Equal(t, "default/CJ1", results[0].Name)
}

View File

@@ -46,7 +46,7 @@ func (d DeploymentAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error)
"analyzer_name": kind,
})
deployments, err := a.Client.GetClient().AppsV1().Deployments(a.Namespace).List(context.Background(), v1.ListOptions{})
deployments, err := a.Client.GetClient().AppsV1().Deployments(a.Namespace).List(context.Background(), v1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -151,3 +151,55 @@ func TestDeploymentAnalyzerNamespaceFiltering(t *testing.T) {
assert.Equal(t, analysisResults[0].Kind, "Deployment")
assert.Equal(t, analysisResults[0].Name, "default/example")
}
func TestDeploymentAnalyzerLabelSelectorFiltering(t *testing.T) {
clientset := fake.NewSimpleClientset(
&appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
Labels: map[string]string{
"app": "deployment",
},
},
Spec: appsv1.DeploymentSpec{
Replicas: func() *int32 { i := int32(3); return &i }(),
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{},
},
},
},
},
&appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "example2",
Namespace: "default",
},
Spec: appsv1.DeploymentSpec{
Replicas: func() *int32 { i := int32(3); return &i }(),
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{},
},
},
},
},
)
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=deployment",
}
deploymentAnalyzer := DeploymentAnalyzer{}
analysisResults, err := deploymentAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
}

View File

@@ -41,7 +41,9 @@ func (GatewayAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
if err != nil {
return nil, err
}
if err := client.List(a.Context, gtwList, &ctrl.ListOptions{}); err != nil {
labelSelector := util.LabelStrToSelector(a.LabelSelector)
if err := client.List(a.Context, gtwList, &ctrl.ListOptions{LabelSelector: labelSelector}); err != nil {
return nil, err
}

View File

@@ -25,10 +25,13 @@ func BuildGatewayClass(name string) gtwapi.GatewayClass {
return GatewayClass
}
func BuildGateway(className gtwapi.ObjectName, status metav1.ConditionStatus) gtwapi.Gateway {
func BuildGateway(className gtwapi.ObjectName, status metav1.ConditionStatus, labels map[string]string) gtwapi.Gateway {
Gateway := gtwapi.Gateway{}
Gateway.Name = "foobar"
Gateway.Namespace = "default"
if labels != nil {
Gateway.Labels = labels
}
Gateway.Spec.GatewayClassName = className
Gateway.Spec.Listeners = []gtwapi.Listener{
{
@@ -53,7 +56,7 @@ func TestGatewayAnalyzer(t *testing.T) {
AcceptedStatus := metav1.ConditionTrue
GatewayClass := BuildGatewayClass(string(ClassName))
Gateway := BuildGateway(ClassName, AcceptedStatus)
Gateway := BuildGateway(ClassName, AcceptedStatus, nil)
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
@@ -91,7 +94,7 @@ func TestGatewayAnalyzer(t *testing.T) {
func TestMissingClassGatewayAnalyzer(t *testing.T) {
ClassName := gtwapi.ObjectName("non-existed")
AcceptedStatus := metav1.ConditionTrue
Gateway := BuildGateway(ClassName, AcceptedStatus)
Gateway := BuildGateway(ClassName, AcceptedStatus, nil)
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
@@ -130,7 +133,7 @@ func TestStatusGatewayAnalyzer(t *testing.T) {
AcceptedStatus := metav1.ConditionUnknown
GatewayClass := BuildGatewayClass(string(ClassName))
Gateway := BuildGateway(ClassName, AcceptedStatus)
Gateway := BuildGateway(ClassName, AcceptedStatus, nil)
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
@@ -178,3 +181,70 @@ func TestStatusGatewayAnalyzer(t *testing.T) {
t.Errorf("Expected message, <%v> , not found in Gateway's analysis results", want)
}
}
func TestGatewayAnalyzerLabelSelectorFiltering(t *testing.T) {
ClassName := gtwapi.ObjectName("non-existed")
AcceptedStatus := metav1.ConditionTrue
Gateway := BuildGateway(ClassName, AcceptedStatus, map[string]string{"app": "gateway"})
scheme := scheme.Scheme
err := gtwapi.Install(scheme)
if err != nil {
t.Error(err)
}
err = apiextensionsv1.AddToScheme(scheme)
if err != nil {
t.Error(err)
}
objects := []runtime.Object{
&Gateway,
}
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build()
analyzerInstance := GatewayAnalyzer{}
// without label selector should return 1 result
config := common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
// with label selector should return 1 result
config = common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=gateway",
}
analysisResults, err = analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
// with wrong label selector should return 0 result
config = common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=wrong",
}
analysisResults, err = analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 0)
}

View File

@@ -39,7 +39,9 @@ func (GatewayClassAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error)
if err != nil {
return nil, err
}
if err := client.List(a.Context, gcList, &ctrl.ListOptions{}); err != nil {
labelSelector := util.LabelStrToSelector(a.LabelSelector)
if err := client.List(a.Context, gcList, &ctrl.ListOptions{LabelSelector: labelSelector}); err != nil {
return nil, err
}
var preAnalysis = map[string]common.PreAnalysis{}

View File

@@ -55,3 +55,51 @@ func TestGatewayClassAnalyzer(t *testing.T) {
assert.Equal(t, len(analysisResults), 1)
}
func TestGatewayClassAnalyzerLabelSelectorFiltering(t *testing.T) {
condition := metav1.Condition{
Type: "Accepted",
Status: "Ready",
Message: "Ready",
Reason: "Ready",
}
// Create two GatewayClasses with different labels
GatewayClass := &gtwapi.GatewayClass{}
GatewayClass.Name = "foobar"
GatewayClass.Spec.ControllerName = "gateway.fooproxy.io/gatewayclass-controller"
GatewayClass.Labels = map[string]string{"app": "gatewayclass"}
GatewayClass.Status.Conditions = []metav1.Condition{condition}
GatewayClass2 := &gtwapi.GatewayClass{}
GatewayClass2.Name = "foobar2"
GatewayClass2.Spec.ControllerName = "gateway.fooproxy.io/gatewayclass-controller"
GatewayClass2.Status.Conditions = []metav1.Condition{condition}
scheme := scheme.Scheme
err := gtwapi.Install(scheme)
if err != nil {
t.Error(err)
}
err = apiextensionsv1.AddToScheme(scheme)
if err != nil {
t.Error(err)
}
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(GatewayClass, GatewayClass2).Build()
analyzerInstance := GatewayClassAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=gatewayclass",
}
analysisResults, err := analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
}

View File

@@ -43,7 +43,7 @@ func (HpaAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
"analyzer_name": kind,
})
list, err := a.Client.GetClient().AutoscalingV1().HorizontalPodAutoscalers(a.Namespace).List(a.Context, metav1.ListOptions{})
list, err := a.Client.GetClient().AutoscalingV1().HorizontalPodAutoscalers(a.Namespace).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -531,3 +531,37 @@ func TestHPAAnalyzerNamespaceFiltering(t *testing.T) {
}
assert.Equal(t, len(analysisResults), 1)
}
func TestHPAAnalyzerLabelSelectorFiltering(t *testing.T) {
clientset := fake.NewSimpleClientset(
&autoscalingv1.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
Labels: map[string]string{
"app": "hpa",
},
},
},
&autoscalingv1.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
Name: "example2",
Namespace: "default",
},
},
)
hpaAnalyzer := HpaAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=hpa",
}
analysisResults, err := hpaAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
}

View File

@@ -42,7 +42,9 @@ func (HTTPRouteAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
if err != nil {
return nil, err
}
if err := client.List(a.Context, routeList, &ctrl.ListOptions{}); err != nil {
labelSelector := util.LabelStrToSelector(a.LabelSelector)
if err := client.List(a.Context, routeList, &ctrl.ListOptions{LabelSelector: labelSelector}); err != nil {
return nil, err
}
var preAnalysis = map[string]common.PreAnalysis{}

View File

@@ -41,7 +41,7 @@ func (IngressAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
"analyzer_name": kind,
})
list, err := a.Client.GetClient().NetworkingV1().Ingresses(a.Namespace).List(a.Context, metav1.ListOptions{})
list, err := a.Client.GetClient().NetworkingV1().Ingresses(a.Namespace).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -201,3 +201,40 @@ func TestIngressAnalyzer(t *testing.T) {
require.Equal(t, expectations[i].failuresCount, len(result.Error))
}
}
func TestIngressAnalyzerLabelSelectorFiltering(t *testing.T) {
validIgClassName := new(string)
*validIgClassName = "valid-ingress-class"
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "Ingress1",
Namespace: "default",
Labels: map[string]string{
"app": "ingress",
},
},
},
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "Ingress2",
Namespace: "default",
},
},
),
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=ingress",
}
igAnalyzer := IngressAnalyzer{}
results, err := igAnalyzer.Analyze(config)
require.NoError(t, err)
require.Equal(t, 1, len(results))
require.Equal(t, "default/Ingress1", results[0].Name)
}

View File

@@ -41,7 +41,7 @@ func (LogAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
})
// search all namespaces for pods that are not running
list, err := a.Client.GetClient().CoreV1().Pods(a.Namespace).List(a.Context, metav1.ListOptions{})
list, err := a.Client.GetClient().CoreV1().Pods(a.Namespace).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -118,3 +118,56 @@ func TestLogAnalyzer(t *testing.T) {
}
}
}
func TestLogAnalyzerLabelSelectorFiltering(t *testing.T) {
oldPattern := errorPattern
errorPattern = regexp.MustCompile(`(fake logs)`)
t.Cleanup(func() {
errorPattern = oldPattern
})
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod1",
Namespace: "default",
Labels: map[string]string{
"app": "log",
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "test-container1",
},
},
},
},
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod2",
Namespace: "default",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "test-container2",
},
},
},
},
),
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=log",
}
logAnalyzer := LogAnalyzer{}
results, err := logAnalyzer.Analyze(config)
require.NoError(t, err)
require.Equal(t, 1, len(results))
require.Equal(t, "default/Pod1/test-container1", results[0].Name)
}

View File

@@ -42,7 +42,7 @@ func (MutatingWebhookAnalyzer) Analyze(a common.Analyzer) ([]common.Result, erro
"analyzer_name": kind,
})
mutatingWebhooks, err := a.Client.GetClient().AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.Background(), v1.ListOptions{})
mutatingWebhooks, err := a.Client.GetClient().AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.Background(), v1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -138,3 +138,78 @@ func TestMutatingWebhookAnalyzer(t *testing.T) {
resultsLen := 3
require.Equal(t, resultsLen, len(results))
}
func TestMutatingWebhookAnalyzerLabelSelectorFiltering(t *testing.T) {
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod1",
Namespace: "default",
Labels: map[string]string{
"app": "mutating-webhook",
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-service1",
Namespace: "default",
},
Spec: v1.ServiceSpec{
Selector: map[string]string{
"app": "mutating-webhook",
},
},
},
&admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "test-mutating-webhook-config",
Namespace: "default",
Labels: map[string]string{
"app": "mutating-webhook",
},
},
Webhooks: []admissionregistrationv1.MutatingWebhook{
{
Name: "webhook1",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service1",
Namespace: "default",
},
},
},
},
},
&admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "test-mutating-webhook-config2",
Namespace: "default",
},
Webhooks: []admissionregistrationv1.MutatingWebhook{
{
Name: "webhook2",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service1",
Namespace: "default",
},
},
},
},
},
),
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=mutating-webhook",
}
mwAnalyzer := MutatingWebhookAnalyzer{}
results, err := mwAnalyzer.Analyze(config)
require.NoError(t, err)
require.Equal(t, 1, len(results))
require.Equal(t, "default/webhook1", results[0].Name)
}

View File

@@ -43,7 +43,7 @@ func (NetworkPolicyAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error)
// get all network policies in the namespace
policies, err := a.Client.GetClient().NetworkingV1().
NetworkPolicies(a.Namespace).List(a.Context, metav1.ListOptions{})
NetworkPolicies(a.Namespace).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -220,3 +220,46 @@ func TestNetpolNoPodsNamespaceFiltering(t *testing.T) {
assert.Equal(t, results[0].Kind, "NetworkPolicy")
}
func TestNetpolLabelSelectorFiltering(t *testing.T) {
clientset := fake.NewSimpleClientset(
&networkingv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "example1",
Namespace: "default",
Labels: map[string]string{
"app": "netpol",
},
},
Spec: networkingv1.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "netpol",
},
},
},
},
&networkingv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "example2",
Namespace: "default",
},
},
)
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=netpol",
}
analyzer := NetworkPolicyAnalyzer{}
results, err := analyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(results), 1)
}

View File

@@ -33,7 +33,7 @@ func (NodeAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
"analyzer_name": kind,
})
list, err := a.Client.GetClient().CoreV1().Nodes().List(a.Context, metav1.ListOptions{})
list, err := a.Client.GetClient().CoreV1().Nodes().List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -167,3 +167,51 @@ func TestNodeAnalyzer(t *testing.T) {
require.Equal(t, expectations[i].failuresCount, len(result.Error))
}
}
func TestNodeAnalyzerLabelSelectorFiltering(t *testing.T) {
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(&v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "Node1",
Namespace: "default",
Labels: map[string]string{
"app": "node",
},
},
Status: v1.NodeStatus{
Conditions: []v1.NodeCondition{
{
Type: v1.NodeReady,
Status: v1.ConditionFalse,
},
},
},
},
&v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "Node2",
Namespace: "default",
},
Status: v1.NodeStatus{
Conditions: []v1.NodeCondition{
{
Type: v1.NodeReady,
Status: v1.ConditionFalse,
},
},
},
},
),
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=node",
}
nAnalyzer := NodeAnalyzer{}
results, err := nAnalyzer.Analyze(config)
require.NoError(t, err)
require.Equal(t, 1, len(results))
require.Equal(t, "Node1", results[0].Name)
}

View File

@@ -41,7 +41,7 @@ func (PdbAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
"analyzer_name": kind,
})
list, err := a.Client.GetClient().PolicyV1().PodDisruptionBudgets(a.Namespace).List(a.Context, metav1.ListOptions{})
list, err := a.Client.GetClient().PolicyV1().PodDisruptionBudgets(a.Namespace).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -115,3 +115,94 @@ func TestPodDisruptionBudgetAnalyzer(t *testing.T) {
require.Equal(t, 1, len(results))
require.Equal(t, "test/PDB3", results[0].Name)
}
func TestPodDisruptionBudgetAnalyzerLabelSelectorFiltering(t *testing.T) {
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&policyv1.PodDisruptionBudget{
ObjectMeta: metav1.ObjectMeta{
Name: "PDB1",
Namespace: "default",
Labels: map[string]string{
"app": "pdb",
},
},
// Status conditions are nil.
Status: policyv1.PodDisruptionBudgetStatus{
Conditions: []metav1.Condition{
{
Type: "DisruptionAllowed",
Status: "False",
Reason: "test reason",
},
},
},
Spec: policyv1.PodDisruptionBudgetSpec{
MaxUnavailable: &intstr.IntOrString{
Type: 0,
IntVal: 17,
StrVal: "17",
},
MinAvailable: &intstr.IntOrString{
Type: 0,
IntVal: 7,
StrVal: "7",
},
// MatchLabels specified.
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"label1": "test1",
"label2": "test2",
},
},
},
},
&policyv1.PodDisruptionBudget{
ObjectMeta: metav1.ObjectMeta{
Name: "PDB2",
Namespace: "default",
},
// Status conditions are empty.
Status: policyv1.PodDisruptionBudgetStatus{
Conditions: []metav1.Condition{
{
Type: "DisruptionAllowed",
Status: "False",
Reason: "test reason",
},
},
},
Spec: policyv1.PodDisruptionBudgetSpec{
MaxUnavailable: &intstr.IntOrString{
Type: 0,
IntVal: 17,
StrVal: "17",
},
MinAvailable: &intstr.IntOrString{
Type: 0,
IntVal: 7,
StrVal: "7",
},
// MatchLabels specified.
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"label1": "test1",
"label2": "test2",
},
},
},
},
),
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=pdb",
}
pdbAnalyzer := PdbAnalyzer{}
results, err := pdbAnalyzer.Analyze(config)
require.NoError(t, err)
require.Equal(t, 1, len(results))
require.Equal(t, "default/PDB1", results[0].Name)
}

View File

@@ -34,7 +34,9 @@ func (PodAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
})
// search all namespaces for pods that are not running
list, err := a.Client.GetClient().CoreV1().Pods(a.Namespace).List(a.Context, metav1.ListOptions{})
list, err := a.Client.GetClient().CoreV1().Pods(a.Namespace).List(a.Context, metav1.ListOptions{
LabelSelector: a.LabelSelector,
})
if err != nil {
return nil, err
}

View File

@@ -33,7 +33,7 @@ func (PvcAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
})
// search all namespaces for pods that are not running
list, err := a.Client.GetClient().CoreV1().PersistentVolumeClaims(a.Namespace).List(a.Context, metav1.ListOptions{})
list, err := a.Client.GetClient().CoreV1().PersistentVolumeClaims(a.Namespace).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -228,3 +228,53 @@ func TestPersistentVolumeClaimAnalyzer(t *testing.T) {
})
}
}
func TestPvcAnalyzerLabelSelectorFiltering(t *testing.T) {
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&appsv1.Event{
ObjectMeta: metav1.ObjectMeta{
Name: "Event1",
Namespace: "default",
},
LastTimestamp: metav1.Time{
Time: time.Date(2024, 3, 15, 10, 0, 0, 0, time.UTC),
},
Reason: "ProvisioningFailed",
Message: "PVC Event1 provisioning failed",
},
&appsv1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "PVC1",
Namespace: "default",
Labels: map[string]string{
"app": "pvc",
},
},
Status: appsv1.PersistentVolumeClaimStatus{
Phase: appsv1.ClaimPending,
},
},
&appsv1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "PVC2",
Namespace: "default",
},
Status: appsv1.PersistentVolumeClaimStatus{
Phase: appsv1.ClaimPending,
},
},
),
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=pvc",
}
pvcAnalyzer := PvcAnalyzer{}
results, err := pvcAnalyzer.Analyze(config)
require.NoError(t, err)
require.Equal(t, 1, len(results))
require.Equal(t, "default/PVC1", results[0].Name)
}

View File

@@ -32,7 +32,7 @@ func (ReplicaSetAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
})
// search all namespaces for pods that are not running
list, err := a.Client.GetClient().AppsV1().ReplicaSets(a.Namespace).List(a.Context, metav1.ListOptions{})
list, err := a.Client.GetClient().AppsV1().ReplicaSets(a.Namespace).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -144,3 +144,58 @@ func TestReplicaSetAnalyzer(t *testing.T) {
require.Equal(t, expectations[i].failuresCount, len(result.Error))
}
}
func TestReplicaSetAnalyzerLabelSelectorFiltering(t *testing.T) {
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
Name: "ReplicaSet1",
Namespace: "default",
Labels: map[string]string{
"app": "replicaset",
},
},
Status: appsv1.ReplicaSetStatus{
Replicas: 0,
Conditions: []appsv1.ReplicaSetCondition{
{
// Should contribute to failures.
Type: appsv1.ReplicaSetReplicaFailure,
Reason: "FailedCreate",
Message: "failed to create test replica set 1",
},
},
},
},
&appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
Name: "ReplicaSet2",
Namespace: "default",
},
Status: appsv1.ReplicaSetStatus{
Replicas: 0,
Conditions: []appsv1.ReplicaSetCondition{
{
// Should contribute to failures.
Type: appsv1.ReplicaSetReplicaFailure,
Reason: "FailedCreate",
Message: "failed to create test replica set 1",
},
},
},
},
),
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=replicaset",
}
rsAnalyzer := ReplicaSetAnalyzer{}
results, err := rsAnalyzer.Analyze(config)
require.NoError(t, err)
require.Equal(t, 1, len(results))
require.Equal(t, "default/ReplicaSet1", results[0].Name)
}

View File

@@ -45,7 +45,7 @@ func (ServiceAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
})
// search all namespaces for pods that are not running
list, err := a.Client.GetClient().CoreV1().Endpoints(a.Namespace).List(a.Context, metav1.ListOptions{})
list, err := a.Client.GetClient().CoreV1().Endpoints(a.Namespace).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -165,3 +165,106 @@ func TestServiceAnalyzer(t *testing.T) {
require.Equal(t, expectations[i].failuresCount, len(result.Error))
}
}
func TestServiceAnalyzerLabelSelectorFiltering(t *testing.T) {
clientSet :=
fake.NewSimpleClientset(
&v1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: "Endpoint1",
Namespace: "default",
Labels: map[string]string{
"app": "service",
"part-of": "test",
},
},
// Endpoint with non-zero subsets.
Subsets: []v1.EndpointSubset{
{
// These not ready end points will contribute to failures.
NotReadyAddresses: []v1.EndpointAddress{
{
TargetRef: &v1.ObjectReference{
Kind: "test-reference",
Name: "reference1",
},
},
{
TargetRef: &v1.ObjectReference{
Kind: "test-reference",
Name: "reference2",
},
},
},
},
{
// These not ready end points will contribute to failures.
NotReadyAddresses: []v1.EndpointAddress{
{
TargetRef: &v1.ObjectReference{
Kind: "test-reference",
Name: "reference3",
},
},
},
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "Service1",
Namespace: "default",
Labels: map[string]string{
"app": "service",
},
},
Spec: v1.ServiceSpec{
Selector: map[string]string{
"app1": "test-app1",
"app2": "test-app2",
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "Service2",
Namespace: "default",
},
Spec: v1.ServiceSpec{
Selector: map[string]string{
"app1": "test-app1",
"app2": "test-app2",
},
},
},
)
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientSet,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=service",
}
sAnalyzer := ServiceAnalyzer{}
results, err := sAnalyzer.Analyze(config)
require.NoError(t, err)
require.Equal(t, 1, len(results))
require.Equal(t, "default/Endpoint1", results[0].Name)
config = common.Analyzer{
Client: &kubernetes.Client{
Client: clientSet,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=service,part-of=test",
}
sAnalyzer = ServiceAnalyzer{}
results, err = sAnalyzer.Analyze(config)
require.NoError(t, err)
require.Equal(t, 1, len(results))
require.Equal(t, "default/Endpoint1", results[0].Name)
}

View File

@@ -41,7 +41,7 @@ func (StatefulSetAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
"analyzer_name": kind,
})
list, err := a.Client.GetClient().AppsV1().StatefulSets(a.Namespace).List(a.Context, metav1.ListOptions{})
list, err := a.Client.GetClient().AppsV1().StatefulSets(a.Namespace).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -188,3 +188,55 @@ func TestStatefulSetAnalyzerNamespaceFiltering(t *testing.T) {
}
assert.Equal(t, len(analysisResults), 1)
}
func TestStatefulSetAnalyzerLabelSelectorFiltering(t *testing.T) {
clientSet := fake.NewSimpleClientset(
&appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Name: "example1",
Namespace: "default",
Labels: map[string]string{
"app": "statefulset",
"part-of": "test",
},
},
},
&appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Name: "example2",
Namespace: "default",
},
},
)
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientSet,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=statefulset",
}
statefulSetAnalyzer := StatefulSetAnalyzer{}
results, err := statefulSetAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, 1, len(results))
assert.Equal(t, "default/example1", results[0].Name)
config = common.Analyzer{
Client: &kubernetes.Client{
Client: clientSet,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=statefulset,part-of=test",
}
statefulSetAnalyzer = StatefulSetAnalyzer{}
results, err = statefulSetAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, 1, len(results))
assert.Equal(t, "default/example1", results[0].Name)
}

View File

@@ -42,7 +42,7 @@ func (ValidatingWebhookAnalyzer) Analyze(a common.Analyzer) ([]common.Result, er
"analyzer_name": kind,
})
validatingWebhooks, err := a.Client.GetClient().AdmissionregistrationV1().ValidatingWebhookConfigurations().List(context.Background(), v1.ListOptions{})
validatingWebhooks, err := a.Client.GetClient().AdmissionregistrationV1().ValidatingWebhookConfigurations().List(context.Background(), v1.ListOptions{LabelSelector: a.LabelSelector})
if err != nil {
return nil, err
}

View File

@@ -138,3 +138,80 @@ func TestValidatingWebhookAnalyzer(t *testing.T) {
resultsLen := 3
require.Equal(t, resultsLen, len(results))
}
func TestValidatingWebhookAnalyzerLabelSelectorFiltering(t *testing.T) {
clientSet := fake.NewSimpleClientset(
&admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "test-validating-webhook-config1",
Namespace: "default",
Labels: map[string]string{
"app": "validating-webhook",
"part-of": "test",
},
},
Webhooks: []admissionregistrationv1.ValidatingWebhook{
{
// Failure: Pointing to an inactive receiver pod
Name: "webhook1",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service1",
Namespace: "default",
},
},
},
},
},
&admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "test-validating-webhook-config2",
Namespace: "default",
},
Webhooks: []admissionregistrationv1.ValidatingWebhook{
{
// Failure: Pointing to an inactive receiver pod
Name: "webhook1",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service1",
Namespace: "default",
},
},
},
},
},
)
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientSet,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=validating-webhook",
}
vwAnalyzer := ValidatingWebhookAnalyzer{}
results, err := vwAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
require.Equal(t, 1, len(results))
config = common.Analyzer{
Client: &kubernetes.Client{
Client: clientSet,
},
Context: context.Background(),
Namespace: "default",
LabelSelector: "app=validating-webhook,part-of=test",
}
vwAnalyzer = ValidatingWebhookAnalyzer{}
results, err = vwAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
require.Equal(t, 1, len(results))
}

View File

@@ -39,6 +39,7 @@ type Analyzer struct {
Client *kubernetes.Client
Context context.Context
Namespace string
LabelSelector string
AIClient ai.IAI
PreAnalysis map[string]PreAnalysis
Results []Result

View File

@@ -25,6 +25,7 @@ func (h *handler) Analyze(ctx context.Context, i *schemav1.AnalyzeRequest) (
i.Language,
i.Filters,
i.Namespace,
i.LabelSelector,
i.Nocache,
i.Explain,
int(i.MaxConcurrency),

View File

@@ -26,6 +26,8 @@ import (
"regexp"
"strings"
"k8s.io/apimachinery/pkg/labels"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -295,3 +297,17 @@ func NewHeaders(customHeaders []string) []http.Header {
return result
}
func LabelStrToSelector(labelStr string) labels.Selector {
if labelStr == "" {
return nil
}
labelSelectorMap := make(map[string]string)
for _, s := range strings.Split(labelStr, ",") {
parts := strings.SplitN(s, "=", 2)
if len(parts) == 2 {
labelSelectorMap[parts[0]] = parts[1]
}
}
return labels.SelectorFromSet(labels.Set(labelSelectorMap))
}