Skip to content

Commit 63b4b8c

Browse files
committed
fix: fix op patching to not add elements if key already exists
1 parent 4543273 commit 63b4b8c

3 files changed

Lines changed: 112 additions & 24 deletions

File tree

e2e/tests/config/config.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,51 @@ var _ = DevSpaceDescribe("config", func() {
244244
framework.ExpectEqual(latestConfig.Deployments["test2"].Name, "test2")
245245
})
246246

247+
ginkgo.It("should not be able to add in patch if key already exists", func() {
248+
tempDir, err := framework.CopyToTempDir("tests/config/testdata/patch-add-dont-overwrite-existing-key")
249+
framework.ExpectNoError(err)
250+
defer framework.CleanupTempDir(initialDir, tempDir)
251+
252+
configBuffer := &bytes.Buffer{}
253+
printCmd := &cmd.PrintCmd{
254+
GlobalFlags: &flags.GlobalFlags{
255+
ConfigPath: "devspace.yaml",
256+
Profiles: []string{"deploy"},
257+
},
258+
Out: configBuffer,
259+
SkipInfo: true,
260+
}
261+
262+
err = printCmd.Run(f)
263+
framework.ExpectError(err)
264+
})
265+
266+
ginkgo.It("should be able to add in patch if key does not already exists", func() {
267+
tempDir, err := framework.CopyToTempDir("tests/config/testdata/patch-add-dont-overwrite-existing-key")
268+
framework.ExpectNoError(err)
269+
defer framework.CleanupTempDir(initialDir, tempDir)
270+
271+
configBuffer := &bytes.Buffer{}
272+
printCmd := &cmd.PrintCmd{
273+
GlobalFlags: &flags.GlobalFlags{
274+
ConfigPath: "devspace.yaml",
275+
Profiles: []string{"patch-ok"},
276+
},
277+
Out: configBuffer,
278+
SkipInfo: true,
279+
}
280+
281+
err = printCmd.Run(f)
282+
framework.ExpectNoError(err)
283+
284+
latestConfig := &latest.Config{}
285+
err = yaml.Unmarshal(configBuffer.Bytes(), latestConfig)
286+
framework.ExpectNoError(err)
287+
288+
// validate config
289+
framework.ExpectEqual(string(latestConfig.Images["importme"].RebuildStrategy), "ignoreContextChanges")
290+
})
291+
247292
ginkgo.It("should load profile cached and uncached", func() {
248293
tempDir, err := framework.CopyToTempDir("tests/config/testdata/profile")
249294
framework.ExpectNoError(err)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
version: v1beta11
2+
images:
3+
importme:
4+
image: golang:1.17
5+
context: ../..
6+
profiles:
7+
- name: deploy
8+
patches:
9+
- op: add
10+
path: images.importme.context
11+
value: ../..
12+
- name: patch-ok
13+
patches:
14+
- op: add
15+
path: images.importme.rebuildStrategy
16+
value: ignoreContextChanges

pkg/devspace/config/loader/patch/operation.go

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package patch
22

33
import (
44
"fmt"
5-
65
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
76
yaml "gopkg.in/yaml.v3"
87
)
@@ -35,38 +34,66 @@ func (op *Operation) Perform(doc *yaml.Node) error {
3534
return err
3635
}
3736

38-
if len(matches) == 0 {
39-
if op.Op == opAdd {
40-
matches, err = getParents(doc, op.Path)
41-
if err != nil {
42-
return fmt.Errorf("could not add using path: %s", op.Path)
37+
if len(matches) == 0 && op.Op != opAdd {
38+
return fmt.Errorf("%s operation does not apply: doc is missing path: %s", op.Op, op.Path)
39+
}
40+
41+
var f func(parent *yaml.Node, match *yaml.Node)
42+
43+
switch op.Op {
44+
case opAdd:
45+
f = op.add
46+
47+
var pathMatches bool
48+
49+
if len(matches) > 0 {
50+
pathMatches = true
51+
52+
if matches[0].Kind == yaml.MappingNode || matches[0].Kind == yaml.SequenceNode {
53+
break
4354
}
55+
}
4456

45-
parentPath := op.Path.getParentPath()
46-
propertName := op.Path.getChildName()
47-
if op.Value != nil {
48-
propertyValue := op.Value.Content[0]
49-
op.Value = createMappingNode(propertName, propertyValue)
57+
originalMatches := matches
58+
59+
matches, err = getParents(doc, op.Path)
60+
if err != nil {
61+
return fmt.Errorf("could not add using path: %s", op.Path)
62+
}
63+
64+
if len(matches) > 0 && pathMatches {
65+
if matches[0].Kind == yaml.SequenceNode {
66+
matches = originalMatches
67+
break
68+
} else {
69+
// we are trying to overwrite an existing key in a map, don't do that!
70+
return fmt.Errorf(
71+
"attempting add operation for non array/object path '%s' which already exists",
72+
op.Path,
73+
)
5074
}
51-
op.Path = OpPath(parentPath)
52-
} else {
53-
return fmt.Errorf("%s operation does not apply: doc is missing path: %s", op.Op, op.Path)
5475
}
76+
77+
parentPath := op.Path.getParentPath()
78+
propertyName := op.Path.getChildName()
79+
if op.Value != nil {
80+
propertyValue := op.Value.Content[0]
81+
op.Value = createMappingNode(propertyName, propertyValue)
82+
}
83+
op.Path = OpPath(parentPath)
84+
85+
case opRemove:
86+
f = op.remove
87+
case opReplace:
88+
f = op.replace
89+
default:
90+
return fmt.Errorf("unexpected op: %s", op.Op)
5591
}
5692

5793
for _, match := range matches {
5894
parent := find(doc, containsChild(match))
5995

60-
switch op.Op {
61-
case opAdd:
62-
op.add(parent, match)
63-
case opRemove:
64-
op.remove(parent, match)
65-
case opReplace:
66-
op.replace(parent, match)
67-
default:
68-
return fmt.Errorf("unexpected op: %s", op.Op)
69-
}
96+
f(parent, match)
7097
}
7198

7299
return nil

0 commit comments

Comments
 (0)