-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a8639a1
commit 3cef8db
Showing
10 changed files
with
1,269 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
## kubectl-druid-plugin | ||
- Kubectl plugin to simplify operations on druid CR. | ||
|
||
## Prerequisite | ||
- Druid CRD defination to be installed. | ||
- A druid CR managed druid cluster running. | ||
- https://github.com/druid-io/druid-operator/tree/master/deploy/crds | ||
|
||
## Getting Started | ||
- NOTE: go version 1.15 | ||
- ```go build -o kubectl-druid``` | ||
- ```mv kubectl-druid /usr/local/bin``` | ||
- ```kubectl druid --help``` | ||
|
||
## Commands | ||
|
||
- List All Druid CR's in a k8s cluster | ||
``` | ||
kubectl druid list | ||
``` | ||
|
||
- List Druid CR's in a namespace | ||
``` | ||
kubectl druid list --namespace <namespace> | ||
``` | ||
|
||
- Get Druid Nodes's in a namespace for a specific cr | ||
``` | ||
kubectl druid get nodes --cr <cr>--namespace <namespace> | ||
``` | ||
|
||
- Scale Druid Replicas for a specific druid cr node in a namespace | ||
``` | ||
kubectl druid scale --cr <cr> --namespace <namespace> --node middlemanager --replicas 4 | ||
``` | ||
|
||
- Update Image for a specific druid CR node in namespace | ||
``` | ||
kubectl druid update --CR <CR> --image <image> --namespace <namespace> --node broker | ||
``` | ||
|
||
- Patch Operation of CR Flags | ||
``` | ||
kubectl druid patch --cr <cr> --namespace <namespace> --deleteOrphanPvc true | ||
kubectl druid patch --cr <cr> --namespace <namespace> --rollingDeploy true | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package cmd | ||
|
||
import ( | ||
"k8s.io/client-go/dynamic" | ||
"k8s.io/client-go/rest" | ||
"k8s.io/client-go/tools/clientcmd" | ||
) | ||
|
||
// newClient() shall return a dynamic client | ||
func newClient() *client { | ||
|
||
dynamicClient, err := dynamic.NewForConfig(newConfig()) | ||
if err != nil { | ||
panic(err.Error()) | ||
} | ||
|
||
return &client{dynamicClient} | ||
} | ||
|
||
// newConfig() shall return a config | ||
func newConfig() *rest.Config { | ||
|
||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() | ||
configOverrides := &clientcmd.ConfigOverrides{} | ||
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) | ||
|
||
config, err := kubeConfig.ClientConfig() | ||
if err != nil { | ||
panic(err.Error()) | ||
} | ||
return config | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
package cmd | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"sort" | ||
|
||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/client-go/dynamic" | ||
) | ||
|
||
// GVK for Druid CR | ||
var GVK = schema.GroupVersionResource{ | ||
Group: "druid.apache.org", | ||
Version: "v1alpha1", | ||
Resource: "druids", | ||
} | ||
|
||
// patchValue specifies a patch operation. | ||
type patchValue struct { | ||
Op string `json:"op"` | ||
Path string `json:"path"` | ||
Value interface{} `json:"value"` | ||
} | ||
|
||
// constructor for patchValue{} | ||
func NewPatchValue(op, path string, value interface{}) []byte { | ||
patchPayload := make([]patchValue, 1) | ||
|
||
patchPayload[0].Op = op | ||
patchPayload[0].Path = path | ||
patchPayload[0].Value = value | ||
|
||
bytes, _ := json.Marshal(patchPayload) | ||
return bytes | ||
} | ||
|
||
// dynamicInterface holds writers,reader and patcher interfaces | ||
type dynamicInterface interface { | ||
writers | ||
readers | ||
patcher | ||
} | ||
|
||
// readers interface | ||
type readers interface { | ||
listDruidCR(namespaces string) ([]string, error) | ||
getDruidNodeNames(namespaces, CR string) ([]string, error) | ||
} | ||
|
||
// writers interface | ||
type writers interface { | ||
writerDruidNodeSpecReplicas(nodeName, namespace, CR string, replica int64) (bool, error) | ||
writerDruidNodeImages(nodeName, namespace, CR, image string) (bool, error) | ||
} | ||
|
||
// patchers interface | ||
type patcher interface { | ||
patcherDruidDeleteOrphanPvc(namespace, CR string, value bool) (bool, error) | ||
patcherDruidRollingDeploy(namespace, CR string, value bool) (bool, error) | ||
} | ||
|
||
// client struct holds the dynamic client | ||
type client struct { | ||
dynamic.Interface | ||
} | ||
|
||
// initalize dynamicInterface | ||
var di dynamicInterface = client{newClient()} | ||
|
||
// getDruidNodeNames gets all the druid nodes in a namespace | ||
func (c client) getDruidNodeNames(namespaces, CR string) ([]string, error) { | ||
|
||
var err error | ||
|
||
druidNodeName, err := c.Resource(GVK).Namespace(namespaces).Get(context.TODO(), CR, v1.GetOptions{}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var names []string | ||
|
||
nameLists, _, _ := unstructured.NestedMap(druidNodeName.Object, "spec", "nodes") | ||
for nameList := range nameLists { | ||
names = append(names, nameList) | ||
sort.Strings(names) | ||
} | ||
|
||
return names, nil | ||
|
||
} | ||
|
||
// listDruidCR lists all the druid CR in a namespace or all namespaces | ||
func (c client) listDruidCR(namespaces string) ([]string, error) { | ||
|
||
var err error | ||
|
||
druidList, err := c.Resource(GVK).Namespace(namespaces).List(context.TODO(), v1.ListOptions{}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var names []string | ||
for _, d := range druidList.Items { | ||
names = append(names, d.GetName()) | ||
sort.Strings(names) | ||
} | ||
|
||
return names, nil | ||
|
||
} | ||
|
||
// writerNodeSpecReplicas writer nodespec replica | ||
func (c client) writerDruidNodeSpecReplicas(nodeName, namespace, CR string, replica int64) (bool, error) { | ||
var err error | ||
cr, err := c.Resource(GVK).Namespace(namespace).Get(context.TODO(), CR, v1.GetOptions{}) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
if err := unstructured.SetNestedField(cr.Object, int64(replica), "spec", "nodes", nodeName, "replicas"); err != nil { | ||
return false, err | ||
} | ||
|
||
_, err = c.Resource(GVK).Namespace(namespace).Update(context.TODO(), cr, v1.UpdateOptions{}) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
return true, nil | ||
} | ||
|
||
// writerDruidNodeImages writers updates nodes images | ||
func (c client) writerDruidNodeImages(nodeName, namespace, CR, image string) (bool, error) { | ||
var err error | ||
|
||
cr, err := c.Resource(GVK).Namespace(namespace).Get(context.TODO(), CR, v1.GetOptions{}) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
if err := unstructured.SetNestedField(cr.Object, image, "spec", "nodes", nodeName, "image"); err != nil { | ||
return false, err | ||
} | ||
|
||
_, err = c.Resource(GVK).Namespace(namespace).Update(context.TODO(), cr, v1.UpdateOptions{}) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
return true, nil | ||
} | ||
|
||
// patcherDruidDeleteOrphanPvc patches DeleteOrphanPvc flag | ||
func (c client) patcherDruidDeleteOrphanPvc(namespace, CR string, value bool) (bool, error) { | ||
var err error | ||
|
||
patchBytes := NewPatchValue("replace", "/spec/deleteOrphanPvc", value) | ||
|
||
_, err = c.Resource(GVK).Namespace(namespace).Patch(context.TODO(), CR, types.JSONPatchType, patchBytes, v1.PatchOptions{}) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
return true, nil | ||
} | ||
|
||
// patcherDruidRollingDeploy patches rollingDeploy flag | ||
func (c client) patcherDruidRollingDeploy(namespace, CR string, value bool) (bool, error) { | ||
var err error | ||
|
||
patchBytes := NewPatchValue("replace", "/spec/rollingDeploy", value) | ||
|
||
_, err = c.Resource(GVK).Namespace(namespace).Patch(context.TODO(), CR, types.JSONPatchType, patchBytes, v1.PatchOptions{}) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
return true, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package cmd | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io" | ||
"strconv" | ||
|
||
"github.com/spf13/cobra" | ||
"k8s.io/cli-runtime/pkg/genericclioptions" | ||
) | ||
|
||
type druidPatcherCmd struct { | ||
out io.Writer | ||
} | ||
|
||
func druidCRPatcher(streams genericclioptions.IOStreams) *cobra.Command { | ||
druidCmdList := &druidListCmd{ | ||
out: streams.Out, | ||
} | ||
|
||
var deleteOrphanPvc, rollingDeploy, namespace, cr string | ||
cmd := &cobra.Command{ | ||
Use: "patch", | ||
Short: "patches druid CR", | ||
SilenceUsage: true, | ||
RunE: func(c *cobra.Command, args []string) error { | ||
if len(args) != 0 { | ||
return errors.New("this command does not accept arguments") | ||
} | ||
return druidCmdList.druidCRPatcherRun(namespace, cr, deleteOrphanPvc, rollingDeploy) | ||
}, | ||
} | ||
|
||
f := cmd.Flags() | ||
f.StringVar(&namespace, "namespace", "", "namespace of druid CR") | ||
f.StringVar(&cr, "cr", "", "name of the druid CR") | ||
f.StringVar(&deleteOrphanPvc, "deleteOrphanPvc", "", "deleteOrphanPvc is a bool, enabling to true will lead to delete of orphan pvc") | ||
f.StringVar(&rollingDeploy, "rollingDeploy", "", "rollingDeploy is a bool, enabling to true will lead to sequential rolling upgrades") | ||
|
||
return cmd | ||
} | ||
|
||
func (sv *druidListCmd) druidCRPatcherRun(namespace, CR, deleteOrphanPvc, rollingDeploy string) error { | ||
|
||
if deleteOrphanPvc != "" { | ||
b, _ := strconv.ParseBool(deleteOrphanPvc) | ||
patcherResult, err := di.patcherDruidDeleteOrphanPvc(namespace, CR, b) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if patcherResult { | ||
_, err := fmt.Fprintf(sv.out, "Druid CR [%s],successfully patched DeleteOrphanPvc to [%v] in Namespace [%s]\n", CR, deleteOrphanPvc, namespace) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} else if rollingDeploy != "" { | ||
b, _ := strconv.ParseBool(rollingDeploy) | ||
patcherResult, err := di.patcherDruidRollingDeploy(namespace, CR, b) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if patcherResult { | ||
_, err := fmt.Fprintf(sv.out, "Druid CR [%s],successfully patched rollingDeploy to [%v] in Namespace [%s]\n", CR, rollingDeploy, namespace) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
"k8s.io/cli-runtime/pkg/genericclioptions" | ||
) | ||
|
||
var rootCmd = &cobra.Command{ | ||
Use: "druid", | ||
Long: "kubectl druid plugin", | ||
SilenceUsage: true, | ||
} | ||
|
||
func NewCmdDruidPlugin(streams genericclioptions.IOStreams) *cobra.Command { | ||
rootCmd.AddCommand(druidCRList(streams)) | ||
rootCmd.AddCommand(druidCRGet(streams)) | ||
rootCmd.AddCommand(druidCRWriterNodeSpecReplicas(streams)) | ||
rootCmd.AddCommand(druidCRWriterUpdates(streams)) | ||
rootCmd.AddCommand(druidCRPatcher(streams)) | ||
return rootCmd | ||
} |
Oops, something went wrong.