climc: k8s resource cli

This commit is contained in:
Zexi Li
2018-08-20 11:45:10 +08:00
parent 8b752ff2de
commit 9cdbeb943b
17 changed files with 646 additions and 14 deletions
+27
View File
@@ -0,0 +1,27 @@
package k8s
import (
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules/k8s"
)
func initConfigMap() {
cmdN := func(suffix string) string {
return resourceCmdN("configmap", suffix)
}
type listOpt struct {
namespaceListOptions
baseListOptions
}
R(&listOpt{}, cmdN("list"), "List k8s configmap", func(s *mcclient.ClientSession, args *listOpt) error {
params := fetchNamespaceParams(args.namespaceListOptions)
params.Update(fetchPagingParams(args.baseListOptions))
ret, err := k8s.ConfigMaps.ListInContexts(s, params, args.ClusterContext())
if err != nil {
return err
}
printList(ret, k8s.ConfigMaps.GetColumns(s))
return nil
})
}
+145
View File
@@ -0,0 +1,145 @@
package k8s
import (
"fmt"
"strconv"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/util/sets"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules/k8s"
)
func initDeployment() {
cmdN := func(suffix string) string {
return resourceCmdN("deployment", suffix)
}
type listOpt struct {
namespaceListOptions
baseListOptions
}
R(&listOpt{}, cmdN("list"), "List k8s deployment", func(s *mcclient.ClientSession, args *listOpt) error {
params := fetchNamespaceParams(args.namespaceListOptions)
params.Update(fetchPagingParams(args.baseListOptions))
ret, err := k8s.Deployments.ListInContexts(s, params, args.ClusterContext())
if err != nil {
return err
}
printList(ret, k8s.Deployments.GetColumns(s))
return nil
})
type createOpt struct {
namespaceOptions
NAME string `help:"Name of deployment"`
Image string `help:"The image for the container to run"`
Replicas int64 `help:"Number of replicas for pods in this deployment"`
RunAsPrivileged bool `help:"Whether to run the container as privileged user"`
Labels string `help:"Comma separated labels to apply to the pod(s)"`
Env []string `help:"Environment variables to set in container"`
Port []string `help:"Port for the service that is created, format is <protocol>:<service_port>:<container_port> e.g. tcp:80:3000"`
}
R(&createOpt{}, cmdN("create"), "Create deployment resource", func(s *mcclient.ClientSession, args *createOpt) error {
params := jsonutils.NewDict()
if len(args.Image) == 0 {
return fmt.Errorf("Image must provided")
}
params.Add(jsonutils.NewString(args.NAME), "name")
params.Add(jsonutils.NewString(args.Image), "containerImage")
if args.Namespace != "" {
params.Add(jsonutils.NewString(args.Namespace), "namespace")
}
if args.Replicas > 1 {
params.Add(jsonutils.NewInt(args.Replicas), "replicas")
}
if args.RunAsPrivileged {
params.Add(jsonutils.JSONTrue, "runAsPrivileged")
}
if len(args.Port) != 0 {
portMappings, err := parsePortMappings(args.Port)
if err != nil {
return err
}
params.Add(portMappings, "portMappings")
}
ret, err := k8s.Deployments.CreateInContexts(s, params, args.ClusterContext())
if err != nil {
return err
}
printObject(ret)
return nil
})
type getOpt struct {
resourceGetOptions
}
R(&getOpt{}, cmdN("show"), "Get deployment details", func(s *mcclient.ClientSession, args *getOpt) error {
id := args.NAME
params := jsonutils.NewDict()
if args.Namespace != "" {
params.Add(jsonutils.NewString(args.Namespace), "namespace")
}
ret, err := k8s.Deployments.GetInContexts(s, id, params, args.ClusterContext())
if err != nil {
return err
}
printObjectYAML(ret)
return nil
})
}
type portMapping struct {
Port int32 `json:"port"`
TargetPort int32 `json:"targetPort"`
Protocol string `json:"protocol"`
}
func parsePortMapping(port string) (*portMapping, error) {
if len(port) == 0 {
return nil, fmt.Errorf("empty port mapping desc string")
}
parts := strings.Split(port, ":")
mapping := &portMapping{}
for _, part := range parts {
if sets.NewString("tcp", "udp").Has(strings.ToLower(part)) {
mapping.Protocol = strings.ToUpper(part)
}
if port, err := strconv.Atoi(part); err != nil {
continue
} else {
if mapping.Port == 0 {
mapping.Port = int32(port)
} else {
mapping.TargetPort = int32(port)
}
}
}
if mapping.Protocol == "" {
mapping.Protocol = "TCP"
}
if mapping.Port <= 0 {
return nil, fmt.Errorf("Service port not provided")
}
if mapping.TargetPort < 0 {
return nil, fmt.Errorf("Container invalid targetPort %d", mapping.TargetPort)
}
if mapping.TargetPort == 0 {
mapping.TargetPort = mapping.Port
}
return mapping, nil
}
func parsePortMappings(ports []string) (*jsonutils.JSONArray, error) {
ret := jsonutils.NewArray()
for _, port := range ports {
mapping, err := parsePortMapping(port)
if err != nil {
return nil, fmt.Errorf("Port %q error: %v", port, err)
}
ret.Add(jsonutils.Marshal(mapping))
}
return ret, nil
}
+64 -3
View File
@@ -12,24 +12,81 @@ import (
)
func init() {
// cluster resources
initCluster()
initNode()
// helm resources
initRepo()
// kubernetes original resources
initRaw()
initConfigMap()
initDeployment()
initPod()
initService()
}
type BaseListOptions shell.BaseListOptions
type clusterBaseOptions struct {
Cluster string `default:"$K8S_CLUSTER" help:"Kubernetes cluster name"`
Cluster string `default:"$K8S_CLUSTER|default" help:"Kubernetes cluster name"`
}
func (o clusterBaseOptions) ClusterContext() []modules.ManagerContext {
return []modules.ManagerContext{clusterContext(o.Cluster)}
}
type k8sBaseListOptions struct {
type baseListOptions struct {
Limit int `default:"20" help:"Page limit"`
Offset int `default:"0" help:"page offset"`
}
func fetchPagingParams(opt baseListOptions) *jsonutils.JSONDict {
params := jsonutils.NewDict()
if opt.Limit > 0 {
params.Add(jsonutils.NewInt(int64(opt.Limit)), "limit")
}
if opt.Offset > 0 {
params.Add(jsonutils.NewInt(int64(opt.Offset)), "offset")
}
return params
}
type namespaceListOptions struct {
namespaceOptions
AllNamespace bool `help:"Show resource in all namespace"`
}
type namespaceOptions struct {
clusterBaseOptions
Limit int `default:"20" help:"Page limit"`
Namespace string `help:"Namespace of this resource"`
}
type resourceGetOptions struct {
clusterBaseOptions
Namespace string `help:"Namespace of this resource"`
NAME string `help:"Name ident of the resource"`
}
func (o resourceGetOptions) ToJSON() *jsonutils.JSONDict {
params := jsonutils.NewDict()
if o.Namespace != "" {
params.Add(jsonutils.NewString(o.Namespace), "namespace")
}
return params
}
func fetchNamespaceParams(opt namespaceListOptions) *jsonutils.JSONDict {
params := jsonutils.NewDict()
if opt.AllNamespace {
params.Add(jsonutils.JSONTrue, "all_namespace")
return params
}
if opt.Namespace != "" {
params.Add(jsonutils.NewString(opt.Namespace), "namespace")
}
return params
}
var (
@@ -53,3 +110,7 @@ func clusterContext(clusterId string) modules.ManagerContext {
InstanceId: clusterId,
}
}
func printObjectYAML(obj jsonutils.JSONObject) {
fmt.Println(obj.YAMLString())
}
+18 -2
View File
@@ -13,10 +13,26 @@ func initPod() {
}
type listOpt struct {
k8sBaseListOptions
namespaceListOptions
baseListOptions
}
R(&listOpt{}, cmdN("list"), "List k8s pod", func(s *mcclient.ClientSession, args *listOpt) error {
ret, err := k8s.Pods.ListInContexts(s, nil, args.ClusterContext())
params := fetchNamespaceParams(args.namespaceListOptions)
params.Update(fetchPagingParams(args.baseListOptions))
ret, err := k8s.Pods.ListInContexts(s, params, args.ClusterContext())
if err != nil {
return err
}
printList(ret, k8s.Pods.GetColumns(s))
return nil
})
type deleteOpt struct {
resourceGetOptions
}
R(&deleteOpt{}, cmdN("delete"), "Delete pod", func(s *mcclient.ClientSession, args *deleteOpt) error {
id := args.NAME
ret, err := k8s.Pods.DeleteInContexts(s, id, args.ToJSON(), args.ClusterContext())
if err != nil {
return err
}
+35
View File
@@ -0,0 +1,35 @@
package k8s
import (
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules/k8s"
)
type rawGetOpt struct {
namespaceOptions
KIND string `help:"resource kind"`
NAME string `help:"instance name"`
}
type rawDeleteOpt struct {
rawGetOpt
}
func initRaw() {
R(&rawGetOpt{}, "k8s-get", "Get k8s resource instance raw info", func(s *mcclient.ClientSession, args *rawGetOpt) error {
obj, err := k8s.RawResource.Get(s, args.KIND, args.Namespace, args.NAME, nil, args.ClusterContext())
if err != nil {
return err
}
printObjectYAML(obj)
return nil
})
R(&rawDeleteOpt{}, "k8s-delete", "Delete k8s resource instance", func(s *mcclient.ClientSession, args *rawDeleteOpt) error {
err := k8s.RawResource.Delete(s, args.KIND, args.Namespace, args.NAME, nil, args.ClusterContext())
if err != nil {
return err
}
return nil
})
}
+106
View File
@@ -0,0 +1,106 @@
package k8s
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules/k8s"
)
func initRepo() {
cmdN := func(suffix string) string {
return resourceCmdN("repo", suffix)
}
type listOpt struct {
BaseListOptions
}
R(&listOpt{}, cmdN("list"), "List k8s global helm repos", func(s *mcclient.ClientSession, args *listOpt) error {
params := FetchPagingParams(args.BaseListOptions)
result, err := k8s.Repos.List(s, params)
if err != nil {
return err
}
printList(result, k8s.Repos.GetColumns(s))
return nil
})
type getOpt struct {
NAME string `help:"ID or name of the repo"`
}
R(&getOpt{}, cmdN("show"), "Show details fo a repo", func(s *mcclient.ClientSession, args *getOpt) error {
repo, err := k8s.Repos.Get(s, args.NAME, nil)
if err != nil {
return err
}
printObject(repo)
return nil
})
type createOpt struct {
getOpt
URL string `help:"Repository url"`
Public bool `help:"Make repository public"`
}
R(&createOpt{}, cmdN("create"), "Add repository", func(s *mcclient.ClientSession, args *createOpt) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.NAME), "name")
params.Add(jsonutils.NewString(args.URL), "url")
if args.Public {
params.Add(jsonutils.JSONTrue, "is_public")
}
repo, err := k8s.Repos.Create(s, params)
if err != nil {
return err
}
printObject(repo)
return nil
})
type updateOpt struct {
getOpt
Name string `help:"Repository name to change"`
Url string `help:"Repository url to change"`
}
R(&updateOpt{}, cmdN("update"), "Update helm repository", func(s *mcclient.ClientSession, args *updateOpt) error {
params := jsonutils.NewDict()
if args.Name != "" {
params.Add(jsonutils.NewString(args.Name), "name")
}
if args.Url != "" {
params.Add(jsonutils.NewString(args.Url), "url")
}
repo, err := k8s.Repos.Update(s, args.NAME, params)
if err != nil {
return err
}
printObject(repo)
return nil
})
R(&getOpt{}, cmdN("private"), "Make repository private", func(s *mcclient.ClientSession, args *getOpt) error {
repo, err := k8s.Repos.PerformAction(s, args.NAME, "private", nil)
if err != nil {
return err
}
printObject(repo)
return nil
})
R(&getOpt{}, cmdN("public"), "Make repository public", func(s *mcclient.ClientSession, args *getOpt) error {
repo, err := k8s.Repos.PerformAction(s, args.NAME, "public", nil)
if err != nil {
return err
}
printObject(repo)
return nil
})
R(&getOpt{}, cmdN("delete"), "Delete a repository", func(s *mcclient.ClientSession, args *getOpt) error {
repo, err := k8s.Repos.Delete(s, args.NAME, nil)
if err != nil {
return err
}
printObject(repo)
return nil
})
}
+27
View File
@@ -0,0 +1,27 @@
package k8s
import (
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules/k8s"
)
func initService() {
cmdN := func(suffix string) string {
return resourceCmdN("service", suffix)
}
type listOpt struct {
namespaceListOptions
baseListOptions
}
R(&listOpt{}, cmdN("list"), "List k8s service", func(s *mcclient.ClientSession, args *listOpt) error {
params := fetchNamespaceParams(args.namespaceListOptions)
params.Update(fetchPagingParams(args.baseListOptions))
ret, err := k8s.Services.ListInContexts(s, params, args.ClusterContext())
if err != nil {
return err
}
printList(ret, k8s.Services.GetColumns(s))
return nil
})
}
+45 -2
View File
@@ -1,13 +1,56 @@
package k8s
import (
"yunion.io/x/pkg/util/sets"
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
func NewManager(keyword, keywordPlural string, columns, adminColumns []string) *modules.ResourceManager {
func NewManager(keyword, keywordPlural string, columns, adminColumns *Columns) *modules.ResourceManager {
return &modules.ResourceManager{
BaseManager: *modules.NewBaseManager("k8s", "", "", columns, adminColumns),
BaseManager: *modules.NewBaseManager("k8s", "", "", columns.Array(), adminColumns.Array()),
Keyword: keyword,
KeywordPlural: keywordPlural,
}
}
type Columns struct {
cols []string
}
func NewColumns(cols ...string) *Columns {
c := &Columns{cols: make([]string, 0)}
return c.Add(cols...)
}
func (c *Columns) Add(cols ...string) *Columns {
for _, col := range cols {
c.add(col)
}
return c
}
func (c *Columns) add(col string) *Columns {
src := sets.NewString(c.Array()...)
if src.Has(col) {
return c
}
c.cols = append(c.cols, col)
return c
}
func (c Columns) Array() []string {
return c.cols
}
func NewNamespaceCols(col ...string) *Columns {
return NewColumns("name", "namespace").Add(col...)
}
func NewClusterCols(col ...string) *Columns {
return NewColumns("cluster").Add(col...)
}
func NewResourceCols(col ...string) *Columns {
return NewColumns("name", "id").Add(col...)
}
+2 -2
View File
@@ -10,7 +10,7 @@ var (
func init() {
Clusters = NewManager("cluster", "clusters",
[]string{"id", "name", "mode", "k8s_version", "status", "api_endpoint"},
[]string{})
NewResourceCols("mode", "k8s_version", "status", "api_endpoint"),
NewColumns())
modules.Register(Clusters)
}
+20
View File
@@ -0,0 +1,20 @@
package k8s
import (
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
var ConfigMaps *ConfigMapManager
type ConfigMapManager struct {
modules.ResourceManager
}
func init() {
ConfigMaps = &ConfigMapManager{
ResourceManager: *NewManager(
"configmap", "configmaps",
NewNamespaceCols(),
NewClusterCols())}
modules.Register(ConfigMaps)
}
+20
View File
@@ -0,0 +1,20 @@
package k8s
import (
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
var Deployments *DeploymentManager
type DeploymentManager struct {
modules.ResourceManager
}
func init() {
Deployments = &DeploymentManager{
ResourceManager: *NewManager(
"deployment", "deployments",
NewNamespaceCols("labels"),
NewClusterCols())}
modules.Register(Deployments)
}
+2 -2
View File
@@ -10,7 +10,7 @@ var (
func init() {
Logs = NewManager("kube_event", "kube_events",
[]string{"id", "ops_time", "obj_id", "obj_type", "obj_name", "user", "user_id", "tenant", "tenant_id", "owner_tenant_id", "action", "notes"},
[]string{})
NewColumns("id", "ops_time", "obj_id", "obj_type", "obj_name", "user", "user_id", "tenant", "tenant_id", "owner_tenant_id", "action", "notes"),
NewColumns())
modules.Register(Logs)
}
+17
View File
@@ -0,0 +1,17 @@
package k8s
import (
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
var (
Repos *modules.ResourceManager
)
func init() {
Repos = NewManager("repo", "repos",
NewResourceCols("url", "is_public", "source"),
NewColumns(),
)
modules.Register(Repos)
}
+1 -1
View File
@@ -9,6 +9,6 @@ var (
)
func init() {
Nodes = NewManager("node", "nodes", []string{"id", "name", "cluster", "roles", "address", "status"}, []string{})
Nodes = NewManager("node", "nodes", NewResourceCols("cluster", "roles", "address", "status"), NewColumns())
modules.Register(Nodes)
}
+2 -2
View File
@@ -14,7 +14,7 @@ func init() {
Pods = &PodManager{
ResourceManager: *NewManager(
"pod", "pods",
[]string{"id", "name", "cluster", "roles", "address", "status"},
[]string{})}
NewNamespaceCols("podIP", "status", "restartCount", "labels"),
NewClusterCols("nodeName"))}
modules.Register(Pods)
}
+95
View File
@@ -0,0 +1,95 @@
package k8s
import (
"fmt"
"net/url"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
var RawResource *RawResourceManager
func init() {
RawResource = &RawResourceManager{
serviceType: "k8s",
}
}
type rawResourceContext struct {
kind string
name string
query jsonutils.JSONObject
ctxs []modules.ManagerContext
}
func newRawResourceContext(kind, namespace, name string, query jsonutils.JSONObject, ctxs []modules.ManagerContext) *rawResourceContext {
nsQuery := getNamespaceQuery(namespace)
if query != nil {
nsQuery.Update(query)
}
ctx := &rawResourceContext{kind: kind, name: name, query: nsQuery, ctxs: ctxs}
return ctx
}
func (ctx rawResourceContext) contextPath() string {
segs := make([]string, 0)
ctxs := ctx.ctxs
if ctxs != nil && len(ctxs) > 0 {
for _, c := range ctxs {
segs = append(segs, c.InstanceManager.KeyString())
if len(c.InstanceId) > 0 {
segs = append(segs, url.PathEscape(c.InstanceId))
}
}
}
segs = append(segs, "_raw", ctx.kind, ctx.name)
path := fmt.Sprintf("/%s", strings.Join(segs, "/"))
if ctx.query != nil {
qs := ctx.query.QueryString()
if len(qs) > 0 {
path = fmt.Sprintf("%s?%s", path, qs)
}
}
return path
}
type RawResourceManager struct {
serviceType string
}
func (m *RawResourceManager) request(s *mcclient.ClientSession, method string, path string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
_, ret, err := s.JSONRequest(m.serviceType, "", method, path, nil, body)
return ret, err
}
func getNamespaceQuery(namespace string) *jsonutils.JSONDict {
query := jsonutils.NewDict()
if namespace != "" {
query.Set("namespace", jsonutils.NewString(namespace))
}
return query
}
func (m *RawResourceManager) Get(s *mcclient.ClientSession, kind string, namespace string, name string, query jsonutils.JSONObject, ctxs []modules.ManagerContext) (jsonutils.JSONObject, error) {
ctx := newRawResourceContext(kind, namespace, name, query, ctxs)
return m.request(s, "GET", ctx.contextPath(), nil)
}
func (m *RawResourceManager) Put(s *mcclient.ClientSession, kind string, namespace string, name string, body jsonutils.JSONObject, ctxs []modules.ManagerContext) error {
rawBytes := body.String()
newBody := jsonutils.NewDict()
newBody.Add(jsonutils.NewString(rawBytes), "raw")
ctx := newRawResourceContext(kind, namespace, name, nil, ctxs)
_, err := m.request(s, "PUT", ctx.contextPath(), newBody)
return err
}
func (m *RawResourceManager) Delete(s *mcclient.ClientSession, kind string, namespace string, name string, query jsonutils.JSONObject, ctxs []modules.ManagerContext) error {
ctx := newRawResourceContext(kind, namespace, name, query, ctxs)
_, err := m.request(s, "DELETE", ctx.contextPath(), nil)
return err
}
+20
View File
@@ -0,0 +1,20 @@
package k8s
import (
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
var Services *ServiceManager
type ServiceManager struct {
modules.ResourceManager
}
func init() {
Services = &ServiceManager{
ResourceManager: *NewManager(
"service", "services",
NewNamespaceCols("clusterIP", "selector", "internalEndpoint", "externalEndpoints"),
NewClusterCols())}
modules.Register(Services)
}