如何使用golang在docker内或集群内实现kubectl apply -f
或kubectl delete -f
命令?在网上找了一圈没找到,自己研究了下。
package main
import (
"bytes"
"context"
"errors"
"flag"
"fmt"
"io/ioutil"
appsv1 "k8s.io/api/apps/v1"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"strings"
)
const (
Create = "create"
Delete = "delete"
)
type yamlItem struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
MetaData interface{} `json:"metadata"`
}
var RestClient *kubernetes.Clientset
func Init() {
var kubeConfig = "./config"
config, err := clientcmd.BuildConfigFromFlags("192.168.101.4:443", kubeConfig)
if err != nil {
panic(err.Error())
}
RestClient, err = kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
fmt.Printf("RestClient: %v", RestClient)
}
func HandleStatefulSet(config *appsv1.StatefulSet, operation string) error {
ns := "default"
if config.Namespace != "" {
ns = config.Namespace
}
var err error
client := RestClient.AppsV1().StatefulSets(ns)
switch operation {
case Create:
_, err = client.Create(context.Background(), config, metav1.CreateOptions{})
case Delete:
err = client.Delete(context.Background(), config.Name, metav1.DeleteOptions{})
}
if err == nil {
fmt.Printf("%s service name: %s, namespace: %s", operation, config.Name, ns)
}
return err
}
func HandleService(config *apiv1.Service, operation string) error {
ns := "default"
if config.Namespace != "" {
ns = config.Namespace
}
var err error
client := RestClient.CoreV1().Services(ns)
switch operation {
case Create:
_, err = client.Create(context.Background(), config, metav1.CreateOptions{})
case Delete:
err = client.Delete(context.Background(), config.Name, metav1.DeleteOptions{})
}
if err == nil {
fmt.Printf("%s service name: %s, namespace: %s", operation, config.Name, ns)
}
return err
}
func HandleDeployment(config *appsv1.Deployment, operation string) error {
ns := "default"
if config.Namespace != "" {
ns = config.Namespace
}
var err error
client := RestClient.AppsV1().Deployments(ns)
switch operation {
case Create:
_, err = client.Create(context.Background(), config, metav1.CreateOptions{})
case Delete:
err = client.Delete(context.Background(), config.Name, metav1.DeleteOptions{})
}
if err == nil {
fmt.Printf("%s deployment name: %s, namespace: %s", operation, config.Name, ns)
}
return err
}
func HandleConfigMap(config *apiv1.ConfigMap, operation string) error {
ns := "default"
if config.Namespace != "" {
ns = config.Namespace
}
var err error
client := RestClient.CoreV1().ConfigMaps(ns)
switch operation {
case Create:
_, err = client.Create(context.Background(), config, metav1.CreateOptions{})
case Delete:
err = client.Delete(context.Background(), config.Name, metav1.DeleteOptions{})
}
if err == nil {
fmt.Printf("%s configMap name: %s, namespace: %s", operation, config.Name, ns)
}
return err
}
func HandleAppConfig(path string) ([]interface{}, error) {
configs := make([]interface{}, 0)
fileByte, err := ioutil.ReadFile(path)
items := strings.Split(string(fileByte), "---")
if err != nil {
return nil, err
}
for _, item := range items {
if item == "" {
continue
}
var y yamlItem
dec := yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(item)), 10000)
if err := dec.Decode(&y); err != nil {
return nil, err
}
switch y.Kind {
case "StatefulSet":
statefulSetSchema := &appsv1.StatefulSet{}
dec := yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(item)), 1000)
if err := dec.Decode(&statefulSetSchema); err != nil {
return nil, err
}
configs = append(configs, statefulSetSchema)
case "Service":
serviceSchema := &apiv1.Service{}
dec := yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(item)), 1000)
if err := dec.Decode(&serviceSchema); err != nil {
return nil, err
}
configs = append(configs, serviceSchema)
case "ConfigMap":
configMapSchema := &apiv1.ConfigMap{}
dec := yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(item)), 1000)
if err := dec.Decode(&configMapSchema); err != nil {
return nil, err
}
configs = append(configs, configMapSchema)
case "Deployment":
deploymentSchema := &appsv1.Deployment{}
dec := yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(item)), 1000)
if err := dec.Decode(&deploymentSchema); err != nil {
return nil, err
}
configs = append(configs, deploymentSchema)
}
}
return configs, nil
}
// operation指操作类型,delete or create
func HandleK8sApp(path string, operation string) error {
var err error
if RestClient == nil {
return errors.New("RestClient is nil")
}
configs, err := HandleAppConfig(path)
if err != nil {
return err
}
for _, item := range configs {
switch item.(type){
case *appsv1.StatefulSet:
err = HandleStatefulSet(item.(*appsv1.StatefulSet), operation)
case *apiv1.Service:
err = HandleService(item.(*apiv1.Service), operation)
case *apiv1.ConfigMap:
err = HandleConfigMap(item.(*apiv1.ConfigMap), operation)
case *appsv1.Deployment:
err = HandleDeployment(item.(*appsv1.Deployment), operation)
}
}
return err
}
func main() {
var n string
Init() //初始化k8s client
flag.StringVar(&n, "operation", "create", "操作")
if n == Create {
err := HandleK8sApp("adapter.yaml", Create)
if err != nil {
fmt.Println(err)
}
}else {
err := HandleK8sApp("adapter.yaml", Delete)
if err != nil {
fmt.Println(err)
}
}
}
最后附上实现kubectl get pod -o wide
的代码:
func HandleListPod() ([][]interface{}, error) {
var err error
podList := make([][]interface{}, 0)
podList = append(podList, []interface{}{"NAME", "READY", "STATUS", "RESTARTS", "AGE", "IP", "NODE", "NOMINATED NODE", "READINESS GATES"})
if RestClient == nil {
return nil, errors.New("RestClient is nil")
}
pods, err := RestClient.CoreV1().Pods("default").List(context.Background(), metav1.ListOptions{})
if err != nil {
log.Errorf("Get pod err: %s", err.Error())
return nil, err
}
for _, pod := range pods.Items {
var readinessGates interface{}
nominatedNodeName := ""
restartCount := 0
readyCount := 0
for _, status := range pod.Status.ContainerStatuses {
restartCount += int(status.RestartCount)
if status.Ready {
//fmt.Println(count.State.Running.StartedAt.UnixNano())
readyCount ++
}
}
if pod.Spec.ReadinessGates == nil {
readinessGates = "<none>"
}else {
readinessGates = pod.Spec.ReadinessGates
}
if pod.Status.NominatedNodeName == "" {
pod.Status.NominatedNodeName = "<none>"
}
if pod.Status.PodIP == "" {
pod.Status.PodIP = "<node>"
}
if pod.Spec.NodeName == "" {
pod.Spec.NodeName = "<node>"
}
var startTime int64
if pod.Status.StartTime == nil {
startTime = pod.CreationTimestamp.Unix()
}else {
startTime = pod.Status.StartTime.Unix()
}
podList = append(podList, []interface{}{
pod.Name, fmt.Sprintf("%d/%d", readyCount, len(pod.Status.ContainerStatuses)),
string(pod.Status.Phase), strconv.Itoa(restartCount), handleTimeTrans(time.Now().Unix()-
startTime), pod.Status.PodIP, pod.Spec.NodeName, readinessGates,
nominatedNodeName,
})
}
return podList, nil
}
func handleTimeTrans(timeDiff int64) string {
if timeDiff < 60 {
return fmt.Sprintf("%ds", timeDiff)
}
if timeDiff < 3600 {
return fmt.Sprintf("%dm%ds", timeDiff / 60, 320 % 60)
}
if timeDiff < 3600 * 24 {
return fmt.Sprintf("%dh%dm", timeDiff / 3600, (timeDiff % 3600) / 60)
}
return fmt.Sprintf("%dd%dh", timeDiff / (3600 * 24), (timeDiff % (3600 * 24)) / 3600)
}