Skip to content

Commit 01bf561

Browse files
authored
fix: keep the original template if template expansion fails (#9503)
* fix: keep the original template if template expansion fails * fix: keep the original template if template expansion fails
1 parent d3902b1 commit 01bf561

File tree

4 files changed

+91
-18
lines changed

4 files changed

+91
-18
lines changed

integration/diagnose_test.go

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ import (
2525
"text/template"
2626

2727
"github.com/GoogleContainerTools/skaffold/v2/integration/skaffold"
28+
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
29+
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/util"
30+
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/yaml"
2831
"github.com/GoogleContainerTools/skaffold/v2/testutil"
2932
)
3033

@@ -83,27 +86,81 @@ func TestDiagnose(t *testing.T) {
8386
configContents, err := os.ReadFile(filepath.Join(test.dir, "skaffold.yaml"))
8487
t.CheckNoError(err)
8588
templ, err := os.ReadFile(filepath.Join(test.dir, "diagnose.tmpl"))
89+
t.CheckNoError(err)
8690
tmpDir.Write("skaffold.yaml", string(configContents))
8791
args := []string{"--yaml-only", "--output", tmpDir.Path(test.outputFile), "-f", tmpDir.Path("skaffold.yaml")}
8892
args = append(args, test.args...)
8993
skaffold.Diagnose(args...).
9094
InDir(test.dir).RunOrFail(t.T)
91-
t.CheckNoError(err)
9295
outTemplate := template.Must(template.New("tmpl").Parse(string(templ)))
9396
cwd, err := filepath.Abs(test.dir)
9497
t.CheckNoError(err)
9598
expected := &bytes.Buffer{}
9699
outTemplate.Execute(expected, map[string]string{"Root": cwd})
97100

98101
outputPath := tmpDir.Path(test.outputFile)
99-
t.CheckNoError(err)
100102
out, err := os.ReadFile(outputPath)
101103
t.CheckNoError(err)
102104
t.CheckDeepEqual(expected.String(), string(out), testutil.YamlObj(t.T))
103105
})
104106
}
105107
}
106108

109+
// During the schema upgrade(v2beta28->v2beta29), Skaffold injects setTemplate fields into the configuration if a legacy Helm deployer is configured.
110+
// These injected fields contain templates, and we want to ensure that when expanding them with Go templates, the original field values remain unchanged
111+
// when environment variables are not set. This is important because users who use the skaffold diagnose command on the old schema
112+
// with Helm might not be aware of the existence of these templated fields, leading to templating failures.
113+
func TestDiagnoseTemplatingNotAllEnvsSet(t *testing.T) {
114+
tests := []struct {
115+
description string
116+
dir string
117+
outputFile string
118+
args []string
119+
envs map[string]string
120+
}{
121+
{
122+
description: "apply replacements to templates in skaffold.yaml",
123+
dir: "testdata/diagnose/not-all-envs-set",
124+
outputFile: "abc.txt",
125+
args: []string{"--enable-templating"},
126+
envs: map[string]string{"AAA": "aaa"},
127+
},
128+
}
129+
130+
for _, test := range tests {
131+
MarkIntegrationTest(t, CanRunWithoutGcp)
132+
testutil.Run(t, test.description, func(t *testutil.T) {
133+
if test.envs != nil {
134+
for k, v := range test.envs {
135+
t.Setenv(k, v)
136+
}
137+
}
138+
tmpDir := testutil.NewTempDir(t.T)
139+
configContents, err := os.ReadFile(filepath.Join(test.dir, "skaffold.yaml"))
140+
t.CheckNoError(err)
141+
tmpDir.Write("skaffold.yaml", string(configContents))
142+
outputPath := tmpDir.Path(test.outputFile)
143+
args := []string{"--yaml-only", "--output", outputPath, "-f", tmpDir.Path("skaffold.yaml")}
144+
args = append(args, test.args...)
145+
skaffold.Diagnose(args...).
146+
InDir(test.dir).RunOrFail(t.T)
147+
out, err := os.ReadFile(outputPath)
148+
t.CheckNoError(err)
149+
var conf latest.SkaffoldConfig
150+
yaml.Unmarshal(out, &conf)
151+
// templates unchanged
152+
t.CheckDeepEqual(conf.Deploy.LegacyHelmDeploy.Releases[0].SetValueTemplates, util.FlatMap{"image.repository": "{{.IMAGE_REPO_test_image}}",
153+
"image.tag": "{{.IMAGE_TAG_test_image}}@{{.IMAGE_DIGEST_test_image}}",
154+
})
155+
cwd, err := filepath.Abs(test.dir)
156+
t.CheckNoError(err)
157+
158+
// templates successfully expanded.
159+
t.CheckDeepEqual(conf.Deploy.LegacyHelmDeploy.Releases[0].ValuesFiles[0], cwd+"/aaa/test-values.yaml")
160+
})
161+
}
162+
}
163+
107164
func folders(root string) ([]string, error) {
108165
var folders []string
109166

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: skaffold/v2beta26
2+
kind: Config
3+
metadata:
4+
name: cio-cloud-project-mgmt-api
5+
build:
6+
artifacts:
7+
- image: test-image
8+
deploy:
9+
helm:
10+
releases:
11+
- name: test-release
12+
artifactOverrides:
13+
image: test-image
14+
valuesFiles:
15+
- '{{.AAA}}/test-values.yaml'
16+
remoteChart: "oci://test-chart"
17+
version: 1.21.2
18+
imageStrategy:
19+
helm: {}

pkg/skaffold/tags/templates.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package tags
1818

1919
import (
20-
"fmt"
2120
"reflect"
2221
"slices"
2322
"strings"
@@ -118,13 +117,13 @@ func expandTemplate(v reflect.Value) error {
118117
switch v.Kind() {
119118
case reflect.String:
120119
updated, err := util.ExpandEnvTemplate(v.String(), nil)
121-
if strings.Contains(updated, "<no value>") {
122-
return fmt.Errorf("environment variables missing for template keys")
123-
}
124120
if err != nil {
125121
return err
126122
}
127-
v.SetString(updated)
123+
// we want to keep the original template if expanding fails, otherwise update the value with expanded result.
124+
if !strings.Contains(updated, "<no value>") {
125+
v.SetString(updated)
126+
}
128127
case reflect.Ptr:
129128
return expandTemplate(v.Elem())
130129
case reflect.Slice, reflect.Array:
@@ -142,13 +141,13 @@ func expandTemplate(v reflect.Value) error {
142141
}
143142
} else if vv.Kind() == reflect.String {
144143
updated, err := util.ExpandEnvTemplate(vv.String(), nil)
145-
if strings.Contains(updated, "<no value>") {
146-
return fmt.Errorf("environment variables missing for template keys")
147-
}
148144
if err != nil {
149145
return err
150146
}
151-
v.SetMapIndex(key, reflect.ValueOf(updated))
147+
// we want to keep the original template if expanding fails, otherwise update the value with expanded result.
148+
if !strings.Contains(updated, "<no value>") {
149+
v.SetMapIndex(key, reflect.ValueOf(updated))
150+
}
152151
}
153152
}
154153
}

pkg/skaffold/tags/templates_test.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,10 @@ func TestApplyTemplates(t *testing.T) {
7575
envs: map[string]string{"SECOND": "second", "THIRD": "third"},
7676
},
7777
{
78-
name: "Map of strings, no value",
79-
input: testStruct{MapString: map[string]string{"first": "first", "second": "{{.SECOND}}", "third": "{{.THIRD}}"}},
80-
want: testStruct{MapString: map[string]string{"first": "first", "second": "{{.SECOND}}", "third": "{{.THIRD}}"}},
81-
wantErr: true,
82-
envs: map[string]string{},
78+
name: "Map of strings, keep the original template",
79+
input: testStruct{MapString: map[string]string{"first": "first", "second": "{{.SECOND}}", "third": "{{.THIRD}}"}},
80+
want: testStruct{MapString: map[string]string{"first": "first", "second": "{{.SECOND}}", "third": "{{.THIRD}}"}},
81+
envs: map[string]string{},
8382
},
8483
{
8584
name: "Map of pointers to strings",
@@ -108,14 +107,13 @@ func TestApplyTemplates(t *testing.T) {
108107
wantErr: false,
109108
},
110109
{
111-
name: "string not found - <no value>",
110+
name: "string not found - keep the original template",
112111
input: testStruct{
113112
SimpleString: "{{ .ENV_VAR }}",
114113
},
115114
want: testStruct{
116115
SimpleString: "{{ .ENV_VAR }}",
117116
},
118-
wantErr: true,
119117
},
120118
{
121119
name: "invalid template",

0 commit comments

Comments
 (0)