appending to YAML file using go lang

I writing a golang program which append rule to the file as mentioned below Required format:

customRules:
  custom-rules.yaml: |-
    - rule: Pod Created in Kube Namespace
      append: true
      condition: and (k8s_audit_never_true)
      source: k8s_audit
    - rule: Create files below dev
      append: true
      condition: and (never_true)
      source: syscall

I wrote a go program which failing to be in the above format I can't get what Iam missing.

package main

import (
    "fmt"
    "io/ioutil"
    "log"

    "gopkg.in/yaml.v2"
)

type AutoGenerated struct {
    CustomRules CustomRules `yaml:"customRules"`
}
type CustomRulesYaml struct {
    Rule      string `yaml:"rule"`
    Append    bool   `yaml:"append"`
    Condition string `yaml:"condition"`
    Source    string `yaml:"source"`
}
type CustomRules struct {
    CustomRulesYaml []CustomRulesYaml `yaml:"custom-rules.yaml"`
}

func main() {
    // yfile, err := ioutil.ReadFile("/home/revaa/falco/custom_rules.yaml")
    // if err != nil {
    //  log.Fatal(err)
    // }
    c1 := CustomRulesYaml{"K8s serviceaccount created", false, "(never_true)", "k8s-audit"}
    c2 := CustomRulesYaml{"k8s service created", false, "never_true", "k8s-audit"}
    c := []CustomRulesYaml{c1, c2}
    c3 := CustomRules{c}
    data := AutoGenerated{c3}
    check, err := yaml.Marshal(&data)

    if err != nil {

        log.Fatal(err)
    }

    err2 := ioutil.WriteFile("/home/revaa/falco/custom_rules.yaml", check, 0)

    if err2 != nil {

        log.Fatal(err2)
    }

    fmt.Println("data written")

}

Here is my go code, on running the program the YAML is not getting appended in the above format. The values are appended as below instead.

customRules:
  custom-rules.yaml:
  - rule: K8s serviceaccount created
    append: false
    condition: (never_true)
    source: k8s-audit
  - rule: k8s service created
    append: false
    condition: never_true
    source: k8s-audit

Why Iam not getting the YAML file in the required format?


Solution 1:

Your required input format represents the following YAML structure:

  • There is a dictionary with one key-value pair.
    • The key is customRules.
    • The value is a dictionary.

The dictionary stored in the value above has one entry:

  • The key is custom-rules.yaml.
  • The value is a string.

The string stored in the value above is:

"- rule: Pod Created in Kube Namespace\n  append: true\n  condition: and (k8s_audit_never_true)\n  source: k8s_audit\n- rule: Create files below dev\n  append: true\n  condition: and (never_true)\n  source: syscall"

That is, this is not a list type. It's a single string.

Now, the fact is that this single string is valid—albeit slightly dodgy—YAML, and if read, it will produce a list (a slice, in Go) of 2 elements, each of which is a dictionary (generally corresponding to a map or struct type in Go).

Your code is close to right, then, if you really do need this. Instead of wrapping the []CustomRulesYaml in a type, you need to marshal twice. Here's a variant of your code on the Go Playground whose output is:

custom-rules.yaml: |
  - rule: K8s serviceaccount created
    append: false
    condition: (never_true)
    source: k8s-audit
  - rule: k8s service created
    append: false
    condition: never_true
    source: k8s-audit

Now, note that this output has a pipe symbol | without a suffix hyphen -. That's because the marshaled string, string(asBytes), ends with a newline. It probably should end with a newline, which is why yaml.Marshal produced one. But your sample input doesn't end with a newline, which is why your sample input has |- instead of just |: the - means "don't include that newline".

To get that from your existing code, you will have to strip off the newline, e.g., add:

asBytes = asBytes[0:len(asBytes)-1] // delete trailing newline

before building the string in c3 and marshaling it.