Add support for creating HR with .spec.ChartRef
Signed-off-by: Soule BA <bah.soule@gmail.com>
This commit is contained in:
@@ -36,6 +36,8 @@ import (
|
|||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
"github.com/fluxcd/pkg/runtime/transform"
|
"github.com/fluxcd/pkg/runtime/transform"
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
|
sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/v2/internal/flags"
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
@@ -104,7 +106,17 @@ var createHelmReleaseCmd = &cobra.Command{
|
|||||||
--source=HelmRepository/podinfo \
|
--source=HelmRepository/podinfo \
|
||||||
--chart=podinfo \
|
--chart=podinfo \
|
||||||
--values=./values.yaml \
|
--values=./values.yaml \
|
||||||
--export > podinfo-release.yaml`,
|
--export > podinfo-release.yaml
|
||||||
|
|
||||||
|
# Create a HelmRelease using a chart from a HelmChart resource
|
||||||
|
flux create hr podinfo \
|
||||||
|
--namespace=default \
|
||||||
|
--chart-ref=HelmChart/podinfo.flux-system \
|
||||||
|
|
||||||
|
# Create a HelmRelease using a chart from an OCIRepository resource
|
||||||
|
flux create hr podinfo \
|
||||||
|
--namespace=default \
|
||||||
|
--chart-ref=OCIRepository/podinfo.flux-system`,
|
||||||
RunE: createHelmReleaseCmdRun,
|
RunE: createHelmReleaseCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +126,7 @@ type helmReleaseFlags struct {
|
|||||||
dependsOn []string
|
dependsOn []string
|
||||||
chart string
|
chart string
|
||||||
chartVersion string
|
chartVersion string
|
||||||
|
chartRef string
|
||||||
targetNamespace string
|
targetNamespace string
|
||||||
createNamespace bool
|
createNamespace bool
|
||||||
valuesFiles []string
|
valuesFiles []string
|
||||||
@@ -129,6 +142,8 @@ var helmReleaseArgs helmReleaseFlags
|
|||||||
|
|
||||||
var supportedHelmReleaseValuesFromKinds = []string{"Secret", "ConfigMap"}
|
var supportedHelmReleaseValuesFromKinds = []string{"Secret", "ConfigMap"}
|
||||||
|
|
||||||
|
var supportedHelmReleaseReferenceKinds = []string{sourcev1b2.OCIRepositoryKind, sourcev1.HelmChartKind}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.name, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.name, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'")
|
||||||
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.source, "source", helmReleaseArgs.source.Description())
|
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.source, "source", helmReleaseArgs.source.Description())
|
||||||
@@ -144,14 +159,15 @@ func init() {
|
|||||||
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.valuesFrom, "values-from", nil, "a Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>', where kind must be one of: (Secret,ConfigMap)")
|
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.valuesFrom, "values-from", nil, "a Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>', where kind must be one of: (Secret,ConfigMap)")
|
||||||
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.crds, "crds", helmReleaseArgs.crds.Description())
|
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.crds, "crds", helmReleaseArgs.crds.Description())
|
||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.kubeConfigSecretRef, "kubeconfig-secret-ref", "", "the name of the Kubernetes Secret that contains a key with the kubeconfig file for connecting to a remote cluster")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.kubeConfigSecretRef, "kubeconfig-secret-ref", "", "the name of the Kubernetes Secret that contains a key with the kubeconfig file for connecting to a remote cluster")
|
||||||
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartRef, "chart-ref", "", "the name of the HelmChart resource to use as source for the HelmRelease, in the format '<kind>/<name>.<namespace>', where kind must be one of: (OCIRepository,HelmChart)")
|
||||||
createCmd.AddCommand(createHelmReleaseCmd)
|
createCmd.AddCommand(createHelmReleaseCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
if helmReleaseArgs.chart == "" {
|
if helmReleaseArgs.chart == "" && helmReleaseArgs.chartRef == "" {
|
||||||
return fmt.Errorf("chart name or path is required")
|
return fmt.Errorf("chart or chart-ref is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceLabels, err := parseLabels()
|
sourceLabels, err := parseLabels()
|
||||||
@@ -181,23 +197,42 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Duration: createArgs.interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
TargetNamespace: helmReleaseArgs.targetNamespace,
|
TargetNamespace: helmReleaseArgs.targetNamespace,
|
||||||
|
Suspend: false,
|
||||||
Chart: &helmv2.HelmChartTemplate{
|
|
||||||
Spec: helmv2.HelmChartTemplateSpec{
|
|
||||||
Chart: helmReleaseArgs.chart,
|
|
||||||
Version: helmReleaseArgs.chartVersion,
|
|
||||||
SourceRef: helmv2.CrossNamespaceObjectReference{
|
|
||||||
Kind: helmReleaseArgs.source.Kind,
|
|
||||||
Name: helmReleaseArgs.source.Name,
|
|
||||||
Namespace: helmReleaseArgs.source.Namespace,
|
|
||||||
},
|
|
||||||
ReconcileStrategy: helmReleaseArgs.reconcileStrategy,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Suspend: false,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case helmReleaseArgs.chart != "":
|
||||||
|
helmRelease.Spec.Chart = &helmv2.HelmChartTemplate{
|
||||||
|
Spec: helmv2.HelmChartTemplateSpec{
|
||||||
|
Chart: helmReleaseArgs.chart,
|
||||||
|
Version: helmReleaseArgs.chartVersion,
|
||||||
|
SourceRef: helmv2.CrossNamespaceObjectReference{
|
||||||
|
Kind: helmReleaseArgs.source.Kind,
|
||||||
|
Name: helmReleaseArgs.source.Name,
|
||||||
|
Namespace: helmReleaseArgs.source.Namespace,
|
||||||
|
},
|
||||||
|
ReconcileStrategy: helmReleaseArgs.reconcileStrategy,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if helmReleaseArgs.chartInterval != 0 {
|
||||||
|
helmRelease.Spec.Chart.Spec.Interval = &metav1.Duration{
|
||||||
|
Duration: helmReleaseArgs.chartInterval,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case helmReleaseArgs.chartRef != "":
|
||||||
|
kind, name, ns := utils.ParseObjectKindNameNamespace(helmReleaseArgs.chartRef)
|
||||||
|
if kind != sourcev1.HelmChartKind && kind != sourcev1b2.OCIRepositoryKind {
|
||||||
|
return fmt.Errorf("chart reference kind '%s' is not supported, must be one of: %s",
|
||||||
|
kind, strings.Join(supportedHelmReleaseReferenceKinds, ", "))
|
||||||
|
}
|
||||||
|
helmRelease.Spec.ChartRef = &helmv2.CrossNamespaceSourceReference{
|
||||||
|
Kind: kind,
|
||||||
|
Name: name,
|
||||||
|
Namespace: ns,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if helmReleaseArgs.kubeConfigSecretRef != "" {
|
if helmReleaseArgs.kubeConfigSecretRef != "" {
|
||||||
helmRelease.Spec.KubeConfig = &meta.KubeConfigReference{
|
helmRelease.Spec.KubeConfig = &meta.KubeConfigReference{
|
||||||
SecretRef: meta.SecretKeyReference{
|
SecretRef: meta.SecretKeyReference{
|
||||||
@@ -206,12 +241,6 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if helmReleaseArgs.chartInterval != 0 {
|
|
||||||
helmRelease.Spec.Chart.Spec.Interval = &metav1.Duration{
|
|
||||||
Duration: helmReleaseArgs.chartInterval,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if helmReleaseArgs.createNamespace {
|
if helmReleaseArgs.createNamespace {
|
||||||
if helmRelease.Spec.Install == nil {
|
if helmRelease.Spec.Install == nil {
|
||||||
helmRelease.Spec.Install = &helmv2.Install{}
|
helmRelease.Spec.Install = &helmv2.Install{}
|
||||||
|
|||||||
86
cmd/flux/create_helmrelease_test.go
Normal file
86
cmd/flux/create_helmrelease_test.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
//go:build unit
|
||||||
|
// +build unit
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2024 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestCreateHelmRelease(t *testing.T) {
|
||||||
|
tmpl := map[string]string{
|
||||||
|
"fluxns": allocateNamespace("flux-system"),
|
||||||
|
}
|
||||||
|
setupHRSource(t, tmpl)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args string
|
||||||
|
assert assertFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "missing name",
|
||||||
|
args: "create helmrelease --export",
|
||||||
|
assert: assertError("name is required"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing chart template and chartRef",
|
||||||
|
args: "create helmrelease podinfo --export",
|
||||||
|
assert: assertError("chart or chart-ref is required"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown source kind",
|
||||||
|
args: "create helmrelease podinfo --source foobar/podinfo --chart podinfo --export",
|
||||||
|
assert: assertError(`invalid argument "foobar/podinfo" for "--source" flag: source kind 'foobar' is not supported, must be one of: HelmRepository, GitRepository, Bucket`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown chart reference kind",
|
||||||
|
args: "create helmrelease podinfo --chart-ref foobar/podinfo --export",
|
||||||
|
assert: assertError(`chart reference kind 'foobar' is not supported, must be one of: OCIRepository, HelmChart`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic helmrelease",
|
||||||
|
args: "create helmrelease podinfo --source Helmrepository/podinfo --chart podinfo --interval=1m0s --export",
|
||||||
|
assert: assertGoldenTemplateFile("testdata/create_hr/basic.yaml", tmpl),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "chart with OCIRepository source",
|
||||||
|
args: "create helmrelease podinfo --chart-ref OCIRepository/podinfo --interval=1m0s --export",
|
||||||
|
assert: assertGoldenTemplateFile("testdata/create_hr/or_basic.yaml", tmpl),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "chart with HelmChart source",
|
||||||
|
args: "create helmrelease podinfo --chart-ref HelmChart/podinfo --interval=1m0s --export",
|
||||||
|
assert: assertGoldenTemplateFile("testdata/create_hr/hc_basic.yaml", tmpl),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: tt.args + " -n " + tmpl["fluxns"],
|
||||||
|
assert: tt.assert,
|
||||||
|
}
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupHRSource(t *testing.T, tmpl map[string]string) {
|
||||||
|
t.Helper()
|
||||||
|
testEnv.CreateObjectFile("./testdata/create_hr/setup-source.yaml", tmpl, t)
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
|
sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reconcileHrCmd = &cobra.Command{
|
var reconcileHrCmd = &cobra.Command{
|
||||||
@@ -68,32 +69,46 @@ func (obj helmReleaseAdapter) reconcileSource() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (obj helmReleaseAdapter) getSource() (reconcileSource, types.NamespacedName) {
|
func (obj helmReleaseAdapter) getSource() (reconcileSource, types.NamespacedName) {
|
||||||
cmd := reconcileWithSourceCommand{
|
|
||||||
apiType: helmChartType,
|
|
||||||
object: helmChartAdapter{&sourcev1.HelmChart{}},
|
|
||||||
force: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
name string
|
name string
|
||||||
ns string
|
ns string
|
||||||
)
|
)
|
||||||
switch {
|
switch {
|
||||||
case obj.Spec.Chart != &helmv2.HelmChart{}:
|
|
||||||
ns = obj.Spec.Chart.Spec.SourceRef.Namespace
|
|
||||||
name = fmt.Sprintf("%s-%s", obj.Namespace, obj.Name)
|
|
||||||
case obj.Spec.ChartRef != nil:
|
case obj.Spec.ChartRef != nil:
|
||||||
ns = obj.Spec.ChartRef.Namespace
|
name, ns = obj.Spec.ChartRef.Name, obj.Spec.ChartRef.Namespace
|
||||||
name = obj.Spec.ChartRef.Name
|
if ns == "" {
|
||||||
}
|
ns = obj.Namespace
|
||||||
|
}
|
||||||
if ns == "" {
|
namespacedName := types.NamespacedName{
|
||||||
ns = obj.Namespace
|
Name: name,
|
||||||
}
|
Namespace: ns,
|
||||||
|
}
|
||||||
return cmd, types.NamespacedName{
|
if obj.Spec.ChartRef.Kind == sourcev1.HelmChartKind {
|
||||||
Name: name,
|
return reconcileWithSourceCommand{
|
||||||
Namespace: ns,
|
apiType: helmChartType,
|
||||||
|
object: helmChartAdapter{&sourcev1.HelmChart{}},
|
||||||
|
force: true,
|
||||||
|
}, namespacedName
|
||||||
|
}
|
||||||
|
return reconcileCommand{
|
||||||
|
apiType: ociRepositoryType,
|
||||||
|
object: ociRepositoryAdapter{&sourcev1b2.OCIRepository{}},
|
||||||
|
}, namespacedName
|
||||||
|
default:
|
||||||
|
// default case assumes the HelmRelease is using a HelmChartTemplate
|
||||||
|
ns = obj.Spec.Chart.Spec.SourceRef.Namespace
|
||||||
|
if ns == "" {
|
||||||
|
ns = obj.Namespace
|
||||||
|
}
|
||||||
|
name = fmt.Sprintf("%s-%s", obj.Namespace, obj.Name)
|
||||||
|
return reconcileWithSourceCommand{
|
||||||
|
apiType: helmChartType,
|
||||||
|
object: helmChartAdapter{&sourcev1.HelmChart{}},
|
||||||
|
force: true,
|
||||||
|
}, types.NamespacedName{
|
||||||
|
Name: name,
|
||||||
|
Namespace: ns,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
cmd/flux/testdata/create_hr/basic.yaml
vendored
Normal file
15
cmd/flux/testdata/create_hr/basic.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||||
|
kind: HelmRelease
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: {{ .fluxns }}
|
||||||
|
spec:
|
||||||
|
chart:
|
||||||
|
spec:
|
||||||
|
chart: podinfo
|
||||||
|
reconcileStrategy: ChartVersion
|
||||||
|
sourceRef:
|
||||||
|
kind: HelmRepository
|
||||||
|
name: podinfo
|
||||||
|
interval: 1m0s
|
||||||
11
cmd/flux/testdata/create_hr/hc_basic.yaml
vendored
Normal file
11
cmd/flux/testdata/create_hr/hc_basic.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||||
|
kind: HelmRelease
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: {{ .fluxns }}
|
||||||
|
spec:
|
||||||
|
chartRef:
|
||||||
|
kind: HelmChart
|
||||||
|
name: podinfo
|
||||||
|
interval: 1m0s
|
||||||
11
cmd/flux/testdata/create_hr/or_basic.yaml
vendored
Normal file
11
cmd/flux/testdata/create_hr/or_basic.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||||
|
kind: HelmRelease
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: {{ .fluxns }}
|
||||||
|
spec:
|
||||||
|
chartRef:
|
||||||
|
kind: OCIRepository
|
||||||
|
name: podinfo
|
||||||
|
interval: 1m0s
|
||||||
39
cmd/flux/testdata/create_hr/setup-source.yaml
vendored
Normal file
39
cmd/flux/testdata/create_hr/setup-source.yaml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: {{ .fluxns }}
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1
|
||||||
|
kind: HelmRepository
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: {{ .fluxns }}
|
||||||
|
spec:
|
||||||
|
interval: 1m0s
|
||||||
|
provider: generic
|
||||||
|
type: oci
|
||||||
|
url: oci://ghcr.io/stefanprodan/charts
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1
|
||||||
|
kind: HelmChart
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: {{ .fluxns }}
|
||||||
|
spec:
|
||||||
|
interval: 1m0s
|
||||||
|
chart: podinfo
|
||||||
|
sourceRef:
|
||||||
|
kind: HelmRepository
|
||||||
|
name: podinfo
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||||
|
kind: OCIRepository
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 10m
|
||||||
|
url: oci://ghcr.io/stefanprodan/manifests/podinfo
|
||||||
|
ref:
|
||||||
|
tag: latest
|
||||||
Reference in New Issue
Block a user