英文:
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))
}
这段代码是危险的,因为它忽略了许多无效和错误的情况。一个完全健壮的实现超出了一个 Stack Overflow 的回答范围。
英文:
Here's minimal code to show how using yaml.Node
generally works:
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))
}
This code is dangerous since it ignores lots of invalid and error cases. A completely robust implementation is beyond an SO answer.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论