Skip to content

Commit

Permalink
feat: allow per-version kustomize options (argoproj#5967)
Browse files Browse the repository at this point in the history
Allow adding build options that are specific to a kustomize version instead of using the same default options for each version.

Signed-off-by: Chetan Banavikalmutt <[email protected]>
  • Loading branch information
chetan-rns authored Apr 6, 2021
1 parent 92c7ca0 commit f06fc0d
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 20 deletions.
6 changes: 5 additions & 1 deletion docs/operator-manual/argocd-cm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,11 @@ data:
# Build options/parameters to use with `kustomize build` (optional)
kustomize.buildOptions: --load_restrictor none

# Additional Kustomize versions and corresponding binary paths
# Per-version build options and binary paths
kustomize.path.v3.9.1: /custom-tools/kustomize_3_9
kustomize.buildOptions.v3.9.1: --enable_kyaml true

# Additional Kustomize versions and corresponding binary paths (deprecated)
kustomize.version.v3.5.1: /custom-tools/kustomize_3_5_1
kustomize.version.v3.5.4: /custom-tools/kustomize_3_5_4

Expand Down
9 changes: 5 additions & 4 deletions docs/user-guide/kustomize.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Read more about [private repos](private-repositories.md).

## `kustomize build` Options/Parameters

To provide build options to `kustomize build` add a property to the ArgoCD CM under data:
To provide build options to `kustomize build` of default kustomize version, use `kustomize.buildOptions` field of `argocd-cm` ConfigMap. Use `kustomize.buildOptions.<version>` to register version specific build options.

```yaml
apiVersion: v1
Expand All @@ -36,12 +36,13 @@ metadata:
app.kubernetes.io/part-of: argocd
data:
kustomize.buildOptions: --load_restrictor none
kustomize.buildOptions.v3.9.1: --output /tmp
```
## Custom Kustomize versions
Argo CD supports using multiple kustomize versions simultaneously and specifies required version per application.
To add additional versions make sure required versions are [bundled](../operator-manual/custom_tools.md) and then
use `kustomize.version.<version>` fields of `argocd-cm` ConfigMap to register bundled additional versions.
use `kustomize.path.<version>` fields of `argocd-cm` ConfigMap to register bundled additional versions.

```yaml
apiVersion: v1
Expand All @@ -53,8 +54,8 @@ metadata:
app.kubernetes.io/name: argocd-cm
app.kubernetes.io/part-of: argocd
data:
kustomize.version.v3.5.1: /custom-tools/kustomize_3_5_1
kustomize.version.v3.5.4: /custom-tools/kustomize_3_5_4
kustomize.path.v3.5.1: /custom-tools/kustomize_3_5_1
kustomize.path.v3.5.4: /custom-tools/kustomize_3_5_4
```

Once a new version is configured you can reference it in Application spec as following:
Expand Down
68 changes: 58 additions & 10 deletions util/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,10 @@ type HelmRepoCredentials struct {
type KustomizeVersion struct {
// Name holds Kustomize version name
Name string
// Name holds corresponding binary path
// Path holds corresponding binary path
Path string
// BuildOptions that are specific to Kustomize version
BuildOptions string
}

// KustomizeSettings holds kustomize settings
Expand All @@ -134,19 +136,25 @@ type KustomizeSettings struct {

func (ks *KustomizeSettings) GetOptions(source v1alpha1.ApplicationSource) (*v1alpha1.KustomizeOptions, error) {
binaryPath := ""
buildOptions := ""
if source.Kustomize != nil && source.Kustomize.Version != "" {
for _, ver := range ks.Versions {
if ver.Name == source.Kustomize.Version {
// add version specific path and build options
binaryPath = ver.Path
buildOptions = ver.BuildOptions
break
}
}
if binaryPath == "" {
return nil, fmt.Errorf("kustomize version %s is not registered", source.Kustomize.Version)
}
} else {
// add build options for the default version
buildOptions = ks.BuildOptions
}
return &v1alpha1.KustomizeOptions{
BuildOptions: ks.BuildOptions,
BuildOptions: buildOptions,
BinaryPath: binaryPath,
}, nil
}
Expand Down Expand Up @@ -264,6 +272,8 @@ const (
kustomizeBuildOptionsKey = "kustomize.buildOptions"
// kustomizeVersionKeyPrefix is a kustomize version key prefix
kustomizeVersionKeyPrefix = "kustomize.version"
// kustomizePathPrefixKey is a kustomize path for a specific version
kustomizePathPrefixKey = "kustomize.path"
// anonymousUserEnabledKey is the key which enables or disables anonymous user
anonymousUserEnabledKey = "users.anonymous.enabled"
// anonymousUserEnabledKey is the key which specifies token expiration duration
Expand Down Expand Up @@ -586,22 +596,60 @@ func (mgr *SettingsManager) GetKustomizeSettings() (*KustomizeSettings, error) {
if err != nil {
return nil, err
}
kustomizeVersionsMap := map[string]KustomizeVersion{}
buildOptions := map[string]string{}
settings := &KustomizeSettings{}
if value, ok := argoCDCM.Data[kustomizeBuildOptionsKey]; ok {
settings.BuildOptions = value

// extract build options for the default version
if options, ok := argoCDCM.Data[kustomizeBuildOptionsKey]; ok {
settings.BuildOptions = options
}

// extract per-version binary paths and build options
for k, v := range argoCDCM.Data {
if !strings.HasPrefix(k, kustomizeVersionKeyPrefix) {
continue
// extract version and path from kustomize.version.<version>
if strings.HasPrefix(k, kustomizeVersionKeyPrefix) {
err = addKustomizeVersion(kustomizeVersionKeyPrefix, k, v, kustomizeVersionsMap)
if err != nil {
return nil, err
}
}

// extract version and path from kustomize.path.<version>
if strings.HasPrefix(k, kustomizePathPrefixKey) {
err = addKustomizeVersion(kustomizePathPrefixKey, k, v, kustomizeVersionsMap)
if err != nil {
return nil, err
}
}

// extract version and build options from kustomize.buildOptions.<version>
if strings.HasPrefix(k, kustomizeBuildOptionsKey) && k != kustomizeBuildOptionsKey {
buildOptions[k[len(kustomizeBuildOptionsKey)+1:]] = v
}
}

for _, v := range kustomizeVersionsMap {
if _, ok := buildOptions[v.Name]; ok {
v.BuildOptions = buildOptions[v.Name]
}
settings.Versions = append(settings.Versions, KustomizeVersion{
Name: k[len(kustomizeVersionKeyPrefix)+1:],
Path: v,
})
settings.Versions = append(settings.Versions, v)
}
return settings, nil
}

func addKustomizeVersion(prefix, name, path string, kvMap map[string]KustomizeVersion) error {
version := name[len(prefix)+1:]
if _, ok := kvMap[version]; ok {
return fmt.Errorf("found duplicate kustomize version: %s", version)
}
kvMap[version] = KustomizeVersion{
Name: version,
Path: path,
}
return nil
}

// DEPRECATED. Helm repository credentials are now managed using RepoCredentials
func (mgr *SettingsManager) GetHelmRepositories() ([]HelmRepoCredentials, error) {
argoCDCM, err := mgr.getConfigMap()
Expand Down
91 changes: 86 additions & 5 deletions util/settings/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package settings

import (
"context"
"sort"
"testing"

"github.com/argoproj/argo-cd/v2/common"
Expand Down Expand Up @@ -296,28 +297,108 @@ func TestSettingsManager_GetKustomizeBuildOptions(t *testing.T) {
assert.Equal(t, "foo", options.BuildOptions)
assert.Equal(t, []KustomizeVersion{{Name: "v3.2.1", Path: "somePath"}}, options.Versions)
})

t.Run("Kustomize settings per-version", func(t *testing.T) {
_, settingsManager := fixtures(map[string]string{
"kustomize.buildOptions": "--global true",
"kustomize.version.v3.2.1": "/path_3.2.1",
"kustomize.buildOptions.v3.2.3": "--options v3.2.3",
"kustomize.path.v3.2.3": "/path_3.2.3",
"kustomize.path.v3.2.4": "/path_3.2.4",
"kustomize.buildOptions.v3.2.4": "--options v3.2.4",
"kustomize.buildOptions.v3.2.5": "--options v3.2.5",
})

got, err := settingsManager.GetKustomizeSettings()

assert.NoError(t, err)
assert.Equal(t, "--global true", got.BuildOptions)
want := &KustomizeSettings{
BuildOptions: "--global true",
Versions: []KustomizeVersion{
{Name: "v3.2.1", Path: "/path_3.2.1"},
{Name: "v3.2.3", Path: "/path_3.2.3", BuildOptions: "--options v3.2.3"},
{Name: "v3.2.4", Path: "/path_3.2.4", BuildOptions: "--options v3.2.4"},
},
}
sortVersionsByName := func(versions []KustomizeVersion) {
sort.Slice(versions, func(i, j int) bool {
return versions[i].Name > versions[j].Name
})
}
sortVersionsByName(want.Versions)
sortVersionsByName(got.Versions)
assert.EqualValues(t, want, got)
})

t.Run("Kustomize settings per-version with duplicate versions", func(t *testing.T) {
_, settingsManager := fixtures(map[string]string{
"kustomize.buildOptions": "--global true",
"kustomize.version.v3.2.1": "/path_3.2.1",
"kustomize.buildOptions.v3.2.1": "--options v3.2.3",
"kustomize.path.v3.2.2": "/other_path_3.2.2",
"kustomize.path.v3.2.1": "/other_path_3.2.1",
})

got, err := settingsManager.GetKustomizeSettings()
assert.EqualError(t, err, "found duplicate kustomize version: v3.2.1")
assert.Empty(t, got)
})

t.Run("Config map with no Kustomize settings", func(t *testing.T) {
_, settingsManager := fixtures(map[string]string{
"other.options": "--global true",
})

got, err := settingsManager.GetKustomizeSettings()
assert.NoError(t, err)
assert.Empty(t, got)
})
}

func TestKustomizeSettings_GetOptions(t *testing.T) {
settings := KustomizeSettings{Versions: []KustomizeVersion{
{Name: "v1", Path: "path_v1"},
{Name: "v2", Path: "path_v2"},
{Name: "v3", Path: "path_v3"},
}}
settings := KustomizeSettings{
BuildOptions: "--opt1 val1",
Versions: []KustomizeVersion{
{Name: "v1", Path: "path_v1"},
{Name: "v2", Path: "path_v2"},
{Name: "v3", Path: "path_v3", BuildOptions: "--opt2 val2"},
},
}

t.Run("VersionDoesNotExist", func(t *testing.T) {
_, err := settings.GetOptions(v1alpha1.ApplicationSource{
Kustomize: &v1alpha1.ApplicationSourceKustomize{Version: "v4"}})
assert.Error(t, err)
})

t.Run("DefaultBuildOptions", func(t *testing.T) {
ver, err := settings.GetOptions(v1alpha1.ApplicationSource{})
if !assert.NoError(t, err) {
return
}
assert.Equal(t, "", ver.BinaryPath)
assert.Equal(t, "--opt1 val1", ver.BuildOptions)
})

t.Run("VersionExists", func(t *testing.T) {
ver, err := settings.GetOptions(v1alpha1.ApplicationSource{
Kustomize: &v1alpha1.ApplicationSourceKustomize{Version: "v2"}})
if !assert.NoError(t, err) {
return
}
assert.Equal(t, "path_v2", ver.BinaryPath)
assert.Equal(t, "", ver.BuildOptions)
})

t.Run("VersionExistsWithBuildOption", func(t *testing.T) {
ver, err := settings.GetOptions(v1alpha1.ApplicationSource{
Kustomize: &v1alpha1.ApplicationSourceKustomize{Version: "v3"}})
if !assert.NoError(t, err) {
return
}
assert.Equal(t, "path_v3", ver.BinaryPath)
assert.Equal(t, "--opt2 val2", ver.BuildOptions)
})
}

Expand Down

0 comments on commit f06fc0d

Please sign in to comment.