Home > Net >  k8s how to update specific value in secret
k8s how to update specific value in secret

Time:10-24

I need to update a secret with specific value,(the secret contain additional data) my question is how I can update just the value and not all the secret data (I don't want to override the existing data). I mean if the secret have additional values I don’t want to override them just the entry foo

updSec := v1.Secret{
    TypeMeta: metav1.TypeMeta{},
    ObjectMeta: metav1.ObjectMeta{
        Name:      "d-values",
        Namespace: "terv”,
    },
    Immutable:  nil,
    Data:       nil,
    StringData: nil,
    Type:       "Opaque",
}
updSec.Data[“foo”] = newVal
if err := r.Client.Update(ctx, &updSec); err != nil {
    return ctrl.Result{}, err
}

The issue is that the secret is already exist and here im creating new object and not sure how to do it right ...I need for secret that called d-values just update the newVal for key foo

update

when trying the code in the answer after I run the

patch, err := yaml.Marshal(updSec) the data looks like following and the patch are failed with error, any idea if its related ?

if I try with the c.Client.Update it works but not with Patch but the Patch is the right way as if I've ties before is should keep them..

enter image description here

CodePudding user response:

I don't think you can update a single key using the Update method, but you can certainly do that using Patch instead. Here's an example that uses a StrategicMergePatch; it will replace the key val2 in a secret with the value newval:

package main

import (
  "context"
  "encoding/json"
  "flag"
  "fmt"
  "path/filepath"

  v1 "k8s.io/api/core/v1"
  metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/apimachinery/pkg/types"
  "k8s.io/client-go/kubernetes"
  "k8s.io/client-go/tools/clientcmd"
  "k8s.io/client-go/util/homedir"
)

func main() {
  var kubeconfig *string
  var namespace *string
  var secretname *string

  namespace = flag.String("namespace", "", "namespace of secret")
  secretname = flag.String("name", "", "name of secret")

  if home := homedir.HomeDir(); home != "" {
    kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
  } else {
    kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
  }

  flag.Parse()

  if *namespace == "" {
    panic(fmt.Errorf("you must specify a namespace"))
  }

  if *secretname == "" {
    panic(fmt.Errorf("you must specify a secret name"))
  }

  config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
  if err != nil {
    panic(err)
  }

  clientset, err := kubernetes.NewForConfig(config)
  if err != nil {
    panic(err)
  }

  secretClient := clientset.CoreV1().Secrets(*namespace)
  ctx := context.TODO()

  updSec := v1.Secret{
    Data: map[string][]byte{
      "val2": []byte("newval"),
    },
  }

  payloadBytes, err := json.Marshal(updSec)
  if err != nil {
    panic(err)
  }

  if _, err = secretClient.Patch(ctx, *secretname,
    types.StrategicMergePatchType, payloadBytes, metav1.PatchOptions{}); err != nil {
    panic(err)
  }

  // Fetch updated secret
  sec, err := secretClient.Get(ctx, *secretname, metav1.GetOptions{})
  if err != nil {
    panic(err)
  }
  secJson, err := json.MarshalIndent(sec, "", "  ")
  if err != nil {
    panic(err)
  }

  fmt.Print(string(secJson))
}

For example, if I create a secret like this:

kubectl create secret generic \
  --from-literal val1=key1 \
  --from-literal val2=key2 example

And then run the above code like this:

go run main.go -namespace default -name example

The code will output the update secret. Looking at the data section, we see:

  "data": {
    "val1": "a2V5MQ==",
    "val2": "bmV3dmFs"
  },

And if we decode val2 we see:

$ kubectl get secret example -o json | jq '.data.val2|@base64d'
"newval"

Using the Operator SDK

If you're working with the Operator SDK, you can use Update if you're first reading the existing value, like this:

  // Read the existing secret
  secret := &corev1.Secret{}
  if err := r.Get(ctx, req.NamespacedName, secret); err != nil {
    panic(err)
  }

  // Check if it needs to be modified
  val, ok := secret.Data["val2"]

  // If yes, update the secret with a new value and then write
  // the entire object back with Update
  if !ok || !bytes.Equal(val, []byte("val2")) {
    ctxlog.Info("needs update", "secret", secret)
    secret.Data["val2"] = []byte("newval")
    if err := r.Update(ctx, secret); err != nil {
      panic(err)
    }
  }

You can use the Patch method if you only want to submit a partial update:

  if !ok || !bytes.Equal(val, []byte("val2")) {
    ctxlog.Info("needs update", "secret", secret)
    newVal := corev1.Secret{
      Data: map[string][]byte{
        "val2": []byte("newval"),
      },
    }

    patch, err := json.Marshal(newVal)
    if err != nil {
      panic(err)
    }

    if err := r.Client.Patch(ctx, secret, client.RawPatch(types.StrategicMergePatchType, patch)); err != nil {
      panic(err)
    }
  }

This is pretty much identical to the earlier example. There are examples of using the client.Patch method in the docs, but I'll be honest, I don't find the example very clear.

  • Related