Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow per-version kustomize options (#5533) #5967

Merged
merged 1 commit into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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