Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(secrets): preserve comments, order and aliases in the secrets ed…
…it commands Use yaml-v3 node-based lowlevel yaml parser to work with secret values. Signed-off-by: Timofey Kirillov <timofey.kirillov@flant.com>
- Loading branch information
1 parent
dba573f
commit 5bc6092
Showing
3 changed files
with
153 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package secret | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
|
||
yaml_v3 "gopkg.in/yaml.v3" | ||
) | ||
|
||
func MergeEncodedYaml(oldData, newData, oldEncodedData, newEncodedData []byte) ([]byte, error) { | ||
var oldConfig, newConfig, oldEncodedConfig, newEncodedConfig yaml_v3.Node | ||
|
||
for _, d := range []struct { | ||
Data []byte | ||
Node *yaml_v3.Node | ||
}{ | ||
{Data: oldData, Node: &oldConfig}, | ||
{Data: newData, Node: &newConfig}, | ||
{Data: oldEncodedData, Node: &oldEncodedConfig}, | ||
{Data: newEncodedData, Node: &newEncodedConfig}, | ||
} { | ||
if err := yaml_v3.Unmarshal(d.Data, d.Node); err != nil { | ||
return nil, fmt.Errorf("unable to unmarshal yaml data: %w", err) | ||
} | ||
} | ||
|
||
mergedNode, err := MergeEncodedYamlNode(&oldConfig, &newConfig, &oldEncodedConfig, &newEncodedConfig) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var resultData bytes.Buffer | ||
|
||
yamlEncoder := yaml_v3.NewEncoder(&resultData) | ||
yamlEncoder.SetIndent(2) | ||
if err := yamlEncoder.Encode(mergedNode); err != nil { | ||
return nil, fmt.Errorf("unable to marshal merged encoded data: %w", err) | ||
} | ||
|
||
return resultData.Bytes(), nil | ||
} | ||
|
||
func MergeEncodedYamlNode(oldConfig, newConfig, oldEncodedConfig, newEncodedConfig *yaml_v3.Node) (*yaml_v3.Node, error) { | ||
if newConfig.Kind != oldConfig.Kind { | ||
return newEncodedConfig, nil | ||
} | ||
|
||
switch newEncodedConfig.Kind { | ||
case yaml_v3.DocumentNode: | ||
for pos := 0; pos < len(newEncodedConfig.Content); pos += 1 { | ||
newValueNode, err := MergeEncodedYamlNode(oldConfig.Content[pos], newConfig.Content[pos], oldEncodedConfig.Content[pos], newEncodedConfig.Content[pos]) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to process document key %d: %w", pos, err) | ||
} | ||
newEncodedConfig.Content[pos] = newValueNode | ||
} | ||
|
||
case yaml_v3.MappingNode: | ||
for pos := 0; pos < len(newEncodedConfig.Content); pos += 2 { | ||
newValueNode, err := MergeEncodedYamlNode(oldConfig.Content[pos+1], newConfig.Content[pos+1], oldEncodedConfig.Content[pos+1], newEncodedConfig.Content[pos+1]) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to process map key %q: %w", newEncodedConfig.Content[pos].Value, err) | ||
} | ||
newEncodedConfig.Content[pos+1] = newValueNode | ||
} | ||
|
||
case yaml_v3.SequenceNode: | ||
for pos := 0; pos < len(newEncodedConfig.Content); pos += 1 { | ||
newValueNode, err := MergeEncodedYamlNode(oldConfig.Content[pos], newConfig.Content[pos], oldEncodedConfig.Content[pos], newEncodedConfig.Content[pos]) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to process array key %d: %w", pos, err) | ||
} | ||
newEncodedConfig.Content[pos] = newValueNode | ||
} | ||
|
||
case yaml_v3.AliasNode: | ||
newAliasNode, err := MergeEncodedYamlNode(oldConfig.Alias, newConfig.Alias, oldEncodedConfig.Alias, newEncodedConfig.Alias) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to process an alias node %q: %w", newEncodedConfig.Value, err) | ||
} | ||
newEncodedConfig.Alias = newAliasNode | ||
|
||
case yaml_v3.ScalarNode: | ||
if oldConfig.Value == newConfig.Value { | ||
return oldEncodedConfig, nil | ||
} | ||
return newEncodedConfig, nil | ||
} | ||
|
||
return newEncodedConfig, nil | ||
} |