Skip to content

Commit

Permalink
feat(helm): support --kube-version, --api-versions, and `--namesp…
Browse files Browse the repository at this point in the history
…ace` (argoproj#19325) (argoproj#19328)

* feat(helm): support `--kube-version`, `--api-versions`, and `--namespace` (argoproj#19325)

Signed-off-by: Michael Crenshaw <[email protected]>

* codegen

Signed-off-by: Michael Crenshaw <[email protected]>

* handle kube and api versions per-source

Signed-off-by: Michael Crenshaw <[email protected]>

* lint

Signed-off-by: Michael Crenshaw <[email protected]>

* fix nil ref error

Signed-off-by: Michael Crenshaw <[email protected]>

* fix nil ref error due to not handling multi-source in verify path

Signed-off-by: Michael Crenshaw <[email protected]>

* change casing to be more consistent with helm

Signed-off-by: Michael Crenshaw <[email protected]>

* Kustomize too

Signed-off-by: Michael Crenshaw <[email protected]>

* simplify verify path

Signed-off-by: Michael Crenshaw <[email protected]>

* add e2e tests for kustomize

Signed-off-by: Michael Crenshaw <[email protected]>

* reorder for consistency, and add more tests

Signed-off-by: Michael Crenshaw <[email protected]>

* Update docs/operator-manual/application.yaml

Signed-off-by: Alexandre Gaudreault <[email protected]>

---------

Signed-off-by: Michael Crenshaw <[email protected]>
Signed-off-by: Alexandre Gaudreault <[email protected]>
Co-authored-by: Alexandre Gaudreault <[email protected]>
  • Loading branch information
crenshaw-dev and agaudreault committed Aug 6, 2024
1 parent a96e665 commit 21ed19b
Show file tree
Hide file tree
Showing 38 changed files with 5,629 additions and 691 deletions.
26 changes: 26 additions & 0 deletions assets/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions cmd/util/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ type AppOptions struct {
helmVersion string
helmPassCredentials bool
helmSkipCrds bool
helmNamespace string
helmKubeVersion string
helmApiVersions []string
project string
syncPolicy string
syncOptions []string
Expand All @@ -72,6 +75,8 @@ type AppOptions struct {
kustomizeForceCommonLabels bool
kustomizeForceCommonAnnotations bool
kustomizeNamespace string
kustomizeKubeVersion string
kustomizeApiVersions []string
pluginEnvs []string
Validate bool
directoryExclude string
Expand Down Expand Up @@ -104,6 +109,9 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) {
command.Flags().StringArrayVar(&opts.helmSetStrings, "helm-set-string", []string{}, "Helm set STRING values on the command line (can be repeated to set several values: --helm-set-string key1=val1 --helm-set-string key2=val2)")
command.Flags().StringArrayVar(&opts.helmSetFiles, "helm-set-file", []string{}, "Helm set values from respective files specified via the command line (can be repeated to set several values: --helm-set-file key1=path1 --helm-set-file key2=path2)")
command.Flags().BoolVar(&opts.helmSkipCrds, "helm-skip-crds", false, "Skip helm crd installation step")
command.Flags().StringVar(&opts.helmNamespace, "helm-namespace", "", "Helm namespace to use when running helm template. If not set, use app.spec.destination.namespace")
command.Flags().StringVar(&opts.helmKubeVersion, "helm-kube-version", "", "Helm kube-version to use when running helm template. If not set, use the kube version from the destination cluster")
command.Flags().StringArrayVar(&opts.helmApiVersions, "helm-api-versions", []string{}, "Helm api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster")
command.Flags().StringVar(&opts.project, "project", "", "Application project name")
command.Flags().StringVar(&opts.syncPolicy, "sync-policy", "", "Set the sync policy (one of: manual (aliases of manual: none), automated (aliases of automated: auto, automatic))")
command.Flags().StringArrayVar(&opts.syncOptions, "sync-option", []string{}, "Add or remove a sync option, e.g add `Prune=false`. Remove using `!` prefix, e.g. `!Prune=false`")
Expand All @@ -130,6 +138,8 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) {
command.Flags().BoolVar(&opts.kustomizeForceCommonLabels, "kustomize-force-common-label", false, "Force common labels in Kustomize")
command.Flags().BoolVar(&opts.kustomizeForceCommonAnnotations, "kustomize-force-common-annotation", false, "Force common annotations in Kustomize")
command.Flags().StringVar(&opts.kustomizeNamespace, "kustomize-namespace", "", "Kustomize namespace")
command.Flags().StringVar(&opts.kustomizeKubeVersion, "kustomize-kube-version", "", "kube-version to use when running helm template. If not set, use the kube version from the destination cluster. Only applicable when Helm is enabled for Kustomize builds")
command.Flags().StringArrayVar(&opts.kustomizeApiVersions, "kustomize-api-versions", nil, "api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster. Only applicable when Helm is enabled for Kustomize builds")
command.Flags().StringVar(&opts.directoryExclude, "directory-exclude", "", "Set glob expression used to exclude files from application source path")
command.Flags().StringVar(&opts.directoryInclude, "directory-include", "", "Set glob expression used to include files from application source path")
command.Flags().Int64Var(&opts.retryLimit, "sync-retry-limit", 0, "Max number of allowed sync retries")
Expand Down Expand Up @@ -266,6 +276,8 @@ type kustomizeOpts struct {
forceCommonLabels bool
forceCommonAnnotations bool
namespace string
kubeVersion string
apiVersions []string
}

func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
Expand All @@ -284,6 +296,12 @@ func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
if opts.namespace != "" {
src.Kustomize.Namespace = opts.namespace
}
if opts.kubeVersion != "" {
src.Kustomize.KubeVersion = opts.kubeVersion
}
if len(opts.apiVersions) > 0 {
src.Kustomize.APIVersions = opts.apiVersions
}
if opts.commonLabels != nil {
src.Kustomize.CommonLabels = opts.commonLabels
}
Expand Down Expand Up @@ -340,6 +358,9 @@ type helmOpts struct {
helmSetFiles []string
passCredentials bool
skipCrds bool
namespace string
kubeVersion string
apiVersions []string
}

func setHelmOpt(src *argoappv1.ApplicationSource, opts helmOpts) {
Expand Down Expand Up @@ -370,6 +391,15 @@ func setHelmOpt(src *argoappv1.ApplicationSource, opts helmOpts) {
if opts.skipCrds {
src.Helm.SkipCrds = opts.skipCrds
}
if opts.namespace != "" {
src.Helm.Namespace = opts.namespace
}
if opts.kubeVersion != "" {
src.Helm.KubeVersion = opts.kubeVersion
}
if len(opts.apiVersions) > 0 {
src.Helm.APIVersions = opts.apiVersions
}
for _, text := range opts.helmSets {
p, err := argoappv1.NewHelmParameter(text, false)
if err != nil {
Expand Down Expand Up @@ -628,6 +658,12 @@ func ConstructSource(source *argoappv1.ApplicationSource, appOpts AppOptions, fl
setHelmOpt(source, helmOpts{helmSetFiles: appOpts.helmSetFiles})
case "helm-skip-crds":
setHelmOpt(source, helmOpts{skipCrds: appOpts.helmSkipCrds})
case "helm-namespace":
setHelmOpt(source, helmOpts{namespace: appOpts.helmNamespace})
case "helm-kube-version":
setHelmOpt(source, helmOpts{kubeVersion: appOpts.helmKubeVersion})
case "helm-api-versions":
setHelmOpt(source, helmOpts{apiVersions: appOpts.helmApiVersions})
case "directory-recurse":
if source.Directory != nil {
source.Directory.Recurse = appOpts.directoryRecurse
Expand Down Expand Up @@ -660,6 +696,10 @@ func ConstructSource(source *argoappv1.ApplicationSource, appOpts AppOptions, fl
setKustomizeOpt(source, kustomizeOpts{version: appOpts.kustomizeVersion})
case "kustomize-namespace":
setKustomizeOpt(source, kustomizeOpts{namespace: appOpts.kustomizeNamespace})
case "kustomize-kube-version":
setKustomizeOpt(source, kustomizeOpts{kubeVersion: appOpts.kustomizeKubeVersion})
case "kustomize-api-versions":
setKustomizeOpt(source, kustomizeOpts{apiVersions: appOpts.kustomizeApiVersions})
case "kustomize-common-label":
parsedLabels, err := label.Parse(appOpts.kustomizeCommonLabels)
errors.CheckError(err)
Expand Down
51 changes: 51 additions & 0 deletions cmd/util/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ func Test_setHelmOpt(t *testing.T) {
setHelmOpt(&src, helmOpts{skipCrds: true})
assert.True(t, src.Helm.SkipCrds)
})
t.Run("HelmNamespace", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setHelmOpt(&src, helmOpts{namespace: "custom-namespace"})
assert.Equal(t, "custom-namespace", src.Helm.Namespace)
})
t.Run("HelmKubeVersion", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setHelmOpt(&src, helmOpts{kubeVersion: "v1.16.0"})
assert.Equal(t, "v1.16.0", src.Helm.KubeVersion)
})
t.Run("HelmApiVersions", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setHelmOpt(&src, helmOpts{apiVersions: []string{"v1", "v2"}})
assert.Equal(t, []string{"v1", "v2"}, src.Helm.APIVersions)
})
}

func Test_setKustomizeOpt(t *testing.T) {
Expand Down Expand Up @@ -114,6 +129,16 @@ func Test_setKustomizeOpt(t *testing.T) {
setKustomizeOpt(&src, kustomizeOpts{namespace: "custom-namespace"})
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{Namespace: "custom-namespace"}, src.Kustomize)
})
t.Run("KubeVersion", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setKustomizeOpt(&src, kustomizeOpts{kubeVersion: "999.999.999"})
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{KubeVersion: "999.999.999"}, src.Kustomize)
})
t.Run("ApiVersions", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setKustomizeOpt(&src, kustomizeOpts{apiVersions: []string{"v1", "v2"}})
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{APIVersions: []string{"v1", "v2"}}, src.Kustomize)
})
t.Run("Common labels", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setKustomizeOpt(&src, kustomizeOpts{commonLabels: map[string]string{"foo1": "bar1", "foo2": "bar2"}})
Expand Down Expand Up @@ -233,6 +258,32 @@ func Test_setAppSpecOptions(t *testing.T) {
require.NoError(t, f.SetFlag("kustomize-replica", "my-statefulset=4"))
assert.Equal(t, v1alpha1.KustomizeReplicas{{Name: "my-deployment", Count: intstr.FromInt(2)}, {Name: "my-statefulset", Count: intstr.FromInt(4)}}, f.spec.Source.Kustomize.Replicas)
})
t.Run("Kustomize Namespace", func(t *testing.T) {
require.NoError(t, f.SetFlag("kustomize-namespace", "override-namespace"))
assert.Equal(t, "override-namespace", f.spec.Source.Kustomize.Namespace)
})
t.Run("Kustomize Kube Version", func(t *testing.T) {
require.NoError(t, f.SetFlag("kustomize-kube-version", "999.999.999"))
assert.Equal(t, "999.999.999", f.spec.Source.Kustomize.KubeVersion)
})
t.Run("Kustomize API Versions", func(t *testing.T) {
require.NoError(t, f.SetFlag("kustomize-api-versions", "v1"))
require.NoError(t, f.SetFlag("kustomize-api-versions", "v2"))
assert.Equal(t, []string{"v1", "v2"}, f.spec.Source.Kustomize.APIVersions)
})
t.Run("Helm Namespace", func(t *testing.T) {
require.NoError(t, f.SetFlag("helm-namespace", "override-namespace"))
assert.Equal(t, "override-namespace", f.spec.Source.Helm.Namespace)
})
t.Run("Helm Kube Version", func(t *testing.T) {
require.NoError(t, f.SetFlag("kustomize-kube-version", "999.999.999"))
assert.Equal(t, "999.999.999", f.spec.Source.Kustomize.KubeVersion)
})
t.Run("Helm API Versions", func(t *testing.T) {
require.NoError(t, f.SetFlag("helm-api-versions", "v1"))
require.NoError(t, f.SetFlag("helm-api-versions", "v2"))
assert.Equal(t, []string{"v1", "v2"}, f.spec.Source.Helm.APIVersions)
})
}

func newMultiSourceAppOptionsFixture() *appOptionsFixture {
Expand Down
23 changes: 23 additions & 0 deletions docs/operator-manual/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,19 @@ spec:
# and decide which Helm binary to use automatically. This field can be either 'v2' or 'v3'.
version: v2

# You can specify the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD uses
# the Kubernetes version of the target cluster. The value must be semver formatted. Do not prefix with `v`.
kubeVersion: 1.30.0

# You can specify the Kubernetes resource API versions to pass to Helm when templating manifests. By default, Argo
# CD uses the API versions of the target cluster. The format is [group/]version/kind.
apiVersions:
- traefik.io/v1alpha1/TLSOption
- v1/Service

# Optional namespace to template with. If left empty, defaults to the app's destination namespace.
namespace: custom-namespace

# kustomize specific config
kustomize:
# Optional kustomize version. Note: version must be configured in argocd-cm ConfigMap
Expand Down Expand Up @@ -124,6 +137,16 @@ spec:
value:
env: "pro"
# You can specify the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD uses
# the Kubernetes version of the target cluster. The value must be semver formatted. Do not prefix with `v`.
kubeVersion: 1.30.0

# You can specify the Kubernetes resource API versions to pass to Helm when templating manifests. By default, Argo
# CD uses the API versions of the target cluster. The format is [group/]version/kind.
apiVersions:
- traefik.io/v1alpha1/TLSOption
- v1/Service

# directory
directory:
recurse: true
Expand Down
5 changes: 5 additions & 0 deletions docs/user-guide/commands/argocd_admin_app_generate-spec.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 21ed19b

Please sign in to comment.