k8s how to update specific value in secret

huangapple go评论93阅读模式
英文:

k8s how to update specific value in secret

问题

我需要更新一个包含额外数据的秘密(secret),我的问题是如何只更新值而不是所有的秘密数据(我不想覆盖现有数据)。我的意思是,如果秘密有额外的值,我不想覆盖它们,只更新foo的条目。

问题在于秘密已经存在,而我在这里创建了一个新对象,不确定如何正确操作...我需要更新名为d-values的秘密,只更新键foonewVal

更新

在运行

patch, err := yaml.Marshal(updSec)

之后,数据看起来如下所示,并且补丁失败并出现错误,请问是否相关?

如果我尝试使用c.Client.Update,它可以工作,但是使用Patch就不行,但是Patch是正确的方式,因为如果我之前有绑定,它应该保留它们。

英文:

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..

k8s how to update specific value in secret

答案1

得分: 2

我认为你可以使用Patch方法来更新单个键,而不是使用Update方法。下面是一个使用StrategicMergePatch的示例,它将在一个密钥中替换val2newval的值:

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))
}

例如,如果我像这样创建一个密钥:

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

然后像这样运行上面的代码:

go run main.go -namespace default -name example

该代码将输出更新后的密钥。在data部分中,我们可以看到:

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

如果我们解码val2,我们会看到:

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

使用Operator SDK

如果你正在使用Operator SDK,如果你首先读取现有的值,你可以使用Update方法,像这样:

  // 读取现有的密钥
  secret := &corev1.Secret{}
  if err := r.Get(ctx, req.NamespacedName, secret); err != nil {
    panic(err)
  }

  // 检查是否需要修改
  val, ok := secret.Data["val2"]

  // 如果需要,使用新值更新密钥,然后使用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)
    }
  }

如果你只想提交部分更新,你可以使用Patch方法:

  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)
    }
  }

这与之前的示例几乎相同。在文档中有使用client.Patch方法的示例,但是说实话,我觉得示例不太清楚。

英文:

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.

huangapple
  • 本文由 发表于 2022年10月23日 23:17:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/74172305.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定