Given the yamls like below:
default.yaml:
services:
A:
host: "google.com"
path: "search"
scheme: "https"
B:
host: "bing.com"
path: "find"
logging:
level: WARN
staging.yaml:
services:
A:
path: "search2"
I want this merged yaml:
services:
A:
host: "google.com"
path: "search2"
scheme: "https"
B:
host: "bing.com"
path: "find"
logging:
level: WARN
Is there a way to achieve this kind of merge/patch behavior in go? I'm playing around with mergo
, but currently, it converts services["A"].host
and services["A"].scheme
to nil
or ""
in the merged object representation depending on whether the corresponding struct field is a value or pointer.
import (
"io/ioutil"
"path"
"github.com/imdario/mergo"
"gopkg.in/yaml.v2"
)
type Logging struct {
Level *string `json:"level,omitempty" yaml:"level,omitempty"`
}
type Service struct {
Host *string `json:"host,omitempty" yaml:"host,omitempty"`
Path *string `json:"path,omitempty" yaml:"path,omitempty"`
Scheme *string `json:"scheme,omitempty" yaml:"scheme,omitempty"`
Schedule *string `json:"schedule,omitempty" yaml:"schedule,omitempty"`
Count *int `json:"count,omitempty" yaml:"count,omitempty"`
}
type Config struct {
Services map[string]Service `json:"services,omitempty" yaml:"services,omitempty"`
Logging *Logging `json:"logging,omitempty" yaml:"logging,omitempty"`
}
// error handling omitted for brevity
func main() {
var defc Config
yamlFile, _ := ioutil.ReadFile("default.yaml"))
_ = yaml.Unmarshal(yamlFile, &defc)
var envc Config
yamlFile, _ = ioutil.ReadFile("staging.yaml"))
_ = yaml.Unmarshal(yamlFile, &envc)
_ = mergo.Merge(&defc, envc, mergo.WithOverride)
}
The motivation is to maintain env-specific yaml files that contain various configuration. I want to keep a base yaml (default.yaml
) that defines all the configuration, and then for each environment (dev/staging/prod...) define only the delta in those yamls to keep things DRY.
To give an equivalent from js/node, this is roughly what I'm looking for: https://runkit.com/embed/j9xi2bu00a74