Skip to content

Commit

Permalink
feat: kubernetes
Browse files Browse the repository at this point in the history
  • Loading branch information
d1nfinite committed Sep 30, 2022
1 parent 90390dc commit 98bc6f2
Show file tree
Hide file tree
Showing 10 changed files with 753 additions and 210 deletions.
15 changes: 10 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ go 1.14

require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2
github.com/opencontainers/runtime-spec v1.0.2
github.com/sirupsen/logrus v1.4.2
github.com/spf13/cobra v1.3.0
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.4.0
github.com/spf13/pflag v1.0.5
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
k8s.io/api v0.25.2
k8s.io/apimachinery v0.25.2
k8s.io/cli-runtime v0.25.2
k8s.io/client-go v0.25.2
)
426 changes: 221 additions & 205 deletions go.sum

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions go/cmd/cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package cmd

import (
"context"

api "github.com/chaitin/libveinmind/go"
"github.com/chaitin/libveinmind/go/plugin"
)

// ScanClusters scans cluster list.
func ScanClusters(
ctx context.Context, rang plugin.ExecRange,
cluster []api.Cluster, opts ...plugin.ExecOption,
) error {
iter, err := plugin.IterateTyped(rang, "cluster")
if err != nil {
return err
}
return Scan(ctx, iter, cluster,
plugin.WithExecOptions(opts...))
}

// ClusterHandler is the handler for specified cluster.
type ClusterHandler func(*Command, api.Cluster) error

// MapClusterCommand issues defaultIndex.MapClusterCommand.
func MapClusterCommand(
c *Command, f ClusterHandler,
) *Command {
return defaultIndex.MapClusterCommand(c, f)
}

// MapClusterCommand attempts to create a cluster command.
//
// The command will attempt to initialize the cluster object
// from specified mode with flags, and then invoke the function
// specified by caller.
func (idx *Index) MapClusterCommand(
c *Command, f ClusterHandler,
) *Command {
return idx.MapModeCommand(c, "cluster", struct{}{}, func(
c *Command, _ []string, root interface{},
) error {
r, ok := root.(api.Cluster)
if !ok {
return IncompatibleMode()
}
return f(c, r)
})
}
68 changes: 68 additions & 0 deletions go/cmd/kubernetes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cmd

import (
"github.com/spf13/pflag"

"github.com/chaitin/libveinmind/go/kubernetes"
"github.com/chaitin/libveinmind/go/pkg/pflagext"
"github.com/chaitin/libveinmind/go/plugin"
)

type kubernetesRoot struct {
k *kubernetes.Kubernetes
}

func (r kubernetesRoot) ID() interface{} {
return r.k
}

func (r kubernetesRoot) Mode() string {
return "kubernetes"
}

func (r kubernetesRoot) Options() plugin.ExecOption {
return plugin.WithExecOptions(plugin.WithPrependArgs(
"--kube-config", r.k.ConfigPath()),
plugin.WithPrependArgs(
"--namespace", r.k.CurrentNamespace()))
}

var kubernetesFlags []kubernetes.NewOption

type kubernetesMode struct {
}

func (kubernetesMode) Name() string {
return "kubernetes"
}

func (kubernetesMode) AddFlags(fset *pflag.FlagSet) {
pflagext.StringVarF(fset, func(path string) error {
kubernetesFlags = append(kubernetesFlags,
kubernetes.WithKubeConfig(path))
return nil
}, "kube-config",
`flag "--kube-config" specified kube config`)
pflagext.StringVarF(fset, func(namespace string) error {
kubernetesFlags = append(kubernetesFlags,
kubernetes.WithNamespace(namespace))
return nil
}, "namespace",
`flag "--namespace" specified namespace`)
}

func (kubernetesMode) Invoke(c *Command, args []string, m ModeHandler) error {
k, err := kubernetes.New(kubernetesFlags...)
if err != nil {
return err
}
defer func() { _ = k.Close() }()
return m(c, args, k)
}

func init() {
RegisterPartition(func(k *kubernetes.Kubernetes) Root {
return kubernetesRoot{k: k}
})
RegisterMode(&kubernetesMode{})
}
94 changes: 94 additions & 0 deletions go/kubernetes/kind.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package kubernetes

type NamespaceKind string
type ClusterKind string

const (
Deployments NamespaceKind = "deployments"
ReplicaSets NamespaceKind = "replicasets"
ReplicationControllers NamespaceKind = "replicationcontrollers"
StatefulSets NamespaceKind = "statefulsets"
DaemonSets NamespaceKind = "daemonsets"
CronJobs NamespaceKind = "cronjobs"
Services NamespaceKind = "services"
Jobs NamespaceKind = "jobs"
Pods NamespaceKind = "pods"
ConfigMaps NamespaceKind = "configmaps"
Roles NamespaceKind = "roles"
RoleBindings NamespaceKind = "rolebindings"
NetworkPolicys NamespaceKind = "networkpolicies"
Ingresss NamespaceKind = "ingresses"
ResourceQuotas NamespaceKind = "resourcequotas"
LimitRanges NamespaceKind = "limitranges"
)

const (
ComponentStatus ClusterKind = "componentstatuses"
Nodes ClusterKind = "nodes"
Namespaces ClusterKind = "namespaces"
PersistentVolumes ClusterKind = "persistentvolumes"
ClusterRoles ClusterKind = "clusterroles"
ClusterRoleBindings ClusterKind = "clusterrolebindings"
PodSecurityPolicies ClusterKind = "podsecuritypolicies"
)

func (k NamespaceKind) String() string {
return string(k)
}

func (k ClusterKind) String() string {
return string(k)
}

func GetNamespaceKinds() []NamespaceKind {
return []NamespaceKind{
Deployments,
ReplicaSets,
ReplicationControllers,
StatefulSets,
DaemonSets,
CronJobs,
Services,
Jobs,
Pods,
ConfigMaps,
Roles,
RoleBindings,
NetworkPolicys,
Ingresss,
ResourceQuotas,
LimitRanges,
}
}

func GetClusterKinds() []ClusterKind {
return []ClusterKind{
ComponentStatus,
Nodes,
Namespaces,
PersistentVolumes,
ClusterRoles,
ClusterRoleBindings,
PodSecurityPolicies,
}
}

func IsNamespaceKind(kind string) bool {
for _, namespaceKind := range GetNamespaceKinds() {
if namespaceKind.String() == kind {
return true
}
}

return false
}

func IsClusterKind(kind string) bool {
for _, clusterKind := range GetClusterKinds() {
if clusterKind.String() == kind {
return true
}
}

return false
}
136 changes: 136 additions & 0 deletions go/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package kubernetes

import (
"context"
"os"

"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/dynamic"

api "github.com/chaitin/libveinmind/go"
)

type Kubernetes struct {
// kubernetes cluster namespace
namespace string

// kube kubeConfig path for cluster
kubeConfig string

// dynamicClient reference dynamic.Interface
// return data use map[string]interface{} format
dynamicClient dynamic.Interface

// restMapper reference mete.RESTMapper
// used to fetch schema.GroupVersionResource from kind
restMapper meta.RESTMapper
}

type NewOption func(kubernetes *Kubernetes) error

func WithNamespace(namespace string) NewOption {
return func(kubernetes *Kubernetes) error {
kubernetes.namespace = namespace
return nil
}
}

func WithKubeConfig(path string) NewOption {
return func(kubernetes *Kubernetes) error {
kubernetes.kubeConfig = path
return nil
}
}

func New(options ...NewOption) (*Kubernetes, error) {
k := new(Kubernetes)

for _, opt := range options {
err := opt(k)
if err != nil {
continue
}
}

if k.kubeConfig == "" {
if os.Getenv("KUBECONFIG") == "" {
return nil, errors.New("kubernetes: can't find kube config path")
} else {
k.kubeConfig = os.Getenv("KUBECONFIG")
}
}

if k.namespace == "" {
k.namespace = "default"
}

// init dynamic client config
config := genericclioptions.NewConfigFlags(true)
*config.KubeConfig = k.kubeConfig
configLoader := config.ToRawKubeConfigLoader()
restConfig, err := configLoader.ClientConfig()
if err != nil {
return nil, errors.Wrap(err, "kubernetes: can't get rest config")
}

// init dynamic client
dynamicClient, err := dynamic.NewForConfig(restConfig)
if err != nil {
return nil, errors.Wrap(err, "kubernetes: can't init dynamic client")
}
k.dynamicClient = dynamicClient

// init rest mapper
mapper, err := config.ToRESTMapper()
if err != nil {
return nil, errors.Wrap(err, "kubernetes: can't init rest mapper")
}
k.restMapper = mapper

return k, nil
}

func (k *Kubernetes) ListNamespaces() ([]string, error) {
namespaceResource, err := k.Resource(Namespaces.String())
if err != nil {
return nil, err
}

return namespaceResource.List(context.Background())
}

func (k *Kubernetes) CurrentNamespace() string {
return k.namespace
}

func (k *Kubernetes) ConfigPath() string {
return k.kubeConfig
}

func (k *Kubernetes) Namespace(namespace string) api.Cluster {
k.namespace = namespace
return k
}

func (k *Kubernetes) Resource(kind string) (api.ClusterResource, error) {
gvr, err := k.restMapper.ResourceFor(schema.GroupVersionResource{Resource: kind})
if err != nil {
return nil, err
}

// cluster resource can't use namespace (namespaced is false)
if IsClusterKind(kind) {
return Resource{kind, k.dynamicClient.Resource(gvr)}, nil
} else if IsNamespaceKind(kind) {
return Resource{kind, k.dynamicClient.Resource(gvr).Namespace(k.namespace)}, nil
} else {
return nil, errors.New("kubernetes: not support resource kind for cluster")
}
}

func (k *Kubernetes) Close() error {
return nil
}
Loading

0 comments on commit 98bc6f2

Please sign in to comment.