How can I marshall a golang nested structs with YAML

Issue

How do I represent the match labels section of this struct to print out.
I try to add the ‘NetworkPolicySpecPodSelector’ struct in a format like metadata but my format is incorrect.

package main

import (
    "fmt"
    "io/ioutil"

    "gopkg.in/yaml.v2"
)

type Metadata struct {
    Name      string `yaml:"name"`
    Namespace string `yaml:"namespace"`
}

type Spec struct {
    PodSelector NetworkPolicySpecPodSelector `yaml:"Spec"`
}

type NetworkPolicySpecPodSelector struct {
    MatchLabels map[string][]string `yaml:"matchLabels"`
}

type Ingress struct {
}

type NetworkPolicy struct {
    ApiVersion string   `yaml:"apiVersion`
    Kind       string   `yaml:"kind"`
    Metadata   Metadata `yaml:"metadata"`
    Spec       struct {
        PodSelector NetworkPolicySpecPodSelector
    }
    PolicyTypes []string `yaml:"policyTypes"`
}

func main() {
    np := NetworkPolicy{
        ApiVersion: "networking.k8s.io/v1",
        Kind:       "NetworkPolicy",
        Metadata: Metadata{
            Name:      "allow-ingress",
            Namespace: "default",
        },
        PolicyTypes: []string{"Ingress"},
    }

    yamlData, err := yaml.Marshal(&np)

    if err != nil {
        fmt.Printf("Error while Marshaling. %v", err)
    }

    fileName := "test.yaml"
    err = ioutil.WriteFile(fileName, yamlData, 0644)
    if err != nil {
        panic("Unable to write data into the file")
    }
}

My output is here:

apiversion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress
  namespace: default
spec:
  podselector:
    matchLabels: {}
policyTypes:
- Ingress

Desired output:

apiversion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress
  namespace: default
spec:
  podselector:
    matchLabels: 
      env:prod
policyTypes:
- Ingress

still needs to grow but Im struggling with this part. Goal of the project is to output a Kubernetes network policy using YAML.

Solution

First you need to fix struct field tags to reflect your desired structure. Spec in NetworkPolicy needs to serialize as key spec and its field PodSelector as podselector:

type NetworkPolicy struct {
    ApiVersion  string   `yaml:"apiVersion`
    Kind        string   `yaml:"kind"`
    Metadata    Metadata `yaml:"metadata"`
    Spec        Spec     `yaml:"spec"`
    PolicyTypes []string `yaml:"policyTypes"`
}

type Spec struct {
    PodSelector NetworkPolicySpecPodSelector `yaml:"podselector"`
}

Then you can simply use literals:

    np := NetworkPolicy{
        ApiVersion: "networking.k8s.io/v1",
        Kind:       "NetworkPolicy",
        Metadata: Metadata{
            Name:      "allow-ingress",
            Namespace: "default",
        },
        Spec: Spec{
            PodSelector: NetworkPolicySpecPodSelector{
                MatchLabels: map[string][]string{
                    "env": []string{"prod"},
                },
            },
        },
        PolicyTypes: []string{"Ingress"},
    }

Here is full example on playground: https://go.dev/play/p/xJ-mmCVcv2M

NOTE: In your code snippet the type of MatchLabels is map[string][]string. I kept it like that although from example it looks like you want map[string]string.

Answered By – blami

Answer Checked By – Gilberto Lyons (GoLangFix Admin)

Leave a Reply

Your email address will not be published.