英文:
k8s how to update specific value in secret
问题
我需要更新一个包含额外数据的秘密(secret),我的问题是如何只更新值而不是所有的秘密数据(我不想覆盖现有数据)。我的意思是,如果秘密有额外的值,我不想覆盖它们,只更新foo
的条目。
问题在于秘密已经存在,而我在这里创建了一个新对象,不确定如何正确操作...我需要更新名为d-values
的秘密,只更新键foo
的newVal
。
更新
在运行
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..
答案1
得分: 2
我认为你可以使用Patch
方法来更新单个键,而不是使用Update
方法。下面是一个使用StrategicMergePatch的示例,它将在一个密钥中替换val2
为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))
}
例如,如果我像这样创建一个密钥:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论