labring / fist

on the way~
Apache License 2.0
239 stars 46 forks source link

执行器设计 #137

Open fanux opened 5 years ago

fanux commented 5 years ago

前端渲染好的模板需要执行,这的实现方式有几种:

  1. 把yaml文件传给后台,起一个临时的kubectl容器去执行 (效率低下,依赖变多)
  2. 解析yaml文件使用client-go,调用apiserver去创建对象。 其实这还是挺复杂的,基本每种对象都需要实现一遍,曾经有个项目就是基于这种方式实现的,虽然代码足够解耦,增加一种类型的object增加一个实现即可,但是还是偏复杂了,而且后续最好还是能支持CRD的创建。
  3. 参考kubectl的实现并复用其代码。

因为如这种中间带有---的yaml,其实是在kubectl这块做切分的

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: admin
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin
  namespace: sealyun
  labels:
    kubernetes.io/cluster-service: "true"
   这个切分虽然不难,但是需要考虑一些细节,比如有时confimap里也有---应该如何处理。 这块代码我之前已经复用过kubectl里的函数进行处理了。

  但是切分完成后我们依然要根据kind判断去实例化一个object 然后调用client-go进行创建。 这里最好能复用kubectl的代码。

 fist相比kubectl更复杂的地方在于fist是多租户系统,client-set也需要根据租户的token 或者特定的kubeconfig字符串进行创建,这块实现我也已经做过:
fanux commented 5 years ago
package client

import (
    "fmt"

    "git.xfyun.cn/container/genesis/modules/authentication"
    "git.xfyun.cn/container/genesis/utils"

    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
    "k8s.io/client-go/tools/clientcmd"
    "github.com/pkg/errors"
)

//KubeClientFromInClusterConfig is
func KubeClientFromInClusterConfig() (clientSet *kubernetes.Clientset, err error) {
    config, err := rest.InClusterConfig()
    if err != nil {
        return clientSet, err
    }
    // creates the clientset
    clientSet, err = kubernetes.NewForConfig(config)
    if err != nil {
        return clientSet, err
    }
    return clientSet, nil
}

//KubeClientFromKubeconfigPath is
func KubeClientFromKubeconfigPath(path string) (clientSet *kubernetes.Clientset, err error) {

    cfg, err := clientcmd.BuildConfigFromFlags("", path)
    if err != nil {
        return nil, fmt.Errorf("new kube config error: %s", err)
    }

    if clientSet, err = kubernetes.NewForConfig(cfg); err != nil {
        return nil, fmt.Errorf("new kube config error: %s", err)
    }
    return clientSet, nil
}

//KubeClientFromKubeconfigStringBody is
func KubeClientFromKubeconfigStringBody(body string) (*kubernetes.Clientset, error) {
    b, err := utils.Base64Decode(body)
    if err != nil {
        return nil, err
    }

    clientConfig, err := clientcmd.NewClientConfigFromBytes(b)
    if err != nil {
        return nil, fmt.Errorf("new client config from body error: %s", err)
    }
    cfg, err := clientConfig.ClientConfig()
    if err != nil {
        return nil, fmt.Errorf("new kube config from body error: %s", err)
    }

    clientSet, err := kubernetes.NewForConfig(cfg)
    if err != nil {
        return nil, fmt.Errorf("new kube config from body error: %s", err)
    }
    return clientSet, nil
}

客户端创建可参考

fanux commented 5 years ago

yaml切分处理:

package utils

import (
    "bytes"
    "fmt"
    "io"

    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/apimachinery/pkg/util/yaml"
    "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
    clientcmdapi "k8s.io/client-go/tools/clientcmd/api"

    cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"

    "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)

var bs = []byte(`
kind: Namespace
metadata:
   name: test
---

kind: bbb
name: aaa`)

/* Out put
{"kind":"Namespace","metadata":{"name":"test"}}
{"kind":"bbb","name":"aaa"}
*/
func example() {
    reader := bytes.NewReader(bs)
    ext := runtime.RawExtension{}
    d := yaml.NewYAMLOrJSONDecoder(reader, 4096)
    for {
        if err := d.Decode(&ext); err != nil {
            if err == io.EOF {
                return
            }
            return
        }
        fmt.Println(string(ext.Raw))
    }
}

//YamlCallback is
type YamlCallback func([]byte) error

//YamlHandler is
func YamlHandler(rawBytes []byte, fn YamlCallback) (err error) {
    reader := bytes.NewReader(rawBytes)

    d := yaml.NewYAMLOrJSONDecoder(reader, 4096)
    for {
        ext := runtime.RawExtension{}
        if err = d.Decode(&ext); err != nil {
            if err == io.EOF {
                return nil
            }
            return fmt.Errorf("decode yaml json failed: %v", err)
        }

        // TODO: This needs to be able to handle object in other encodings and schemas.
        ext.Raw = bytes.TrimSpace(ext.Raw)
        if len(ext.Raw) == 0 || bytes.Equal(ext.Raw, []byte("null")) {
            continue
        }

        kubeConfigFlags := genericclioptions.NewConfigFlags()
        matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
        f := cmdutil.NewFactory(matchVersionKubeConfigFlags)

        schema, err := f.Validator(true)
        if err != nil {
            return err
        }

        //if err := resource.ValidateSchema(ext.Raw, validation.NullSchema{}); err != nil {
        if err := resource.ValidateSchema(ext.Raw, schema); err != nil {
            return fmt.Errorf("error validating yaml: %v", err)
        }

        //Raw is already to json
        if err = fn(ext.Raw); err != nil {
            return fmt.Errorf("handler yaml callback fn failed: %v", err)
        }
    }
}