You've already forked sap-jenkins-library
mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-09-16 09:26:22 +02:00
feat(helmExecute): update value files with dynamic values (#3861)
* Add getAndRenderImageInfo func * Add unit tests * Add comments * Improve value files handling * Rename getAndRenderImageInfo to parseAndRenderCPETemplate * Clean up * Update logic to parse and render templates * Update tests * Test: use t.TempDir for creating temporary dir * Use ParseTemplate method from piperenv pkg * Fix err message * Fix test
This commit is contained in:
committed by
GitHub
parent
274c11d28f
commit
1f242ea139
@@ -2,9 +2,11 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
|
|
||||||
"github.com/SAP/jenkins-library/pkg/kubernetes"
|
"github.com/SAP/jenkins-library/pkg/kubernetes"
|
||||||
"github.com/SAP/jenkins-library/pkg/log"
|
"github.com/SAP/jenkins-library/pkg/log"
|
||||||
|
"github.com/SAP/jenkins-library/pkg/piperenv"
|
||||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
"github.com/SAP/jenkins-library/pkg/telemetry"
|
||||||
"github.com/SAP/jenkins-library/pkg/versioning"
|
"github.com/SAP/jenkins-library/pkg/versioning"
|
||||||
)
|
)
|
||||||
@@ -54,6 +56,11 @@ func helmExecute(config helmExecuteOptions, telemetryData *telemetry.CustomData)
|
|||||||
helmConfig.PublishVersion = artifactInfo.Version
|
helmConfig.PublishVersion = artifactInfo.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = parseAndRenderCPETemplate(config, GeneralConfig.EnvRootPath, utils)
|
||||||
|
if err != nil {
|
||||||
|
log.Entry().WithError(err).Fatalf("failed to parse/render template: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
helmExecutor := kubernetes.NewHelmExecutor(helmConfig, utils, GeneralConfig.Verbose, log.Writer())
|
helmExecutor := kubernetes.NewHelmExecutor(helmConfig, utils, GeneralConfig.Verbose, log.Writer())
|
||||||
|
|
||||||
// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end
|
// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end
|
||||||
@@ -120,3 +127,45 @@ func runHelmExecuteDefault(config helmExecuteOptions, helmExecutor kubernetes.He
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseAndRenderCPETemplate allows to parse and render a template which contains references to the CPE
|
||||||
|
func parseAndRenderCPETemplate(config helmExecuteOptions, rootPath string, utils kubernetes.DeployUtils) error {
|
||||||
|
cpe := piperenv.CPEMap{}
|
||||||
|
err := cpe.LoadFromDisk(path.Join(rootPath, "commonPipelineEnvironment"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load values from commonPipelineEnvironment: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
valueFiles := []string{}
|
||||||
|
defaultValueFile := fmt.Sprintf("%s/%s", config.ChartPath, "values.yaml")
|
||||||
|
defaultValueFileExists, err := utils.FileExists(defaultValueFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if defaultValueFileExists {
|
||||||
|
valueFiles = append(valueFiles, defaultValueFile)
|
||||||
|
} else {
|
||||||
|
if len(config.HelmValues) == 0 {
|
||||||
|
return fmt.Errorf("no value file to proccess, please provide value file(s)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
valueFiles = append(valueFiles, config.HelmValues...)
|
||||||
|
|
||||||
|
for _, valueFile := range valueFiles {
|
||||||
|
cpeTemplate, err := utils.FileRead(valueFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read file: %v", err)
|
||||||
|
}
|
||||||
|
generated, err := cpe.ParseTemplate(string(cpeTemplate))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse template: %v", err)
|
||||||
|
}
|
||||||
|
err = utils.FileWrite(valueFile, generated.Bytes(), 0700)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update file: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@@ -2,13 +2,35 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/SAP/jenkins-library/pkg/kubernetes/mocks"
|
"github.com/SAP/jenkins-library/pkg/kubernetes/mocks"
|
||||||
|
"github.com/SAP/jenkins-library/pkg/mock"
|
||||||
|
"github.com/SAP/jenkins-library/pkg/piperenv"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type helmMockUtilsBundle struct {
|
||||||
|
*mock.ExecMockRunner
|
||||||
|
*mock.FilesMock
|
||||||
|
*mock.HttpClientMock
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHelmMockUtilsBundle() helmMockUtilsBundle {
|
||||||
|
utils := helmMockUtilsBundle{
|
||||||
|
ExecMockRunner: &mock.ExecMockRunner{},
|
||||||
|
FilesMock: &mock.FilesMock{},
|
||||||
|
HttpClientMock: &mock.HttpClientMock{
|
||||||
|
FileUploads: map[string]string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return utils
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunHelmUpgrade(t *testing.T) {
|
func TestRunHelmUpgrade(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -327,3 +349,126 @@ func TestRunHelmDefaultCommand(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseAndRenderCPETemplate(t *testing.T) {
|
||||||
|
commonPipelineEnvironment := "commonPipelineEnvironment"
|
||||||
|
valuesYaml := []byte(`
|
||||||
|
image: "image_1"
|
||||||
|
tag: {{ cpe "artifactVersion" }}
|
||||||
|
`)
|
||||||
|
values1Yaml := []byte(`
|
||||||
|
image: "image_2"
|
||||||
|
tag: {{ cpe "artVersion" }}
|
||||||
|
`)
|
||||||
|
values3Yaml := []byte(`
|
||||||
|
image: "image_3"
|
||||||
|
tag: {{ .CPE.artVersion
|
||||||
|
`)
|
||||||
|
values4Yaml := []byte(`
|
||||||
|
image: "test-image"
|
||||||
|
tag: {{ imageTag "test-image" }}
|
||||||
|
`)
|
||||||
|
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
require.DirExists(t, tmpDir)
|
||||||
|
err := os.Mkdir(path.Join(tmpDir, commonPipelineEnvironment), 0700)
|
||||||
|
require.NoError(t, err)
|
||||||
|
cpe := piperenv.CPEMap{
|
||||||
|
"artifactVersion": "1.0.0-123456789",
|
||||||
|
"container/imageNameTags": []string{"test-image:1.0.0-123456789"},
|
||||||
|
}
|
||||||
|
err = cpe.WriteToDisk(tmpDir)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defaultValueFile := "values.yaml"
|
||||||
|
config := helmExecuteOptions{
|
||||||
|
ChartPath: ".",
|
||||||
|
}
|
||||||
|
|
||||||
|
tt := []struct {
|
||||||
|
name string
|
||||||
|
defaultValueFile string
|
||||||
|
config helmExecuteOptions
|
||||||
|
expectedErr error
|
||||||
|
valueFile []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "'artifactVersion' file exists in CPE",
|
||||||
|
defaultValueFile: defaultValueFile,
|
||||||
|
config: config,
|
||||||
|
expectedErr: nil,
|
||||||
|
valueFile: valuesYaml,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "'artVersion' file does not exist in CPE",
|
||||||
|
defaultValueFile: defaultValueFile,
|
||||||
|
config: config,
|
||||||
|
expectedErr: nil,
|
||||||
|
valueFile: values1Yaml,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Good template ({{ imageTag 'test-image' }})",
|
||||||
|
defaultValueFile: defaultValueFile,
|
||||||
|
config: config,
|
||||||
|
expectedErr: nil,
|
||||||
|
valueFile: values4Yaml,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Wrong template ({{ .CPE.artVersion)",
|
||||||
|
defaultValueFile: defaultValueFile,
|
||||||
|
config: config,
|
||||||
|
expectedErr: fmt.Errorf("failed to parse template: failed to parse cpe template '\nimage: \"image_3\"\ntag: {{ .CPE.artVersion\n': template: cpetemplate:4: unclosed action started at cpetemplate:3"),
|
||||||
|
valueFile: values3Yaml,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiple value files",
|
||||||
|
defaultValueFile: defaultValueFile,
|
||||||
|
config: helmExecuteOptions{
|
||||||
|
ChartPath: ".",
|
||||||
|
HelmValues: []string{"./values_1.yaml", "./values_2.yaml"},
|
||||||
|
},
|
||||||
|
expectedErr: nil,
|
||||||
|
valueFile: valuesYaml,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "No value file is provided",
|
||||||
|
defaultValueFile: "",
|
||||||
|
config: helmExecuteOptions{
|
||||||
|
ChartPath: ".",
|
||||||
|
HelmValues: []string{},
|
||||||
|
},
|
||||||
|
expectedErr: fmt.Errorf("no value file to proccess, please provide value file(s)"),
|
||||||
|
valueFile: valuesYaml,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Wrong path to value file",
|
||||||
|
defaultValueFile: defaultValueFile,
|
||||||
|
config: helmExecuteOptions{
|
||||||
|
ChartPath: ".",
|
||||||
|
HelmValues: []string{"wrong/path/to/values_1.yaml"},
|
||||||
|
},
|
||||||
|
expectedErr: fmt.Errorf("failed to read file: could not read 'wrong/path/to/values_1.yaml'"),
|
||||||
|
valueFile: valuesYaml,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tt {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
utils := newHelmMockUtilsBundle()
|
||||||
|
utils.AddFile(fmt.Sprintf("%s/%s", config.ChartPath, test.defaultValueFile), test.valueFile)
|
||||||
|
|
||||||
|
if len(test.config.HelmValues) == 2 {
|
||||||
|
for _, value := range test.config.HelmValues {
|
||||||
|
utils.AddFile(value, test.valueFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := parseAndRenderCPETemplate(test.config, tmpDir, utils)
|
||||||
|
if test.expectedErr != nil {
|
||||||
|
assert.EqualError(t, err, test.expectedErr.Error())
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user