package data import ( "encoding/json" "fmt" "os" "path/filepath" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" "gopkg.in/yaml.v3" ) func protoMap(message proto.Message) (map[string]any, error) { raw, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(message) if err != nil { return nil, err } var value map[string]any if err = json.Unmarshal(raw, &value); err != nil { return nil, err } delete(value, "config_path") return value, nil } func setYAMLMapping(node *yaml.Node, key string, value any) error { if node.Kind == yaml.DocumentNode { node = node.Content[0] } if node.Kind != yaml.MappingNode { return fmt.Errorf("configuration root is not a mapping") } raw, err := yaml.Marshal(value) if err != nil { return err } var replacement yaml.Node if err = yaml.Unmarshal(raw, &replacement); err != nil { return err } for i := 0; i < len(node.Content); i += 2 { if node.Content[i].Value == key { node.Content[i+1] = replacement.Content[0] return nil } } node.Content = append(node.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: key}, replacement.Content[0]) return nil } func (d *Data) persistConfig() error { if d.admin == nil || d.admin.ConfigPath == "" { return nil } raw, err := os.ReadFile(d.admin.ConfigPath) if err != nil { return err } var document yaml.Node if err = yaml.Unmarshal(raw, &document); err != nil { return err } dataValue, err := protoMap(d.config) if err != nil { return err } adminValue, err := protoMap(d.admin) if err != nil { return err } if err = setYAMLMapping(&document, "data", dataValue); err != nil { return err } if err = setYAMLMapping(&document, "admin", adminValue); err != nil { return err } output, err := yaml.Marshal(&document) if err != nil { return err } if err = os.MkdirAll(filepath.Dir(d.admin.ConfigPath), 0o755); err != nil { return err } temporary, err := os.CreateTemp(filepath.Dir(d.admin.ConfigPath), ".kra-config-*.yaml") if err != nil { return err } tempName := temporary.Name() defer os.Remove(tempName) if _, err = temporary.Write(output); err != nil { _ = temporary.Close() return err } if err = temporary.Chmod(0o600); err != nil { _ = temporary.Close() return err } if err = temporary.Close(); err != nil { return err } return os.Rename(tempName, d.admin.ConfigPath) }