Add the ability to copy configs between stacks (#4971)

This commit is contained in:
Paul Stack 2020-07-17 13:14:10 +03:00 committed by GitHub
parent 418e2291a2
commit 6d09fe32df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 415 additions and 4 deletions

View file

@ -5,6 +5,9 @@ CHANGELOG
- Add support for streamInvoke during update
[#4990](https://github.com/pulumi/pulumi/pull/4990)
- Add ability to copy configuration values between stacks
[#4971](https://github.com/pulumi/pulumi/pull/4971)
- Add logic to parce pulumi venv on github action
[#4994](https://github.com/pulumi/pulumi/pull/4994)

View file

@ -80,10 +80,162 @@ func newConfigCmd() *cobra.Command {
cmd.AddCommand(newConfigRmCmd(&stack))
cmd.AddCommand(newConfigSetCmd(&stack))
cmd.AddCommand(newConfigRefreshCmd(&stack))
cmd.AddCommand(newConfigCopyCmd(&stack))
return cmd
}
func newConfigCopyCmd(stack *string) *cobra.Command {
var path bool
var destinationStackName string
cpCommand := &cobra.Command{
Use: "cp [key]",
Short: "Copy config to another stack",
Long: "Copies the config from the current stack to the destination stack. If `key` is omitted,\n" +
"then all of the config from the current stack will be copied to the destination stack.",
Args: cmdutil.MaximumNArgs(1),
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
opts := display.Options{
Color: cmdutil.GetGlobalColorization(),
}
// Get current stack and ensure that it is a different stack to the destination stack
currentStack, err := requireStack(*stack, false, opts, true /*setCurrent*/)
if err != nil {
return err
}
if currentStack.Ref().Name().String() == destinationStackName {
return errors.New("current stack and destination stack are the same")
}
currentProjectStack, err := loadProjectStack(currentStack)
if err != nil {
return err
}
// Get the destination stack
destinationStack, err := requireStack(destinationStackName, false, opts, false /*setCurrent*/)
if err != nil {
return err
}
destinationProjectStack, err := loadProjectStack(destinationStack)
if err != nil {
return err
}
// Do we need to copy a single value or the entire map
if len(args) > 0 {
// A single key was specified so we only need to copy that specific value
return copySingleConfigKey(args[0], path, currentStack, currentProjectStack, destinationStack,
destinationProjectStack)
}
return copyEntireConfigMap(currentStack, currentProjectStack, destinationStack, destinationProjectStack)
}),
}
cpCommand.PersistentFlags().BoolVar(
&path, "path", false,
"The key contains a path to a property in a map or list to set")
cpCommand.PersistentFlags().StringVarP(
&destinationStackName, "dest", "d", "",
"The name of the new stack to copy the config to")
return cpCommand
}
func copySingleConfigKey(configKey string, path bool, currentStack backend.Stack,
currentProjectStack *workspace.ProjectStack, destinationStack backend.Stack,
destinationProjectStack *workspace.ProjectStack) error {
var decrypter config.Decrypter
key, err := parseConfigKey(configKey)
if err != nil {
return errors.Wrap(err, "invalid configuration key")
}
v, ok, err := currentProjectStack.Config.Get(key, path)
if err != nil {
return err
}
if ok {
if v.Secure() {
var err error
if decrypter, err = getStackDecrypter(currentStack); err != nil {
return errors.Wrap(err, "could not create a decrypter")
}
} else {
decrypter = config.NewPanicCrypter()
}
encrypter, cerr := getStackEncrypter(destinationStack)
if cerr != nil {
return cerr
}
val, err := v.Copy(decrypter, encrypter)
if err != nil {
return err
}
err = destinationProjectStack.Config.Set(key, val, path)
if err != nil {
return err
}
return saveProjectStack(destinationStack, destinationProjectStack)
}
return errors.Errorf(
"configuration key '%s' not found for stack '%s'", prettyKey(key), currentStack.Ref())
}
func copyEntireConfigMap(currentStack backend.Stack,
currentProjectStack *workspace.ProjectStack, destinationStack backend.Stack,
destinationProjectStack *workspace.ProjectStack) error {
var decrypter config.Decrypter
currentConfig := currentProjectStack.Config
if currentConfig.HasSecureValue() {
dec, decerr := getStackDecrypter(currentStack)
if decerr != nil {
return decerr
}
decrypter = dec
} else {
decrypter = config.NewPanicCrypter()
}
encrypter, cerr := getStackEncrypter(destinationStack)
if cerr != nil {
return cerr
}
newProjectConfig, err := currentConfig.Copy(decrypter, encrypter)
if err != nil {
return err
}
var requiresSaving bool
for key, val := range newProjectConfig {
err = destinationProjectStack.Config.Set(key, val, false)
if err != nil {
return err
}
requiresSaving = true
}
// The use of `requiresSaving` here ensures that there was actually some config
// that needed saved, otherwise it's an unnecessary save call
if requiresSaving {
err := saveProjectStack(destinationStack, destinationProjectStack)
if err != nil {
return err
}
}
return nil
}
func newConfigGetCmd(stack *string) *cobra.Command {
var jsonOut bool
var path bool
@ -431,7 +583,7 @@ func listConfig(stack backend.Stack, showSecrets bool, jsonOut bool) error {
// By default, we will use a blinding decrypter to show "[secret]". If requested, display secrets in plaintext.
decrypter := config.NewBlindingDecrypter()
if cfg.HasSecureValue() && showSecrets {
dec, decerr := getStackDencrypter(stack)
dec, decerr := getStackDecrypter(stack)
if decerr != nil {
return decerr
}
@ -518,7 +670,7 @@ func getConfig(stack backend.Stack, key config.Key, path, jsonOut bool) error {
var d config.Decrypter
if v.Secure() {
var err error
if d, err = getStackDencrypter(stack); err != nil {
if d, err = getStackDecrypter(stack); err != nil {
return errors.Wrap(err, "could not create a decrypter")
}
} else {

View file

@ -37,7 +37,7 @@ func getStackEncrypter(s backend.Stack) (config.Encrypter, error) {
return sm.Encrypter()
}
func getStackDencrypter(s backend.Stack) (config.Decrypter, error) {
func getStackDecrypter(s backend.Stack) (config.Decrypter, error) {
sm, err := getStackSecretsManager(s)
if err != nil {
return nil, err

View file

@ -61,7 +61,7 @@ This command lists data about previous updates for a stack.`,
}
var decrypter config.Decrypter
if showSecrets {
crypter, err := getStackDencrypter(s)
crypter, err := getStackDecrypter(s)
if err != nil {
return errors.Wrap(err, "decrypting secrets")
}

View file

@ -134,6 +134,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -323,12 +325,16 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=

View file

@ -24,6 +24,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -80,10 +81,14 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=

View file

@ -15,6 +15,8 @@ require (
github.com/golang/protobuf v1.3.5
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645
github.com/hashicorp/go-multierror v1.0.0
github.com/kr/pretty v0.2.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/mattn/go-runewidth v0.0.8 // indirect
github.com/mitchellh/go-ps v1.0.0

View file

@ -32,6 +32,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -104,10 +105,14 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=

View file

@ -208,3 +208,21 @@ func decryptAES256GCM(ciphertext []byte, key []byte, nonce []byte) (string, erro
return string(msg), err
}
// Crypter that just adds a prefix to the plaintext string when encrypting,
// and removes the prefix from the ciphertext when decrypting, for use in tests.
type prefixCrypter struct {
prefix string
}
func newPrefixCrypter(prefix string) Crypter {
return prefixCrypter{prefix: prefix}
}
func (c prefixCrypter) DecryptValue(ciphertext string) (string, error) {
return strings.TrimPrefix(ciphertext, c.prefix), nil
}
func (c prefixCrypter) EncryptValue(plaintext string) (string, error) {
return c.prefix + plaintext, nil
}

View file

@ -43,6 +43,20 @@ func (m Map) Decrypt(decrypter Decrypter) (map[Key]string, error) {
return r, nil
}
func (m Map) Copy(decrypter Decrypter, encrypter Encrypter) (Map, error) {
newConfig := make(Map)
for k, c := range m {
val, err := c.Copy(decrypter, encrypter)
if err != nil {
return nil, err
}
newConfig[k] = val
}
return newConfig, nil
}
// HasSecureValue returns true if the config map contains a secure (encrypted) value.
func (m Map) HasSecureValue() bool {
for _, v := range m {

View file

@ -1170,6 +1170,82 @@ func TestSetFail(t *testing.T) {
}
}
func TestCopyMap(t *testing.T) {
tests := []struct {
Config Map
Expected Map
}{
{
Config: Map{
MustMakeKey("my", "testKey"): NewValue("testValue"),
},
Expected: Map{
MustMakeKey("my", "testKey"): NewValue("testValue"),
},
},
{
Config: Map{
MustMakeKey("my", "testKey"): NewSecureValue("stackAsecurevalue"),
},
Expected: Map{
MustMakeKey("my", "testKey"): NewSecureValue("stackBsecurevalue"),
},
},
{
Config: Map{
MustMakeKey("my", "testKey"): NewObjectValue(`{"inner":"value"}`),
},
Expected: Map{
MustMakeKey("my", "testKey"): NewObjectValue(`{"inner":"value"}`),
},
},
{
Config: Map{
MustMakeKey("my", "testKey"): NewSecureObjectValue(`{"inner":{"secure":"stackAsecurevalue"}}`),
},
Expected: Map{
MustMakeKey("my", "testKey"): NewSecureObjectValue(`{"inner":{"secure":"stackBsecurevalue"}}`),
},
},
{
Config: Map{
//nolint:lll
MustMakeKey("my", "testKey"): NewSecureObjectValue(`[{"inner":{"secure":"stackAsecurevalue"}},{"secure":"stackAsecurevalue2"}]`),
},
Expected: Map{
//nolint:lll
MustMakeKey("my", "testKey"): NewSecureObjectValue(`[{"inner":{"secure":"stackBsecurevalue"}},{"secure":"stackBsecurevalue2"}]`),
},
},
{
Config: Map{
MustMakeKey("my", "test.Key"): NewValue("testValue"),
},
Expected: Map{
MustMakeKey("my", "test.Key"): NewValue("testValue"),
},
},
{
Config: Map{
MustMakeKey("my", "name"): NewObjectValue(`[["value"]]`),
},
Expected: Map{
MustMakeKey("my", "name"): NewObjectValue(`[["value"]]`),
},
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%v", test), func(t *testing.T) {
newConfig, err := test.Config.Copy(newPrefixCrypter("stackA"), newPrefixCrypter("stackB"))
assert.NoError(t, err)
assert.Equal(t, test.Expected, newConfig)
})
}
}
func roundtripMapYAML(m Map) (Map, error) {
return roundtripMap(m, yaml.Marshal, yaml.Unmarshal)
}

View file

@ -72,6 +72,46 @@ func (c Value) Value(decrypter Decrypter) (string, error) {
return decrypter.DecryptValue(c.value)
}
func (c Value) Copy(decrypter Decrypter, encrypter Encrypter) (Value, error) {
var val Value
raw, err := c.Value(decrypter)
if err != nil {
return Value{}, err
}
if c.Secure() {
if c.Object() {
objVal, err := c.ToObject()
if err != nil {
return Value{}, err
}
encryptedObj, err := reencryptObject(objVal, decrypter, encrypter)
if err != nil {
return Value{}, err
}
json, err := json.Marshal(encryptedObj)
if err != nil {
return Value{}, err
}
val = NewSecureObjectValue(string(json))
} else {
enc, eerr := encrypter.EncryptValue(raw)
if eerr != nil {
return Value{}, eerr
}
val = NewSecureValue(enc)
}
} else {
if c.Object() {
val = NewObjectValue(raw)
} else {
val = NewValue(raw)
}
}
return val, nil
}
func (c Value) SecureValues(decrypter Decrypter) ([]string, error) {
d := NewTrackingDecrypter(decrypter)
if _, err := c.Value(d); err != nil {
@ -240,6 +280,53 @@ func isSecureValue(v interface{}) (bool, string) {
return false, ""
}
func reencryptObject(v interface{}, decrypter Decrypter, encrypter Encrypter) (interface{}, error) {
reencryptIt := func(val interface{}) (interface{}, error) {
if isSecure, secureVal := isSecureValue(val); isSecure {
newVal := NewSecureValue(secureVal)
raw, err := newVal.Value(decrypter)
if err != nil {
return nil, err
}
encVal, err := encrypter.EncryptValue(raw)
if err != nil {
return nil, err
}
m := make(map[string]string)
m["secure"] = encVal
return m, nil
}
return reencryptObject(val, decrypter, encrypter)
}
switch t := v.(type) {
case map[string]interface{}:
m := make(map[string]interface{})
for key, val := range t {
encrypted, err := reencryptIt(val)
if err != nil {
return nil, err
}
m[key] = encrypted
}
return m, nil
case []interface{}:
a := make([]interface{}, len(t))
for i, val := range t {
encrypted, err := reencryptIt(val)
if err != nil {
return nil, err
}
a[i] = encrypted
}
return a, nil
}
return v, nil
}
// decryptObject returns a new object with all secure values in the object converted to decrypted strings.
func decryptObject(v interface{}, decrypter Decrypter) (interface{}, error) {
decryptIt := func(val interface{}) (interface{}, error) {

View file

@ -245,6 +245,43 @@ func TestSecureValues(t *testing.T) {
}
}
func TestCopyValue(t *testing.T) {
tests := []struct {
Val Value
Expected Value
}{
{
Val: NewValue("value"),
Expected: NewValue("value"),
},
{
Val: NewObjectValue(`{"foo":"bar"}`),
Expected: NewObjectValue(`{"foo":"bar"}`),
},
{
Val: NewSecureObjectValue(`{"foo":{"secure":"stackAsecurevalue"}}`),
Expected: NewSecureObjectValue(`{"foo":{"secure":"stackBsecurevalue"}}`),
},
{
Val: NewSecureValue("stackAsecurevalue"),
Expected: NewSecureValue("stackBsecurevalue"),
},
{
Val: NewSecureObjectValue(`["a",{"secure":"stackAalpha"},{"test":{"secure":"stackAbeta"}}]`),
Expected: NewSecureObjectValue(`["a",{"secure":"stackBalpha"},{"test":{"secure":"stackBbeta"}}]`),
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%v", test), func(t *testing.T) {
newConfig, err := test.Val.Copy(newPrefixCrypter("stackA"), newPrefixCrypter("stackB"))
assert.NoError(t, err)
assert.Equal(t, test.Expected, newConfig)
})
}
}
func roundtripValueYAML(v Value) (Value, error) {
return roundtripValue(v, yaml.Marshal, yaml.Unmarshal)
}

View file

@ -130,6 +130,8 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -314,12 +316,16 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=