Skip to content

Commit c38dabf

Browse files
authored
Merge pull request #1803 from lizardruss/fix-profile-activation
fix: profiles should not activate on substring matches
2 parents da6af1c + 240ffb3 commit c38dabf

File tree

7 files changed

+221
-5
lines changed

7 files changed

+221
-5
lines changed

docs/pages/configuration/profiles/activation.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ profiles:
3131
path: images.backend.image
3232
value: john/prodbackend
3333
```
34-
The profile `production` would be activated when the environment variable `ENV` matches the regular expression `prod-\d+`. This can be useful for matching environment variables that have dynamic values.
34+
The profile `production` would be activated when the environment variable `ENV` matches the regular expression `prod-\d+`. This can be useful for matching environment variables that have dynamic values. Regular expressions will have start of string (`^`) and end of string (`$`) anchors added automatically to avoid unexpected substring matching.
3535

3636
#### Example: Matching All Environment Variables
3737
When multiple `env` name/expression pairs are specified in the same activation, all environment variables values must match the expression to activate the profile. For example, the `production` profile is activated when both environment variables match their expressions:

e2e/tests/config/config.go

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,110 @@ var _ = DevSpaceDescribe("config", func() {
288288
framework.ExpectEqual(latestConfig.Deployments[1].Name, "test2")
289289
})
290290

291+
ginkgo.It("should auto activate profile using exact string matching environment variable", func() {
292+
tempDir, err := framework.CopyToTempDir("tests/config/testdata/profile-activation")
293+
framework.ExpectNoError(err)
294+
defer framework.CleanupTempDir(initialDir, tempDir)
295+
296+
// run with non-matching vars
297+
configBuffer := &bytes.Buffer{}
298+
printCmd := &cmd.PrintCmd{
299+
GlobalFlags: &flags.GlobalFlags{
300+
ConfigPath: "string-exact.yaml",
301+
},
302+
Out: configBuffer,
303+
SkipInfo: true,
304+
}
305+
306+
os.Setenv("FOO", "test123")
307+
err = printCmd.Run(f)
308+
framework.ExpectNoError(err)
309+
os.Unsetenv("FOO")
310+
311+
latestConfig := &latest.Config{}
312+
err = yaml.Unmarshal(configBuffer.Bytes(), latestConfig)
313+
framework.ExpectNoError(err)
314+
315+
// validate no profile was activated
316+
framework.ExpectEqual(len(latestConfig.Deployments), 0)
317+
318+
// run with environment variable set.
319+
configBuffer = &bytes.Buffer{}
320+
printCmd = &cmd.PrintCmd{
321+
GlobalFlags: &flags.GlobalFlags{
322+
ConfigPath: "string-exact.yaml",
323+
},
324+
Out: configBuffer,
325+
SkipInfo: true,
326+
}
327+
328+
os.Setenv("FOO", "test")
329+
err = printCmd.Run(f)
330+
framework.ExpectNoError(err)
331+
os.Unsetenv("FOO")
332+
333+
latestConfig = &latest.Config{}
334+
err = yaml.Unmarshal(configBuffer.Bytes(), latestConfig)
335+
framework.ExpectNoError(err)
336+
337+
// validate profile was activated
338+
framework.ExpectEqual(len(latestConfig.Deployments), 2)
339+
framework.ExpectEqual(latestConfig.Deployments[0].Name, "test")
340+
framework.ExpectEqual(latestConfig.Deployments[1].Name, "test2")
341+
})
342+
343+
ginkgo.It("should auto activate profile using exact regular expression matching environment variable", func() {
344+
tempDir, err := framework.CopyToTempDir("tests/config/testdata/profile-activation")
345+
framework.ExpectNoError(err)
346+
defer framework.CleanupTempDir(initialDir, tempDir)
347+
348+
// run with non-matching vars
349+
configBuffer := &bytes.Buffer{}
350+
printCmd := &cmd.PrintCmd{
351+
GlobalFlags: &flags.GlobalFlags{
352+
ConfigPath: "regexp-exact.yaml",
353+
},
354+
Out: configBuffer,
355+
SkipInfo: true,
356+
}
357+
358+
os.Setenv("FOO", "some test here")
359+
err = printCmd.Run(f)
360+
framework.ExpectNoError(err)
361+
os.Unsetenv("FOO")
362+
363+
latestConfig := &latest.Config{}
364+
err = yaml.Unmarshal(configBuffer.Bytes(), latestConfig)
365+
framework.ExpectNoError(err)
366+
367+
// validate no profile was activated
368+
framework.ExpectEqual(len(latestConfig.Deployments), 0)
369+
370+
// run with environment variable set.
371+
configBuffer = &bytes.Buffer{}
372+
printCmd = &cmd.PrintCmd{
373+
GlobalFlags: &flags.GlobalFlags{
374+
ConfigPath: "regexp-exact.yaml",
375+
},
376+
Out: configBuffer,
377+
SkipInfo: true,
378+
}
379+
380+
os.Setenv("FOO", "test")
381+
err = printCmd.Run(f)
382+
framework.ExpectNoError(err)
383+
os.Unsetenv("FOO")
384+
385+
latestConfig = &latest.Config{}
386+
err = yaml.Unmarshal(configBuffer.Bytes(), latestConfig)
387+
framework.ExpectNoError(err)
388+
389+
// validate profile was activated
390+
framework.ExpectEqual(len(latestConfig.Deployments), 2)
391+
framework.ExpectEqual(latestConfig.Deployments[0].Name, "test")
392+
framework.ExpectEqual(latestConfig.Deployments[1].Name, "test2")
393+
})
394+
291395
ginkgo.It("should auto activate profile using regular expression matching environment variable", func() {
292396
tempDir, err := framework.CopyToTempDir("tests/config/testdata/profile-activation")
293397
framework.ExpectNoError(err)
@@ -325,7 +429,59 @@ var _ = DevSpaceDescribe("config", func() {
325429
SkipInfo: true,
326430
}
327431

328-
os.Setenv("FOO", "truthy")
432+
os.Setenv("FOO", "^the string begins with ^t and ends with $")
433+
err = printCmd.Run(f)
434+
framework.ExpectNoError(err)
435+
os.Unsetenv("FOO")
436+
437+
latestConfig = &latest.Config{}
438+
err = yaml.Unmarshal(configBuffer.Bytes(), latestConfig)
439+
framework.ExpectNoError(err)
440+
441+
// validate profile was activated
442+
framework.ExpectEqual(len(latestConfig.Deployments), 2)
443+
framework.ExpectEqual(latestConfig.Deployments[0].Name, "test")
444+
framework.ExpectEqual(latestConfig.Deployments[1].Name, "test2")
445+
})
446+
447+
ginkgo.It("should auto activate profile using regular expression matching environment variable substring", func() {
448+
tempDir, err := framework.CopyToTempDir("tests/config/testdata/profile-activation")
449+
framework.ExpectNoError(err)
450+
defer framework.CleanupTempDir(initialDir, tempDir)
451+
452+
// run with non-matching vars
453+
configBuffer := &bytes.Buffer{}
454+
printCmd := &cmd.PrintCmd{
455+
GlobalFlags: &flags.GlobalFlags{
456+
ConfigPath: "regexp-substring.yaml",
457+
},
458+
Out: configBuffer,
459+
SkipInfo: true,
460+
}
461+
462+
os.Setenv("FOO", "the best string")
463+
err = printCmd.Run(f)
464+
framework.ExpectNoError(err)
465+
os.Unsetenv("FOO")
466+
467+
latestConfig := &latest.Config{}
468+
err = yaml.Unmarshal(configBuffer.Bytes(), latestConfig)
469+
framework.ExpectNoError(err)
470+
471+
// validate no profile was activated
472+
framework.ExpectEqual(len(latestConfig.Deployments), 0)
473+
474+
// run with environment variable set.
475+
configBuffer = &bytes.Buffer{}
476+
printCmd = &cmd.PrintCmd{
477+
GlobalFlags: &flags.GlobalFlags{
478+
ConfigPath: "regexp-substring.yaml",
479+
},
480+
Out: configBuffer,
481+
SkipInfo: true,
482+
}
483+
484+
os.Setenv("FOO", "a test string")
329485
err = printCmd.Run(f)
330486
framework.ExpectNoError(err)
331487
os.Unsetenv("FOO")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
version: v1beta11
2+
profiles:
3+
- name: one
4+
activation:
5+
- env:
6+
FOO: "^t.*$"
7+
patches:
8+
- op: replace
9+
path: deployments
10+
value:
11+
- name: test
12+
kubectl:
13+
manifests:
14+
- test.yaml
15+
- name: test2
16+
kubectl:
17+
manifests:
18+
- test2.yaml
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
version: v1beta11
2+
profiles:
3+
- name: one
4+
activation:
5+
- env:
6+
FOO: ".*test.*"
7+
patches:
8+
- op: replace
9+
path: deployments
10+
value:
11+
- name: test
12+
kubectl:
13+
manifests:
14+
- test.yaml
15+
- name: test2
16+
kubectl:
17+
manifests:
18+
- test2.yaml

e2e/tests/config/testdata/profile-activation/regexp.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ profiles:
33
- name: one
44
activation:
55
- env:
6-
FOO: "t.*"
6+
FOO: "\\^t.*\\$"
77
patches:
88
- op: replace
99
path: deployments
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
version: v1beta11
2+
profiles:
3+
- name: one
4+
activation:
5+
- env:
6+
FOO: "test"
7+
patches:
8+
- op: replace
9+
path: deployments
10+
value:
11+
- name: test
12+
kubectl:
13+
manifests:
14+
- test.yaml
15+
- name: test2
16+
kubectl:
17+
manifests:
18+
- test2.yaml

pkg/devspace/config/versions/versions.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package versions
22

33
import (
44
"fmt"
5-
"github.com/loft-sh/devspace/pkg/devspace/config/versions/v1beta10"
65
"io/ioutil"
76
"os"
87
"path/filepath"
98
"regexp"
9+
"strings"
10+
11+
"github.com/loft-sh/devspace/pkg/devspace/config/versions/v1beta10"
1012

1113
"github.com/loft-sh/devspace/pkg/devspace/config/constants"
1214
"github.com/loft-sh/devspace/pkg/devspace/config/versions/config"
@@ -435,7 +437,11 @@ func getActivatedProfiles(data map[interface{}]interface{}) ([]string, error) {
435437

436438
func matchEnvironment(env map[string]string) (bool, error) {
437439
for k, v := range env {
438-
match, err := regexp.MatchString(v, os.Getenv(k))
440+
expression := strings.TrimPrefix(v, "^")
441+
expression = strings.TrimSuffix(expression, "$")
442+
expression = fmt.Sprintf("^%s$", expression)
443+
444+
match, err := regexp.MatchString(expression, os.Getenv(k))
439445
if err != nil {
440446
return false, err
441447
}

0 commit comments

Comments
 (0)