更新 YAML 文件中的属性

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

update property in a yaml file

问题

我正在阅读一个 YAML 文件,将其中的特定字段 test1 修改为 test2,并希望将其写回文件。问题是原始数据显示为数字,并且有很多空字段(与结构相关,但我希望避免它)。我该如何实现?我的意思是只更新 YAML 文件的属性并将其写回到新文件中。

以下是我尝试过的代码:

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gardener/gardener/pkg/apis/core/v1beta1"
	gyaml "github.com/go-yaml/yaml/v3"
	"io/ioutil"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/yaml"
)

type InfraConfig struct {
	APIVersion string `json:"apiVersion,omitempty"`
	Kind       string `json:"kind,omitempty"`
	Networks   struct {
		CloudNAT struct {
			MinPortsPerVM int `json:"minPortsPerVM,omitempty"`
			NatIPNames    []struct {
				Name string `json:"name,omitempty"`
			} `json:"natIPNames,omitempty"`
		} `json:"cloudNAT,omitempty"`
	} `json:"networks,omitempty"`
}

func main() {
	shoot, e := parseShoot("test.yaml")
	if e != nil {
		fmt.Println(e)
	}

	var existingInfraConfig InfraConfig
	err := json.Unmarshal(shoot.Spec.Provider.InfrastructureConfig.Raw, &existingInfraConfig)
	fmt.Println(err)

	existingInfraConfig.Networks.CloudNAT.NatIPNames[0].Name = "test2"
	byteData, _ := json.Marshal(existingInfraConfig)

	shoot.Spec.Provider.InfrastructureConfig = &runtime.RawExtension{
		Raw:    byteData,
		Object: nil,
	}
	fmt.Println(string(shoot.Spec.Provider.InfrastructureConfig.Raw))

	aa, e := gyaml.Marshal(shoot)
	if e != nil {
		fmt.Println(e)
	}

	ioutil.WriteFile("test2.yaml", aa, 0644)
}

func parseShoot(path string) (*v1beta1.Shoot, error) {
	var shootSpec *v1beta1.Shoot
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(bytes, &shootSpec)
	if err != nil {
		return nil, err
	}
	return shootSpec, nil
}

这是我将数据写入文件后得到的结果:

typemeta:
  kind: Shoot
  apiversion: core.gardener.cloud/v1beta1
objectmeta:
  name: test
  generatename: ""
  namespace: ns
  selflink: ""
  uid: ""
  resourceversion: ""
  generation: 0
  creationtimestamp: "0001-01-01T00:00:00Z"
  deletiontimestamp: null
  deletiongraceperiodseconds: null
  labels: {}
  annotations: {}
  ownerreferences: []
  finalizers: []
  clustername: ""
  managedfields: []
spec:
  addons: null
  cloudprofilename: ""
  dns: null
  extensions: []
  hibernation: null
  kubernetes:
    allowprivilegedcontainers: null
    clusterautoscaler: null
    kubeapiserver: null
    kubecontrollermanager: null
    kubescheduler: null
    kubeproxy: null
    kubelet: null
    version: ""
    verticalpodautoscaler: null
  networking:
    type: ""
    providerconfig: null
    pods: null
    nodes: null
    services: null
  maintenance: null
  monitoring: null
  provider:
    type: aaa
    controlplaneconfig: null
    infrastructureconfig:
      raw:
      - 123
      - 34
      - 97
      - 112
      - 105
      - 86
      - 101
      - 114
      - 115
      - 105
      - 111
      - 110
      - 34
      - 58
      - 34
      - 103
      - 99
      - 112
      - 46
      - 112
      - 114
      - 111
      - 118
      - 105
      - 100
      - 101
      - 114
      - 46
      - 101
      - 120
      - 116
      - 101
      - 110
      - 115
      - 105
      - 111
      - 110
      - 115
      - 46
      - 103
      - 97
      - 114
      - 100
      - 101
      - 110
      - 101
      - 114
      - 46
      - 99
      - 108
      - 111
      - 117
      - 100
      - 47
      - 118
      - 49
      - 97
      - 108
      - 112
      - 104
      - 97
      - 49
      - 34
      - 44
      - 34
      - 107
      - 105
      - 110
      - 100
      - 34
      - 58
      - 34
      - 73
      - 110
      - 102
      - 114
      - 97
      - 115
      - 116
      - 114
      - 117
      - 99
      - 116
      - 117
      - 114
      - 101
      - 67
      - 111
      - 110
      - 102
      - 105
      - 103
      - 34
      - 44
      - 34
      - 110
      - 101
      - 116
      - 119
      - 111
      - 114
      - 107
      - 115
      - 34
      - 58
      - 123
      - 34
      - 99
      - 108
      - 111
      - 117
      - 100
      - 78
      - 65
      - 84
      - 34
      - 58
      - 123
      - 34
      - 109
      - 105
      - 110
      - 80
      - 111
      - 114
      - 116
      - 115
      - 80
      - 101
      - 114
      - 86
      - 77
      - 34
      - 58
      - 49
      - 48
      - 48
      - 48
      - 48
      - 44
      - 34
      - 110
      - 97
      - 116
      - 73
      - 80
      - 78
      - 97
      - 109
      - 101
      - 115
      - 34
      - 58
      - 91
      - 123
      - 34
      - 110
      - 97
      - 109
      - 101
      - 34
      - 58
      - 34
      - 116
      - 101
      - 115
      - 116
      - 50
      - 34
      - 125
      - 93
      - 125
      - 125
      - 125
    object: null
  workers: []
purpose: null
region: ""
secretbindingname: ""
seedname: null
seedselector: null
resources: []
tolerations: []
exposureclassname: null
systemcomponents: null
status:
  conditions: []
  constraints: []
  gardener:
    id: ""
    name: ""
    version: ""
  ishibernated: false
  lastoperation: null
  lasterrors: []
  observedgeneration: 0
  retrycyclestarttime: null
  seedname: null
  technicalid: ""
  uid: ""
  clusteridentity: null
  advertisedaddresses: []
  migrationstarttime: null
  credentials: null

更新

这是完整的文件内容:

apiVersion: core.gardener.cloud/v1beta1
kind: Shoot
metadata:
  name: test
  namespace: ns
spec:
  provider:
    type: aaa
    infrastructureConfig:
      apiVersion: gcp.provider.extensions.gardener.cloud/v1alpha1
      kind: InfrastructureConfig
      networks:
        cloudNAT:
          minPortsPerVM: 10000
          natIPNames:
            - name: test1

请问有什么我可以帮助您的吗?

英文:

Im reading a yaml file modify a specific field test1 to test2 and I want to write it back to file
The problem that the raw data is display as numbers and I got a lot empty fields (which is related to the struct but I want to avoid it,
How I make it works? I mean just update the property of the yaml file and write it back to a new file.

This is what I’ve tried

https://go.dev/play/p/Fop37gRwmjR?v=goprev

package main
import (
"encoding/json"
"fmt"
"github.com/gardener/gardener/pkg/apis/core/v1beta1"
gyaml "github.com/go-yaml/yaml/v3"
"io/ioutil"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/yaml"
)
type InfraConfig struct {
APIVersion string `json:"apiVersion,omitempty"`
Kind       string `json:"kind,omitempty"`
Networks   struct {
CloudNAT struct {
MinPortsPerVM int `json:"minPortsPerVM,omitempty"`
NatIPNames    []struct {
Name string `json:"name,omitempty"`
} `json:"natIPNames,omitempty"`
} `json:"cloudNAT,omitempty"`
} `json:"networks,omitempty"`
}
func main() {
shoot, e := parseShoot("test.yaml")
if e != nil {
fmt.Println(e)
}
var existingInfraConfig InfraConfig
err := json.Unmarshal(shoot.Spec.Provider.InfrastructureConfig.Raw, &existingInfraConfig)
fmt.Println(err)
existingInfraConfig.Networks.CloudNAT.NatIPNames[0].Name = "test2"
byteData, _ := json.Marshal(existingInfraConfig)
shoot.Spec.Provider.InfrastructureConfig = &runtime.RawExtension{
Raw:    byteData,
Object: nil,
}
fmt.Println(string(shoot.Spec.Provider.InfrastructureConfig.Raw))
aa, e := gyaml.Marshal(shoot)
if e != nil {
fmt.Println(e)
}
ioutil.WriteFile("test2.yaml", aa, 0644)
}
func parseShoot(path string) (*v1beta1.Shoot, error) {
var shootSpec *v1beta1.Shoot
bytes, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(bytes, &shootSpec)
if err != nil {
return nil, err
}
return shootSpec, nil
}
// this is the yaml
apiVersion: core.gardener.cloud/v1beta1
kind: Shoot
metadata:
name: test
namespace: ns
spec:
provider:
type: aaa
infrastructureConfig:
apiVersion: gcp.provider.extensions.gardener.cloud/v1alpha1
kind: InfrastructureConfig
networks:
cloudNAT:
minPortsPerVM: 10000
natIPNames:
- name: test1

This is what I got after writing the data to a file.

typemeta:
kind: Shoot
apiversion: core.gardener.cloud/v1beta1
objectmeta:
name: test
generatename: ""
namespace: ns
selflink: ""
uid: ""
resourceversion: ""
generation: 0
creationtimestamp: "0001-01-01T00:00:00Z"
deletiontimestamp: null
deletiongraceperiodseconds: null
labels: {}
annotations: {}
ownerreferences: []
finalizers: []
clustername: ""
managedfields: []
spec:
addons: null
cloudprofilename: ""
dns: null
extensions: []
hibernation: null
kubernetes:
allowprivilegedcontainers: null
clusterautoscaler: null
kubeapiserver: null
kubecontrollermanager: null
kubescheduler: null
kubeproxy: null
kubelet: null
version: ""
verticalpodautoscaler: null
networking:
type: ""
providerconfig: null
pods: null
nodes: null
services: null
maintenance: null
monitoring: null
provider:
type: aaa
controlplaneconfig: null
infrastructureconfig:
raw:
- 123
- 34
- 97
- 112
- 105
- 86
- 101
- 114
- 115
- 105
- 111
- 110
- 34
- 58
- 34
- 103
- 99
- 112
- 46
- 112
- 114
- 111
- 118
- 105
- 100
- 101
- 114
- 46
- 101
- 120
- 116
- 101
- 110
- 115
- 105
- 111
- 110
- 115
- 46
- 103
- 97
- 114
- 100
- 101
- 110
- 101
- 114
- 46
- 99
- 108
- 111
- 117
- 100
- 47
- 118
- 49
- 97
- 108
- 112
- 104
- 97
- 49
- 34
- 44
- 34
- 107
- 105
- 110
- 100
- 34
- 58
- 34
- 73
- 110
- 102
- 114
- 97
- 115
- 116
- 114
- 117
- 99
- 116
- 117
- 114
- 101
- 67
- 111
- 110
- 102
- 105
- 103
- 34
- 44
- 34
- 110
- 101
- 116
- 119
- 111
- 114
- 107
- 115
- 34
- 58
- 123
- 34
- 99
- 108
- 111
- 117
- 100
- 78
- 65
- 84
- 34
- 58
- 123
- 34
- 109
- 105
- 110
- 80
- 111
- 114
- 116
- 115
- 80
- 101
- 114
- 86
- 77
- 34
- 58
- 49
- 48
- 48
- 48
- 48
- 44
- 34
- 110
- 97
- 116
- 73
- 80
- 78
- 97
- 109
- 101
- 115
- 34
- 58
- 91
- 123
- 34
- 110
- 97
- 109
- 101
- 34
- 58
- 34
- 116
- 101
- 115
- 116
- 51
- 34
- 125
- 93
- 125
- 125
- 125
object: null
workers: []
purpose: null
region: ""
secretbindingname: ""
seedname: null
seedselector: null
resources: []
tolerations: []
exposureclassname: null
systemcomponents: null
status:
conditions: []
constraints: []
gardener:
id: ""
name: ""
version: ""
ishibernated: false
lastoperation: null
lasterrors: []
observedgeneration: 0
retrycyclestarttime: null
seedname: null
technicalid: ""
uid: ""
clusteridentity: null
advertisedaddresses: []
migrationstarttime: null
credentials: null

UPDATE

This is the complete file

apiVersion: core.gardener.cloud/v1beta1
kind: Shoot
metadata:
name: test
namespace: ns
spec:
provider:
type: aaa
infrastructureConfig:
apiVersion: gcp.provider.extensions.gardener.cloud/v1alpha1
kind: InfrastructureConfig
networks:
cloudNAT:
minPortsPerVM: 10000
natIPNames:
- name: test1

答案1

得分: 2

以下是展示如何使用yaml.Node的最小代码:

package main

import (
	"fmt"
	"strconv"

	"gopkg.in/yaml.v3"
)

var input = []byte(`
spec:
  provider:
    infrastructureConfig:
      networks:
        cloudNAT:
          natIPNames:
            - name: test
`)

func set(root *yaml.Node, path []string, value yaml.Node) {
	if len(path) == 0 {
		*root = value
		return
	}
	key := path[0]
	rest := path[1:]
	switch root.Kind {
	case yaml.DocumentNode:
		set(root.Content[0], path, value)
	case yaml.MappingNode:
		for i := 0; i < len(root.Content); i += 2 {
			if root.Content[i].Value == key {
				set(root.Content[i+1], rest, value)
				return
			}
		}
	case yaml.SequenceNode:
		index, err := strconv.Atoi(key)
		if err != nil {
			panic(err)
		}
		set(root.Content[index], rest, value)
	}
}

func main() {
	var doc yaml.Node
	if err := yaml.Unmarshal(input, &doc); err != nil {
		panic(err)
	}
	newVal := yaml.Node{
		Kind:  yaml.ScalarNode,
		Value: "test2",
	}
	set(&doc, []string{"spec", "provider", "infrastructureConfig", "networks", "cloudNAT", "natIPNames", "0", "name"}, newVal)
	out, err := yaml.Marshal(doc.Content[0])
	if err != nil {
		panic(err)
	}
	fmt.Println(string(out))
}

在 Go Playground 上运行

这段代码是危险的,因为它忽略了许多无效和错误的情况。一个完全健壮的实现超出了一个 Stack Overflow 的回答范围。

英文:

Here's minimal code to show how using yaml.Node generally works:

package main

import (
	&quot;fmt&quot;
	&quot;strconv&quot;

	&quot;gopkg.in/yaml.v3&quot;
)

var input = []byte(`
spec:
  provider:
    infrastructureConfig:
      networks:
        cloudNAT:
          natIPNames:
            - name: test
`)

func set(root *yaml.Node, path []string, value yaml.Node) {
	if len(path) == 0 {
		*root = value
		return
	}
	key := path[0]
	rest := path[1:]
	switch root.Kind {
	case yaml.DocumentNode:
		set(root.Content[0], path, value)
	case yaml.MappingNode:
		for i := 0; i &lt; len(root.Content); i += 2 {
			if root.Content[i].Value == key {
				set(root.Content[i+1], rest, value)
				return
			}
		}
	case yaml.SequenceNode:
		index, err := strconv.Atoi(key)
		if err != nil {
			panic(err)
		}
		set(root.Content[index], rest, value)
	}
}

func main() {
	var doc yaml.Node
	if err := yaml.Unmarshal(input, &amp;doc); err != nil {
		panic(err)
	}
	newVal := yaml.Node{
		Kind:  yaml.ScalarNode,
		Value: &quot;test2&quot;,
	}
	set(&amp;doc, []string{&quot;spec&quot;, &quot;provider&quot;, &quot;infrastructureConfig&quot;, &quot;networks&quot;, &quot;cloudNAT&quot;, &quot;natIPNames&quot;, &quot;0&quot;, &quot;name&quot;}, newVal)
	out, err := yaml.Marshal(doc.Content[0])
	if err != nil {
		panic(err)
	}
	fmt.Println(string(out))
}

On Go Playground

This code is dangerous since it ignores lots of invalid and error cases. A completely robust implementation is beyond an SO answer.

huangapple
  • 本文由 发表于 2022年3月30日 21:39:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/71678320.html
匿名

发表评论

匿名网友

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

确定