成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

K8S client-go Patch example

null1145 / 3368人閱讀

摘要:所以使用記住要把原始的數(shù)據(jù)取出來(lái)和你要新增的數(shù)據(jù)合并后再提交,如單元測(cè)試如各位還有其他更好的方式,歡迎交流補(bǔ)充。

使用Patch方式更新K8S的 API Objects 一共有三種方式:strategic merge patch, json-patch,json merge patch。關(guān)于這三種方式的文字描述區(qū)別可看官方文檔update-api-object-kubectl-patch。

我在本文中主要會(huì)介紹使用client-go的Patch方式,主要包括strategic merge patchjson-patch。不介紹json merge patch的原因,是該方式使用場(chǎng)景比較少,因此不做介紹,如果有同學(xué)有興趣,可做補(bǔ)充。

StrategicMergePatch 新增Object值

本次示例以給一個(gè)node新增一個(gè)labels為例,直接上代碼:

//根據(jù)Pod Sn 更新 pod
func UpdatePodByPodSn(coreV1 v1.CoreV1Interface, podSn string, patchData map[string]interface{}) (*apiv1.Pod, error) {
    v1Pod, err := coreV1.Pods("").Get(podSn, metav1.GetOptions{})
    if err != nil {
        logs.Error("[UpdatePodByPodSn]  get pod %v  fail %v", podSn, err)
        return nil, fmt.Errorf("[UpdatePodByPodSn]  get pod %v  fail %v", podSn, err)
    }

    namespace := v1Pod.Namespace
    podName := v1Pod.Name

    playLoadBytes, _ := json.Marshal(patchData)

    newV1Pod, err := coreV1.Pods(namespace).Patch(podName, types.StrategicMergePatchType, playLoadBytes)

    if err != nil {
        logs.Error("[UpdatePodByPodSn] %v pod Patch fail %v", podName, err)
        return nil, fmt.Errorf("[UpdatePodByPodSn] %v pod Patch fail %v", podName, err)
    }

    return newV1Pod, nil
}
注意:上面的PatchData 必須是以 {"metadata":...}的go struct, 如:`map[string]interface{}{"metadata": map[string]map[string]string{"labels": {
        "test2": "test2",
    }}}`

對(duì)應(yīng)單元測(cè)試用例

func pod(podName string, nodeName string, labels map[string]string, annotations map[string]string) *v1.Pod {
    return &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: podName, Labels: labels, Annotations: annotations}, Spec: v1.PodSpec{NodeName: nodeName}, Status: v1.PodStatus{}}
}

func TestUpdatePodByPodSn(t *testing.T) {
    var tests = []struct {
        expectedError      interface{}
        expectedAnnotation string
        expectedLabel      string
        podSn              string
        patchData          map[string]interface{}
        v1Pod              []runtime.Object
    }{
        {nil, "test2", "", "1.1.1.1", map[string]interface{}{"metadata": map[string]map[string]string{"annotations": {
            "test2": "test2",
        }}},
            []runtime.Object{pod("1.1.1.1", "1.1.1.1", map[string]string{"test1": "test1"}, map[string]string{"test1": "test1"})},
        },
        {nil, "", "", "1.1.1.2", map[string]interface{}{"metadata": map[string]map[string]string{"labels": {
            "test2": "",
        }}},
            []runtime.Object{pod("1.1.1.2", "1.1.1.1", map[string]string{"test1": "test1"}, map[string]string{"test1": "test1"})},
        },
        {nil, "", "test2", "1.1.1.3", map[string]interface{}{"metadata": map[string]map[string]string{"labels": {
            "test2": "test2",
        }}},
            []runtime.Object{pod("1.1.1.3", "1.1.1.1", map[string]string{"test1": "test1"}, map[string]string{"test1": "test1"})},
        },
    }

    for _, test := range tests {
        client := fake.NewSimpleClientset(test.v1Pod...)

        v1Pod, err := UpdatePodByPodSn(client.CoreV1(), test.podSn, test.patchData)
        if err != nil {
            t.Errorf("expected error  %s, got %s", test.expectedError, err)
        }

        assert.Equal(t, v1Pod.Annotations["test2"], test.expectedAnnotation)
        assert.Equal(t, v1Pod.Labels["test2"], test.expectedLabel)
    }
}
修改Object的值

修改Obejct的值使用方式如下,當(dāng)使用strategic merge patch的時(shí)候,如果提交的數(shù)據(jù)中鍵已經(jīng)存在,那就會(huì)使用新提交的值替換原先的數(shù)據(jù)。依舊以修改labels的值為例。
如新提交的數(shù)據(jù)為:

{
  "metadata":{
      "labels":{
          "test2":"test3",
      },
  }
}

Node中已經(jīng)存在的labels為:

{
  "metadata":{
      "labels":{
          "test2":"test1",
      },
  }
}

最終Node中l(wèi)abels的key為test2的值會(huì)被替換成 test3。

刪除Object值

當(dāng)需要把某個(gè)Object的值刪除的時(shí)候,當(dāng)使用strategic merge patch的時(shí)候,依舊是刪除labels為例提交方式是:

golang里面的表現(xiàn)形式是:

{
  "metadata":{
      "labels":{
          "test2":nil
      },
  }
}

對(duì)應(yīng)從瀏覽器提交的數(shù)據(jù)是:

{
  "metadata":{
      "labels":{
          "test2":null
      },
  }
}
PS:如果不喜歡使用上面struct的方式組成數(shù)據(jù),可以使用如下的方式     labelsPatch := fmt.Sprintf({"metadata":{"labels":{"%s":"%s"}}}, labelkey, labelvalue) 直接代替上面示例中的patchData
JSONPatch

JSONPatch的詳細(xì)說(shuō)明請(qǐng)參考文檔:http://jsonpatch.com/。
JSONPatch 主要有三種操作方式:add,replace,remove。以下會(huì)以代碼示例說(shuō)明這三種操作在Client-go對(duì)應(yīng)的代碼示例來(lái)說(shuō)明怎樣操作K8s 的資源。

使用JSONPatch,如果Patch中帶有斜杠“/”和 (~)這兩個(gè)字符,不能直接傳入這兩個(gè)字符,需要你輸入的時(shí)候就人工轉(zhuǎn)換下,/轉(zhuǎn)換成~1,~轉(zhuǎn)換成~0。以新增labels為例,如我要新增一個(gè)"test1/test2":"test3"的labels,可以把要傳入的數(shù)據(jù)修改為"test1~1test2":"test3"即可。
Op:add

使用JSONPatch的方式新增一個(gè)標(biāo)簽,其提交的數(shù)據(jù)格式必須是[{ "op": "replace", "path": "/baz", "value": "boo" }] 這樣的。代碼如下:

//patchStringValue specifies a patch operation for a string.
type PatchStringValue struct {
    Op    string      `json:"op"`
    Path  string      `json:"path"`
    Value interface{} `json:"value"`
}

type PatchNodeParam struct {
    coreV1       v1.CoreV1Interface
    NodeSn       string                 `json:"nodeSn"`
    OperatorType string                 `json:"operator_type"`
    OperatorPath string                 `json:"operator_path"`
    OperatorData map[string]interface{} `json:"operator_data"`
}


//patch node info, example label, annotation
func patchNode(param PatchNodeParam) (*apiv1.Node, error) {
    coreV1 := param.coreV1
    nodeSn := param.NodeSn

    node, err := coreV1.Nodes().Get(nodeSn, metav1.GetOptions{})

    if err != nil {
        return nil, err
    }

    operatorData := param.OperatorData
    operatorType := param.OperatorType
    operatorPath := param.OperatorPath

    var payloads []interface{}

    for key, value := range operatorData {
        payload := PatchStringValue{
            Op:    operatorType,
            Path:  operatorPath + key,
            Value: value,
        }

        payloads = append(payloads, payload)

    }

    payloadBytes, _ := json.Marshal(payloads)

    newNode, err := coreV1.Nodes().Patch(nodeSn, types.JSONPatchType, payloadBytes)

    if err != nil {
        return nil, err
    }

    return newNode, err
}

單元測(cè)試:

func TestPatchNode(t *testing.T) {
    Convey("test patchNode", t, func() {
        Convey("Patch Node fail", func() {
            var tests = []struct {
                nodeSn        string
                operatorType  string
                operatorPath  string
                operatorData  map[string]interface{}
                expectedError interface{}
                expectedValue *v1.Node
                objs          []runtime.Object
            }{
                {"1.1.1.1", "add", "/metadata/labels/",
                    map[string]interface{}{
                        "test1": "test1",
                        "test2": "test2"},
                    "nodes "1.1.1.1" not found", nil, nil},
                {"1.1.1.1", "aaa", "/metadata/labels/",
                    map[string]interface{}{
                        "test1": "test1",
                        "test2": "test2"},
                    "Unexpected kind: aaa", nil, []runtime.Object{node("1.1.1.1", nil, nil)}},
            }


            for _, test := range tests {
                client := fake.NewSimpleClientset(test.objs...)

                param := PatchNodeParam{
                    coreV1:       client.CoreV1(),
                    NodeSn:       test.nodeSn,
                    OperatorType: test.operatorType,
                    OperatorPath: test.operatorPath,
                    OperatorData: test.operatorData,
                    EmpId:        test.empId,
                }

                output, err := patchNode(param)

                So(output, ShouldEqual, test.expectedValue)
                So(err.Error(), ShouldEqual, test.expectedError)

            }
        })

        Convey("Patch Node success", func() {
            var tests = []struct {
                nodeSn        string
                operatorType  string
                operatorPath  string
                operatorData  map[string]interface{}
                expectedError interface{}
                expectedValue string
                objs          []runtime.Object
            }{
                {"1.1.1.1", "add", "/metadata/labels/",
                    map[string]interface{}{
                        "test1": "test1",
                        "test2": "test2"},
                    nil, "1.1.1.1", []runtime.Object{node("1.1.1.1", map[string]string{"test3": "test3"}, map[string]string{"test3": "test3"})}},
                {"1.1.1.1", "add", "/metadata/labels/",
                    map[string]interface{}{
                        "test1": "test1",
                        "test2": "test2"},
                    nil, "1.1.1.1", []runtime.Object{node("1.1.1.1", map[string]string{"test1": "modifytest"}, map[string]string{"test1": "modifytest"})}},
            }

            for _, test := range tests {

                client := fake.NewSimpleClientset(test.objs...)

                param := PatchNodeParam{
                    coreV1:       client.CoreV1(),
                    NodeSn:       test.nodeSn,
                    OperatorType: test.operatorType,
                    OperatorPath: test.operatorPath,
                    OperatorData: test.operatorData,
                }

                output, err := patchNode(param)

                So(output, ShouldNotBeNil)
                So(err, ShouldBeNil)
                So(output.Name, ShouldEqual, test.expectedValue)
            }
        })

    })

}
使用add有個(gè)需要注意的地方就是,當(dāng)你的Path是使用的/metadata/labels而不是/metadata/labels/labelkey的時(shí)候,那你這個(gè)add操作實(shí)際是對(duì)整個(gè)labels進(jìn)行替換,而不是新增,一定要注意避免踩坑。
PS:如果不喜歡使用上面struct的方式組成數(shù)據(jù),可以使用如下的方式 labelsPatch := fmt.Sprintf([{"op":"add","path":"/metadata/labels/%s","value":"%s" }], labelkey, labelvalue) 直接代替上面示例中的patchData
Op:remove

要?jiǎng)h除一個(gè)標(biāo)簽的話,代碼和增加區(qū)別不大,唯一的區(qū)別就是提交的數(shù)據(jù)要由鍵值對(duì)修改為提交一個(gè)string slice類型[]string,代碼如下:

type PatchNodeParam struct {
    coreV1       v1.CoreV1Interface
    NodeSn       string                 `json:"nodeSn"`
    OperatorType string                 `json:"operator_type"`
    OperatorPath string                 `json:"operator_path"`
    OperatorData map[string]interface{} `json:"operator_data"`
}

//patchStringValue specifies a remove operation for a string.
type RemoveStringValue struct {
    Op   string `json:"op"`
    Path string `json:"path"`
}

//remove node info, example label, annotation
func removeNodeInfo(param RemoveNodeInfoParam) (*apiv1.Node, error) {
    coreV1 := param.coreV1
    nodeSn := param.NodeSn

    node, err := coreV1.Nodes().Get(nodeSn, metav1.GetOptions{})

    if err != nil {
        return nil, err
    }

    operatorKey := param.OperatorKey
    operatorType := param.OperatorType
    operatorPath := param.OperatorPath

    var payloads []interface{}

    for key := range operatorKey {
        payload := RemoveStringValue{
            Op:   operatorType,
            Path: operatorPath + operatorKey[key],
        }

        payloads = append(payloads, payload)

    }

    payloadBytes, _ := json.Marshal(payloads)

    newNode, err := coreV1.Nodes().Patch(nodeSn, types.JSONPatchType, payloadBytes)

    if err != nil {
        return nil, err
    }


    return newNode, err
}
Op:replace

replace操作,會(huì)對(duì)整個(gè)的Object進(jìn)行替換。所以使用replace記住要把原始的數(shù)據(jù)取出來(lái)和你要新增的數(shù)據(jù)合并后再提交,如:

type ReplaceNodeInfoParam struct {
    coreV1       v1.CoreV1Interface
    NodeSn       string                 `json:"nodeSn"`
    OperatorType string                 `json:"operator_type"`
    OperatorPath string                 `json:"operator_path"`
    OperatorData map[string]interface{} `json:"operator_data"`
    DataType     string                 `json:"data_type"`
}

//patchStringValue specifies a patch operation for a string.
type PatchStringValue struct {
    Op    string      `json:"op"`
    Path  string      `json:"path"`
    Value interface{} `json:"value"`
}



func replaceNodeInfo(param ReplaceNodeInfoParam) (*apiv1.Node, error) {
    coreV1 := param.coreV1
    nodeSn := param.NodeSn

    node, err := coreV1.Nodes().Get(nodeSn, metav1.GetOptions{})

    if err != nil {
        return nil, err
    }

    var originOperatorData map[string]string

    dataType := param.DataType
    operatorData := param.OperatorData
    operatorType := param.OperatorType
    operatorPath := param.OperatorPath

    switch dataType {
    case "labels":
        originOperatorData = node.Labels
    case "annotations":
        originOperatorData = node.Annotations
    default:
        originOperatorData = nil
    }

    if originOperatorData == nil {
        return nil, fmt.Errorf("[replaceNodeInfo] fail, %v originOperatorData is nil", nodeSn)
    }

    for key, value := range originOperatorData {
        operatorData[key] = value
    }

    var payloads []interface{}

    payload := PatchStringValue{
        Op:    operatorType,
        Path:  operatorPath,
        Value: operatorData,
    }

    payloads = append(payloads, payload)

    payloadBytes, _ := json.Marshal(payloads)

    newNode, err := coreV1.Nodes().Patch(nodeSn, types.JSONPatchType, payloadBytes)

    if err != nil {
        return nil, err
    }

    return newNode, err
}

單元測(cè)試

func TestReplaceNodeInfo(t *testing.T) {
    Convey("test ReplaceNodeInfo", t, func() {
        Convey("Patch ReplaceNodeInfo fail", func() {
            var tests = []struct {
                nodeSn        string
                operatorType  string
                operatorPath  string
                dataType      string
                operatorData  map[string]interface{}
                expectedError interface{}
                expectedValue *v1.Node
                objs          []runtime.Object
            }{
                {"1.1.1.1", "add", "/metadata/labels", "labels",
                    map[string]interface{}{
                        "test1": "test1",
                        "test2": "test2"},
                    "nodes "1.1.1.1" not found", nil, nil},
                {"1.1.1.1", "aaa", "/metadata/annotations", "annotations",
                    map[string]interface{}{
                        "test1": "test1",
                        "test2": "test2"},
                    "[replaceNodeInfo] fail, 1.1.1.1 originOperatorData is nil", nil, []runtime.Object{node("1.1.1.1", nil, nil)}},
            }

            for _, test := range tests {
                client := fake.NewSimpleClientset(test.objs...)

                param := ReplaceNodeInfoParam{
                    coreV1:       client.CoreV1(),
                    NodeSn:       test.nodeSn,
                    OperatorType: test.operatorType,
                    OperatorPath: test.operatorPath,
                    OperatorData: test.operatorData,
                    DataType:     test.dataType,
                }

                output, err := replaceNodeInfo(param)

                So(output, ShouldEqual, test.expectedValue)
                So(err.Error(), ShouldEqual, test.expectedError)

            }
        })

        Convey("Patch Node success", func() {
            var tests = []struct {
                nodeSn             string
                operatorType       string
                operatorPath       string
                dataType           string
                operatorData       map[string]interface{}
                expectedError      interface{}
                expectedLabel      string
                expectedAnnotation string
                objs               []runtime.Object
            }{
                {"1.1.1.1", "replace", "/metadata/labels", "labels",
                    map[string]interface{}{
                        "test1": "test1",
                        "test2": "test2"},
                    nil, "test3", "", []runtime.Object{node("1.1.1.1", map[string]string{"test3": "test3"}, map[string]string{"test3": "test3"})}},
                {"1.1.1.1", "replace", "/metadata/annotations", "annotations",
                    map[string]interface{}{
                        "test1": "test1",
                        "test2": "test2"},
                    nil, "", "modifytest", []runtime.Object{node("1.1.1.1", map[string]string{"test1": "modifytest"}, map[string]string{"test1": "modifytest"})}},
            }

            for _, test := range tests {

                client := fake.NewSimpleClientset(test.objs...)

                param := ReplaceNodeInfoParam{
                    coreV1:       client.CoreV1(),
                    NodeSn:       test.nodeSn,
                    OperatorType: test.operatorType,
                    OperatorPath: test.operatorPath,
                    OperatorData: test.operatorData,
                    DataType:     test.dataType,
                }

                output, err := replaceNodeInfo(param)

                So(output, ShouldNotBeNil)
                So(err, ShouldBeNil)
                So(output.Labels["test3"], ShouldEqual, test.expectedLabel)
                So(output.Annotations["test1"], ShouldEqual, test.expectedAnnotation)
            }
        })

    })
}

PS:如各位還有其他更好的方式,歡迎交流補(bǔ)充。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/32987.html

相關(guān)文章

  • K8S 生態(tài)周報(bào)| 2019.04.01~2019.04.07

    摘要:生態(tài)周報(bào)內(nèi)容主要包含我所接觸到的生態(tài)相關(guān)的每周值得推薦的一些信息。歡迎訂閱知乎專欄生態(tài)。版本發(fā)布這是修改發(fā)布周期后,第二個(gè)發(fā)布的版本,上個(gè)版本是。從發(fā)布周期來(lái)看,由原先的季度發(fā)布改成半年發(fā)布,也意味著的日漸成熟。 「K8S 生態(tài)周報(bào)」內(nèi)容主要包含我所接觸到的 K8S 生態(tài)相關(guān)的每周值得推薦的一些信息。歡迎訂閱知乎專欄「k8s生態(tài)」。 Kubernetes client-go v11.0...

    _Suqin 評(píng)論0 收藏0
  • K8S 生態(tài)周報(bào)| 2019.04.01~2019.04.07

    摘要:生態(tài)周報(bào)內(nèi)容主要包含我所接觸到的生態(tài)相關(guān)的每周值得推薦的一些信息。歡迎訂閱知乎專欄生態(tài)。版本發(fā)布這是修改發(fā)布周期后,第二個(gè)發(fā)布的版本,上個(gè)版本是。從發(fā)布周期來(lái)看,由原先的季度發(fā)布改成半年發(fā)布,也意味著的日漸成熟。 「K8S 生態(tài)周報(bào)」內(nèi)容主要包含我所接觸到的 K8S 生態(tài)相關(guān)的每周值得推薦的一些信息。歡迎訂閱知乎專欄「k8s生態(tài)」。 Kubernetes client-go v11.0...

    張遷 評(píng)論0 收藏0
  • k8s的擴(kuò)展資源設(shè)計(jì)和device-plugin

    摘要:如果上的資源耗盡,這類將無(wú)法成功調(diào)度。將這個(gè)資源及其對(duì)應(yīng)的設(shè)備個(gè)數(shù)記錄到更新到。 extended-resources extended-resources在k8s1.9中是一個(gè)stable的特性??梢杂靡痪湓拋?lái)概括這個(gè)特性: 通過(guò)向apiserver發(fā)送一個(gè)patch node 的請(qǐng)求,為這個(gè)node增加一個(gè)自定義的資源類型,用于以該資源的配額統(tǒng)計(jì)和相應(yīng)的QoS的配置。 patch ...

    shiweifu 評(píng)論0 收藏0
  • 容器監(jiān)控實(shí)踐—kube-state-metrics

    摘要:功能提供的指標(biāo),按照階段分為三種類別實(shí)驗(yàn)性質(zhì)的中階段的或者的字段。穩(wěn)定版本的中不向后兼容的主要版本的更新被廢棄的已經(jīng)不在維護(hù)的。通過(guò)比較來(lái)保證的順序并不保證包含所有資源本文為容器監(jiān)控實(shí)踐系列文章,完整內(nèi)容見(jiàn) 概述 已經(jīng)有了cadvisor、heapster、metric-server,幾乎容器運(yùn)行的所有指標(biāo)都能拿到,但是下面這種情況卻無(wú)能為力: 我調(diào)度了多少個(gè)replicas?現(xiàn)在可...

    kevin 評(píng)論0 收藏0
  • 容器監(jiān)控實(shí)踐—kube-state-metrics

    摘要:功能提供的指標(biāo),按照階段分為三種類別實(shí)驗(yàn)性質(zhì)的中階段的或者的字段。穩(wěn)定版本的中不向后兼容的主要版本的更新被廢棄的已經(jīng)不在維護(hù)的。通過(guò)比較來(lái)保證的順序并不保證包含所有資源本文為容器監(jiān)控實(shí)踐系列文章,完整內(nèi)容見(jiàn) 概述 已經(jīng)有了cadvisor、heapster、metric-server,幾乎容器運(yùn)行的所有指標(biāo)都能拿到,但是下面這種情況卻無(wú)能為力: 我調(diào)度了多少個(gè)replicas?現(xiàn)在可...

    cikenerd 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<