PJEM
PJEM

Reputation: 667

update property in a yaml file

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

Upvotes: 3

Views: 1678

Answers (1)

flyx
flyx

Reputation: 39678

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

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.

Upvotes: 2

Related Questions