3

Having trouble figuring out what is wrong. I have a remote kubernetes cluster up and have copied the config locally. I know it is correct because I have gotten other commands to work for me.

The one I can't get to work is a deployment patch. My code:

const namespace = "default"
    
var clientset *kubernetes.Clientset

func init() {
    kubeconfig := "/Users/$USER/go/k8s-api/config"
    config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
    if err != nil {
        log.Fatal(err)
    }

    // create the clientset
    clientset, err = kubernetes.NewForConfig(config)
    if err != nil {
        panic(err.Error())
    }
}

func main() {
    deploymentsClient := clientset.ExtensionsV1beta1().Deployments("default")

    patch := []byte(`[{"spec":{"template":{"spec":{"containers":[{"name":"my-deploy-test","image":"$ORG/$REPO:my-deploy0.0.1"}]}}}}]`)
    res, err := deploymentsClient.Patch("my-deploy", types.JSONPatchType, patch)
    if err != nil {
        panic(err)
    }
    fmt.Println(res)

}

All I get back is: panic: the server rejected our request due to an error in our request

Any help appreciated, thanks!

Joris
  • 862
  • 1
  • 8
  • 17
L. Norman
  • 483
  • 7
  • 21

2 Answers2

6

You have mixed up JSONPatchType with MergePatchType; JSONPatchType wants the input to be RFC 6902 formatted "commands", and in that case can be a JSON array, because there can be multiple commands applied in order to the input document

However, your payload looks much closer to you wanting MergePatchType, in which case the input should not be a JSON array because the source document is not an array of "spec" objects.

Thus, I'd bet just dropping the leading [ and trailing ], changing the argument to be types.MergePatchType will get you much further along

Community
  • 1
  • 1
mdaniel
  • 31,240
  • 5
  • 55
  • 58
3

Actually you should use types.StrategicMergePatchType and remove leading ([) and trailing (]) brackets from patching string.

  • Merge patch:

    With a JSON merge patch, if you want to update a list, you have to specify the entire new list. And the new list completely replaces the existing list.

    Source

  • Strategic merge patch:

    With a strategic merge patch, a list is either replaced or merged depending on its patch strategy. The patch strategy is specified by the value of the patchStrategy key in a field tag in the Kubernetes source code. For example, the Containers field of PodSpec struct has a patchStrategy of merge:

    type PodSpec struct {
      ...
      Containers []Container `json:"containers" patchStrategy:"merge" patchMergeKey:"name" ...`
    

    Source

N.B. kubectl by default uses strategic merge patch to patch Kubernetes resources.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Abu Hanifa
  • 2,857
  • 2
  • 22
  • 38
  • So, I have tried both methods and it seems that StrategicMergePatchType looks for the first matching tag, in this case containers[0].name and then merges that, but if the patched name does not match containers[0].name then it adds a new container object into the array. Does this sound correct? Why would I want this instead of MergePatchType? – L. Norman Dec 27 '18 at 20:31
  • 1
    Edited my answer and following link has some more useful informations. https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#notes-on-the-strategic-merge-patch – Abu Hanifa Dec 27 '18 at 20:48
  • 1
    Ahh that makes sense, thank you for clearing that up! Accepting this as the answer. – L. Norman Dec 27 '18 at 21:09
  • For non list fields, what is the logic of using strategy merge patch or merge patch? such as nodeselector which type is map. – shuiqiang Jan 20 '21 at 02:48
  • @AbuHanifa - Great answer, I want to patch the node condition type, I am not sure how to get the right struct []byte to use for the patch. – Sam May 19 '22 at 05:30