diff --git a/api/internal/generators/configmap.go b/api/internal/generators/configmap.go index 47498aaa58..2f322f7dd3 100644 --- a/api/internal/generators/configmap.go +++ b/api/internal/generators/configmap.go @@ -37,7 +37,7 @@ func MakeConfigMap( if err != nil { return nil, err } - if err = rn.LoadMapIntoConfigMapData(m); err != nil { + if err = rn.LoadMapIntoConfigMapData(m.values); err != nil { return nil, err } err = copyLabelsAndAnnotations(rn, args.Options) diff --git a/api/internal/generators/secret.go b/api/internal/generators/secret.go index 9afaff156a..6a6ef8f68f 100644 --- a/api/internal/generators/secret.go +++ b/api/internal/generators/secret.go @@ -50,7 +50,7 @@ func MakeSecret( if err != nil { return nil, err } - if err = rn.LoadMapIntoSecretData(m); err != nil { + if err = rn.LoadMapIntoSecretData(m.values); err != nil { return nil, err } copyLabelsAndAnnotations(rn, args.Options) diff --git a/api/internal/generators/utils.go b/api/internal/generators/utils.go index b570c7e91b..6368a2d468 100644 --- a/api/internal/generators/utils.go +++ b/api/internal/generators/utils.go @@ -36,25 +36,33 @@ kind: %s return rn, nil } +type orderedMap struct { + keys []string + values map[string]string +} + func makeValidatedDataMap( - ldr ifc.KvLoader, name string, sources types.KvPairSources) (map[string]string, error) { + ldr ifc.KvLoader, name string, sources types.KvPairSources, +) (orderedMap, error) { pairs, err := ldr.Load(sources) if err != nil { - return nil, errors.WrapPrefix(err, "loading KV pairs", 0) + return orderedMap{}, errors.WrapPrefix(err, fmt.Sprintf("loading KV pairs, name: %s", name), 0) } - knownKeys := make(map[string]string) + + seen := make(map[string]struct{}) + values := make(map[string]string, len(pairs)) + keys := make([]string, len(pairs)) + for _, p := range pairs { - // legal key: alphanumeric characters, '-', '_' or '.' if err := ldr.Validator().ErrIfInvalidKey(p.Key); err != nil { - return nil, err - } - if _, ok := knownKeys[p.Key]; ok { - return nil, errors.Errorf( - "configmap %s illegally repeats the key `%s`", name, p.Key) + return orderedMap{}, errors.WrapPrefix(err, fmt.Sprintf("invalid key %s, name %s", p.Key, name), 0) } - knownKeys[p.Key] = p.Value + seen[p.Key] = struct{}{} + keys = append(keys, p.Key) + values[p.Key] = p.Value } - return knownKeys, nil + + return orderedMap{keys: keys, values: values}, nil } // copyLabelsAndAnnotations copies labels and annotations from diff --git a/api/internal/generators/utils_test.go b/api/internal/generators/utils_test.go index fbb2f262cf..77776f6ed2 100644 --- a/api/internal/generators/utils_test.go +++ b/api/internal/generators/utils_test.go @@ -1,13 +1,15 @@ // Copyright 2020 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 -package generators_test +package generators import ( "testing" "github.com/stretchr/testify/require" - . "sigs.k8s.io/kustomize/api/internal/generators" + "sigs.k8s.io/kustomize/api/ifc" + valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" + "sigs.k8s.io/kustomize/api/types" ) func TestParseFileSource(t *testing.T) { @@ -49,3 +51,101 @@ func TestParseFileSource(t *testing.T) { }) } } + +func TestMakeValidateDataMapOrder(t *testing.T) { + tests := []struct { + name string + pairs []types.Pair + wantKeys []string + wantValues map[string]string + wantErr bool + validatorFn func() ifc.Validator + }{ + { + name: "single pair", + pairs: []types.Pair{ + {Key: "key", Value: "value"}, + }, + wantKeys: []string{"key"}, + wantValues: map[string]string{"key": "value"}, + }, + { + name: "multiple pairs preserve order", + pairs: []types.Pair{ + {Key: "A", Value: "1"}, + {Key: "B", Value: "2"}, + {Key: "C", Value: "3"}, + }, + wantKeys: []string{"A", "B", "C"}, + wantValues: map[string]string{"A": "1", "B": "2", "C": "3"}, + }, + { + name: "multiple pairs out of order - loader order wins", + pairs: []types.Pair{ + {Key: "third", Value: "3"}, + {Key: "first", Value: "1"}, + {Key: "second", Value: "2"}, + }, + wantKeys: []string{"third", "first", "second"}, + wantValues: map[string]string{"third": "3", "first": "1", "second": "2"}, + }, + { + name: "duplicate key returns error", + pairs: []types.Pair{ + {Key: "dup", Value: "1"}, + {Key: "dup", Value: "2"}, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + loader := &fakeKvLoader{ + Pairs: tt.pairs, + Val: func() ifc.Validator { + if tt.validatorFn != nil { + return tt.validatorFn() + } + return valtest_test.MakeFakeValidator() + }(), + } + + args := types.ConfigMapArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "envConfigMap", + KvPairSources: types.KvPairSources{ + EnvSources: []string{"app.env"}, + }, + }, + } + + res, err := makeValidatedDataMap(loader, args.Name, args.KvPairSources) + + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Exactly(t, tt.wantKeys, res.keys) + require.Exactly(t, tt.wantValues, res.values) + }) + } +} + +type fakeKvLoader struct { + Pairs []types.Pair + Val ifc.Validator +} + +func (f *fakeKvLoader) Load(_ types.KvPairSources) ([]types.Pair, error) { + return f.Pairs, nil +} + +func (f *fakeKvLoader) Validator() ifc.Validator { + if f.Val != nil { + return f.Val + } + return valtest_test.MakeFakeValidator() +}