Skip to content

Commit 26c3c4a

Browse files
author
ops
committed
init
1 parent 13d824b commit 26c3c4a

File tree

11 files changed

+1196
-0
lines changed

11 files changed

+1196
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@
1313

1414
# Dependency directories (remove the comment below to include it)
1515
# vendor/
16+
17+
*.idea

cmd/root.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
Copyright © 2022 poorops poorops@163.com
3+
4+
*/
5+
package cmd
6+
7+
import (
8+
"fmt"
9+
"github.com/spf13/cobra"
10+
"k8s.io/cli-runtime/pkg/genericclioptions"
11+
"k8s.io/utils/strings/slices"
12+
"kubectl-check/pkg/base"
13+
"kubectl-check/pkg/check"
14+
"kubectl-check/pkg/client"
15+
"kubectl-check/pkg/table"
16+
"os"
17+
"strings"
18+
)
19+
20+
var (
21+
KubernetesConfigFlags *genericclioptions.ConfigFlags
22+
field string
23+
selector string
24+
)
25+
26+
// rootCmd represents the base command when called without any subcommands
27+
var rootCmd = &cobra.Command{
28+
Use: "kubectl-check",
29+
Short: "check field from k8s resource definition",
30+
Long: `kubectl-check is a plugin for kubectl command.
31+
the plugin is a tool to check definition for the specified filed from k8s resource.
32+
For Example:
33+
kubectl check -f nodeSelector`,
34+
// Uncomment the following line if your bare application
35+
// has an action associated with it:
36+
Run: func(cmd *cobra.Command, args []string) {
37+
namespace, _ := cmd.Flags().GetString("namespace")
38+
if namespace == "" {
39+
namespace = "default"
40+
}
41+
42+
f, _ := cmd.Flags().GetString("field")
43+
l, _ := cmd.Flags().GetString("selector")
44+
45+
if !slices.Contains(base.ValidFields, f) {
46+
fmt.Printf("invalid filed, for now supported filed is: %s, current is %s\n", strings.Join(base.ValidFields, ", "), f)
47+
return
48+
}
49+
50+
client, err := client.NewClientSet(KubernetesConfigFlags)
51+
if err != nil {
52+
fmt.Printf("error to set client: %v", err)
53+
return
54+
}
55+
56+
plugin := check.NewPlugin(f, namespace, l, client)
57+
result, e := plugin.Value()
58+
if e != nil {
59+
fmt.Printf("error to get result: %v", e)
60+
return
61+
}
62+
table.GenTable(result)
63+
},
64+
}
65+
66+
// Execute adds all child commands to the root command and sets flags appropriately.
67+
// This is called by main.main(). It only needs to happen once to the rootCmd.
68+
func Execute() {
69+
err := rootCmd.Execute()
70+
if err != nil {
71+
os.Exit(1)
72+
}
73+
}
74+
75+
func init() {
76+
// Here you will define your flags and configuration settings.
77+
// Cobra supports persistent flags, which, if defined here,
78+
// will be global for your application.
79+
80+
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.kubectl-check.yaml)")
81+
82+
// Cobra also supports local flags, which will only run
83+
// when this action is called directly.
84+
KubernetesConfigFlags = genericclioptions.NewConfigFlags(true)
85+
rootCmd.Flags().StringVarP(&field, "field", "f", "image", "The field to check")
86+
rootCmd.Flags().StringVarP(&selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
87+
KubernetesConfigFlags.AddFlags(rootCmd.Flags())
88+
}

go.mod

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module kubectl-check
2+
3+
go 1.16
4+
5+
require (
6+
github.com/olekukonko/tablewriter v0.0.5
7+
github.com/pkg/errors v0.9.1
8+
github.com/spf13/cobra v1.4.0
9+
k8s.io/api v0.24.0
10+
k8s.io/apimachinery v0.24.0
11+
k8s.io/cli-runtime v0.24.0
12+
k8s.io/client-go v0.24.0
13+
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
14+
)

go.sum

Lines changed: 776 additions & 0 deletions
Large diffs are not rendered by default.

main.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
Copyright © 2022 poorops poorops@163.com
3+
4+
*/
5+
package main
6+
7+
import "kubectl-check/cmd"
8+
9+
func main() {
10+
cmd.Execute()
11+
}

pkg/base/base.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package base
2+
3+
import (
4+
"context"
5+
"github.com/pkg/errors"
6+
v1 "k8s.io/api/apps/v1"
7+
batchv1 "k8s.io/api/batch/v1"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/client-go/kubernetes"
10+
)
11+
12+
type Field struct {
13+
Name string
14+
Namespace string
15+
Label string
16+
Client *kubernetes.Clientset
17+
}
18+
19+
type Resources struct {
20+
Deployments []v1.Deployment
21+
StatefulSets []v1.StatefulSet
22+
DaemonSets []v1.DaemonSet
23+
CronJobs []batchv1.CronJob
24+
}
25+
26+
var ValidFields = []string{"image", "resources"}
27+
28+
func (f *Field) genListOptions() metav1.ListOptions {
29+
return metav1.ListOptions{
30+
LabelSelector: f.Label,
31+
}
32+
}
33+
34+
func (f *Field) GetDeployments() ([]v1.Deployment, error) {
35+
list, e := f.Client.AppsV1().Deployments(f.Namespace).List(context.TODO(), f.genListOptions())
36+
if e != nil {
37+
return nil, errors.Wrap(e, "fail to list deployments")
38+
}
39+
return list.Items, nil
40+
}
41+
42+
func (f *Field) GetStatefulSets() ([]v1.StatefulSet, error) {
43+
list, e := f.Client.AppsV1().StatefulSets(f.Namespace).List(context.TODO(), f.genListOptions())
44+
if e != nil {
45+
return nil, errors.Wrap(e, "fail to list sts")
46+
}
47+
return list.Items, nil
48+
}
49+
50+
func (f *Field) GetDaemonSets() ([]v1.DaemonSet, error) {
51+
list, e := f.Client.AppsV1().DaemonSets(f.Namespace).List(context.TODO(), f.genListOptions())
52+
if e != nil {
53+
return nil, errors.Wrap(e, "fail to list ds")
54+
}
55+
return list.Items, nil
56+
}
57+
58+
func (f *Field) GetCronJobs() ([]batchv1.CronJob, error) {
59+
list, err := f.Client.BatchV1().CronJobs(f.Namespace).List(context.TODO(), f.genListOptions())
60+
if err != nil {
61+
return nil, errors.Wrap(err, "fail to list cronjob")
62+
}
63+
return list.Items, nil
64+
}
65+
66+
func (f *Field) GetResources() (Resources, error) {
67+
deployments, err := f.GetDeployments()
68+
if err != nil {
69+
return Resources{}, err
70+
}
71+
72+
sts, err := f.GetStatefulSets()
73+
if err != nil {
74+
return Resources{}, err
75+
}
76+
77+
daemonsets, err := f.GetDaemonSets()
78+
if err != nil {
79+
return Resources{}, err
80+
}
81+
82+
cronjobs, err := f.GetCronJobs()
83+
if err != nil {
84+
return Resources{}, err
85+
}
86+
return Resources{
87+
Deployments: deployments,
88+
StatefulSets: sts,
89+
DaemonSets: daemonsets,
90+
CronJobs: cronjobs,
91+
}, nil
92+
}

pkg/check/check.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package check
2+
3+
import (
4+
"k8s.io/client-go/kubernetes"
5+
"kubectl-check/pkg/base"
6+
"kubectl-check/pkg/fileds"
7+
)
8+
9+
type Plugin interface {
10+
Value() ([][]string, error)
11+
}
12+
13+
func NewPlugin(name, namespace, label string, client *kubernetes.Clientset) Plugin {
14+
switch name {
15+
case "image":
16+
return &fileds.ImageField{
17+
Field: base.Field{
18+
Name: name,
19+
Namespace: namespace,
20+
Label: label,
21+
Client: client,
22+
},
23+
}
24+
case "resources":
25+
return &fileds.ResourcesField{
26+
Field: base.Field{
27+
Name: name,
28+
Namespace: namespace,
29+
Label: label,
30+
Client: client,
31+
},
32+
}
33+
}
34+
return nil
35+
}

pkg/client/client.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package client
2+
3+
import (
4+
"github.com/pkg/errors"
5+
"k8s.io/cli-runtime/pkg/genericclioptions"
6+
"k8s.io/client-go/kubernetes"
7+
)
8+
9+
func NewClientSet(configFlags *genericclioptions.ConfigFlags) (*kubernetes.Clientset, error) {
10+
config, err := configFlags.ToRESTConfig()
11+
if err != nil {
12+
return nil, errors.Wrap(err, "failed to read kubeconfig")
13+
}
14+
15+
return kubernetes.NewForConfig(config)
16+
}

pkg/fileds/image.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package fileds
2+
3+
import (
4+
"fmt"
5+
"kubectl-check/pkg/base"
6+
)
7+
8+
type ImageField struct {
9+
base.Field
10+
}
11+
12+
func (i *ImageField) Value() ([][]string, error) {
13+
result := make([][]string, 0)
14+
15+
resource, err := i.GetResources()
16+
if err != nil {
17+
return nil, err
18+
}
19+
20+
if len(resource.Deployments) != 0 {
21+
for _, deployment := range resource.Deployments {
22+
containers := deployment.Spec.Template.Spec.Containers
23+
var images string
24+
for _, container := range containers {
25+
images += fmt.Sprintf("{container: \"%v\", image: \"%v\"}\n", container.Name, container.Image)
26+
}
27+
result = append(result, []string{"Deployment", deployment.Name, "image", images})
28+
}
29+
}
30+
31+
if len(resource.StatefulSets) != 0 {
32+
for _, s := range resource.StatefulSets {
33+
containers := s.Spec.Template.Spec.Containers
34+
var images string
35+
for _, container := range containers {
36+
images += fmt.Sprintf("{container: \"%v\", image: \"%v\"}\n", container.Name, container.Image)
37+
}
38+
result = append(result, []string{"StatefulSet", s.Name, "image", images})
39+
}
40+
}
41+
42+
if len(resource.DaemonSets) != 0 {
43+
for _, s := range resource.DaemonSets {
44+
containers := s.Spec.Template.Spec.Containers
45+
var images string
46+
for _, container := range containers {
47+
images += fmt.Sprintf("{container: \"%v\", image: \"%v\"}\n", container.Name, container.Image)
48+
}
49+
result = append(result, []string{"DaemonSet", s.Name, "image", images})
50+
}
51+
}
52+
53+
if len(resource.CronJobs) != 0 {
54+
for _, cronjob := range resource.CronJobs {
55+
containers := cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers
56+
var images string
57+
for _, container := range containers {
58+
images += fmt.Sprintf("{container: \"%v\", image: \"%v\"}\n", container.Name, container.Image)
59+
}
60+
result = append(result, []string{"CronJob", cronjob.Name, "image", images})
61+
}
62+
}
63+
64+
return result, nil
65+
}

0 commit comments

Comments
 (0)