Initial support for HelmRelease for upgrading CRDs
Signed-off-by: Alexander Berger <alex-berger@gmx.ch>
This commit is contained in:
parent
449a469ec4
commit
a6cc150aa6
|
@ -1,6 +1,7 @@
|
|||
name: e2e
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
|
@ -511,6 +512,33 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
kubectl -n helm-system delete -f config/testdata/post-renderer-kustomize
|
||||
- name: Boostrap CRDs Upgrade Tests
|
||||
run: |
|
||||
REF=${{ github.ref }}
|
||||
if echo "$REF" | grep 'refs/tags/'; then
|
||||
TYPE=tag
|
||||
REF=${REF#refs/tags/}
|
||||
else
|
||||
TYPE=branch
|
||||
if echo "$REF" | grep 'refs/pull/'; then
|
||||
REF=${REF#refs/pull/}
|
||||
else
|
||||
REF=${REF#refs/heads/}
|
||||
fi
|
||||
fi
|
||||
echo "$HEAD_REF,$CURR_REF -> $REF of type $TYPE"
|
||||
echo "helm install --namespace default --set $TYPE=$REF --set url=https://github.com/${{ github.repository }} this config/testdata/charts/crds/bootstrap"
|
||||
helm install --namespace default --set $TYPE=$REF --set url=https://github.com/${{ github.repository }} this config/testdata/charts/crds/bootstrap
|
||||
kubectl -n default apply -f config/testdata/crds-upgrade/init
|
||||
kubectl -n default wait helmreleases/crds-upgrade-test --for=condition=ready --timeout=2m
|
||||
- name: CRDs Upgrade Test Create
|
||||
run: |
|
||||
kubectl -n default apply -f config/testdata/crds-upgrade/create
|
||||
kubectl -n default wait helmreleases/crds-upgrade-test --for=condition=ready --timeout=2m
|
||||
- name: CRDs Upgrade Test CreateReplace
|
||||
run: |
|
||||
kubectl -n default apply -f config/testdata/crds-upgrade/create-replace
|
||||
kubectl -n default wait helmreleases/crds-upgrade-test --for=condition=ready --timeout=2m
|
||||
- name: Logs
|
||||
run: |
|
||||
kubectl -n helm-system logs deploy/source-controller
|
||||
|
|
|
@ -431,6 +431,19 @@ func (in InstallRemediation) RetriesExhausted(hr HelmRelease) bool {
|
|||
return in.Retries >= 0 && in.GetFailureCount(hr) > int64(in.Retries)
|
||||
}
|
||||
|
||||
// CRDsUpgradePolicy defines the upgrade approach to use for CRDs when upgrading
|
||||
// a HelmRelease.
|
||||
type CRDsChangePolicy string
|
||||
|
||||
const (
|
||||
// Create CRDs which do not already exist, do not replace already existing CRDs
|
||||
// and keep (do not delete) CRDs which no longer exist in the current release.
|
||||
Create CRDsChangePolicy = "Create"
|
||||
// Create CRDs which do not already exist, Replace already existing CRDs
|
||||
// and keep (do not delete) CRDs which no longer exist in the current release.
|
||||
CreateReplace CRDsChangePolicy = "CreateReplace"
|
||||
)
|
||||
|
||||
// Upgrade holds the configuration for Helm upgrade actions for this
|
||||
// HelmRelease.
|
||||
type Upgrade struct {
|
||||
|
@ -473,6 +486,24 @@ type Upgrade struct {
|
|||
// upgrade action when it fails.
|
||||
// +optional
|
||||
CleanupOnFail bool `json:"cleanupOnFail,omitempty"`
|
||||
|
||||
// UpgradeCRDs upgrade CRDs from the Helm Chart's crds directory according
|
||||
// to the CRD upgrade policy provided here. Valid values are `Create` or
|
||||
// `CreateReplace`. If omitted (the default) CRDs
|
||||
// are not upgraded.
|
||||
//
|
||||
// Create: new CRDs are created, existing CRDs are neither updated nor deleted.
|
||||
//
|
||||
// CreateReplace: new CRDs are created, existing CRDs are updated (replaced)
|
||||
// but not deleted.
|
||||
//
|
||||
// By default, CRDs are not applied during Helm upgrade action. With this
|
||||
// option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm.
|
||||
// https://helm.sh/docs/chart_best_practices/custom_resource_definitions.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=Create;CreateReplace
|
||||
// +optional
|
||||
UpgradeCRDs CRDsChangePolicy `json:"upgradeCRDs,omitempty"`
|
||||
}
|
||||
|
||||
// GetTimeout returns the configured timeout for the Helm upgrade action, or the
|
||||
|
|
|
@ -515,6 +515,20 @@ spec:
|
|||
operation (like Jobs for hooks) during the performance of a
|
||||
Helm upgrade action. Defaults to 'HelmReleaseSpec.Timeout'.
|
||||
type: string
|
||||
upgradeCRDs:
|
||||
description: "UpgradeCRDs upgrade CRDs from the Helm Chart's crds
|
||||
directory according to the CRD upgrade policy provided here.
|
||||
Valid values are `Create` or `CreateReplace`. If omitted (the
|
||||
default) CRDs are not upgraded. \n Create: new CRDs are created,
|
||||
existing CRDs are neither updated nor deleted. \n CreateReplace:
|
||||
new CRDs are created, existing CRDs are updated (replaced) but
|
||||
not deleted. \n By default, CRDs are not applied during Helm
|
||||
upgrade action. With this option users can opt-in to CRD upgrade,
|
||||
which is not (yet) natively supported by Helm. https://helm.sh/docs/chart_best_practices/custom_resource_definitions."
|
||||
enum:
|
||||
- Create
|
||||
- CreateReplace
|
||||
type: string
|
||||
type: object
|
||||
values:
|
||||
description: Values holds the values for this Helm release.
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
|
@ -0,0 +1,23 @@
|
|||
apiVersion: v2
|
||||
name: crd-upgrade-bootstrap
|
||||
description: Helper Chart to bootstrap e2e test GitRepository
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 1.0.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
appVersion: 1.0.0
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
name: this
|
||||
namespace: default
|
||||
spec:
|
||||
interval: 1m
|
||||
url: "{{ .Values.url }}"
|
||||
ref:
|
||||
{{- if .Values.branch }}
|
||||
branch: "{{ .Values.branch }}"
|
||||
{{- end}}
|
||||
{{- if .Values.branch }}
|
||||
tag: "{{ .Values.tag }}"
|
||||
{{- end}}
|
|
@ -0,0 +1,3 @@
|
|||
url: "https://github.com/fluxcd/helm-controller"
|
||||
branch: null
|
||||
tag: null
|
|
@ -0,0 +1,7 @@
|
|||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
|
@ -0,0 +1,23 @@
|
|||
apiVersion: v2
|
||||
name: crd-upgrade
|
||||
description: CRDs Upgrade Test Chart v1
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 1.0.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
appVersion: 1.0.0
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: crdupgradetestas.crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
spec:
|
||||
group: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
names:
|
||||
kind: CrdUpgradeTesta
|
||||
listKind: CrdUpgradeTestaList
|
||||
plural: crdupgradetestas
|
||||
singular: crdupgradetesta
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v2beta1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Test
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties: {}
|
||||
type: object
|
||||
status:
|
||||
properties: {}
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: crdupgradetestbs.crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
spec:
|
||||
group: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
names:
|
||||
kind: CrdUpgradeTestb
|
||||
listKind: CrdUpgradeTestbList
|
||||
plural: crdupgradetestbs
|
||||
singular: crdupgradetestb
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v2beta1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Test
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties: {}
|
||||
type: object
|
||||
status:
|
||||
properties: {}
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: crdupgradetestds.crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
spec:
|
||||
group: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
names:
|
||||
kind: CrdUpgradeTestd
|
||||
listKind: CrdUpgradeTestdList
|
||||
plural: crdupgradetestds
|
||||
singular: crdupgradetestd
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v2beta1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Test
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties: {}
|
||||
type: object
|
||||
status:
|
||||
properties: {}
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
kind: CrdUpgradeTesta
|
||||
apiVersion: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io/v2beta1
|
||||
metadata:
|
||||
name: a
|
||||
spec: {}
|
|
@ -0,0 +1,7 @@
|
|||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
|
@ -0,0 +1,23 @@
|
|||
apiVersion: v2
|
||||
name: crd-upgrade
|
||||
description: CRDs Upgrade Test Chart v1
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 1.0.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
appVersion: 1.0.0
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: crdupgradetestas.crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
spec:
|
||||
group: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
names:
|
||||
kind: CrdUpgradeTesta
|
||||
listKind: CrdUpgradeTestaList
|
||||
plural: crdupgradetestas
|
||||
singular: crdupgradetesta
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v2beta1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Test
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties: {}
|
||||
type: object
|
||||
status:
|
||||
properties: {}
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: crdupgradetestbs.crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
spec:
|
||||
group: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
names:
|
||||
kind: CrdUpgradeTestb
|
||||
listKind: CrdUpgradeTestbList
|
||||
plural: crdupgradetestbs
|
||||
singular: crdupgradetestb
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v2beta1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Test
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties: {}
|
||||
type: object
|
||||
status:
|
||||
properties: {}
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
subresources:
|
||||
status: {}
|
||||
- name: v2beta2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Test
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties: {}
|
||||
type: object
|
||||
status:
|
||||
properties: {}
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: crdupgradetestcs.crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
spec:
|
||||
group: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io
|
||||
names:
|
||||
kind: CrdUpgradeTestc
|
||||
listKind: CrdUpgradeTestcList
|
||||
plural: crdupgradetestcs
|
||||
singular: crdupgradetestc
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v2beta2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Test
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties: {}
|
||||
type: object
|
||||
status:
|
||||
properties: {}
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
{{- if .Values.a }}
|
||||
---
|
||||
kind: CrdUpgradeTesta
|
||||
apiVersion: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io/{{.Values.a}}
|
||||
metadata:
|
||||
name: a
|
||||
spec: {}
|
||||
{{- end }}
|
||||
{{- if .Values.b }}
|
||||
---
|
||||
kind: CrdUpgradeTestb
|
||||
apiVersion: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io/{{.Values.b}}
|
||||
metadata:
|
||||
name: b
|
||||
spec: {}
|
||||
{{- end }}
|
||||
{{- if .Values.c }}
|
||||
---
|
||||
kind: CrdUpgradeTestc
|
||||
apiVersion: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io/{{.Values.c}}
|
||||
metadata:
|
||||
name: c
|
||||
spec: {}
|
||||
{{- end }}
|
||||
{{- if .Values.d }}
|
||||
---
|
||||
kind: CrdUpgradeTestd
|
||||
apiVersion: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io/{{.Values.d}}
|
||||
metadata:
|
||||
name: d
|
||||
spec: {}
|
||||
{{- end }}
|
|
@ -0,0 +1,4 @@
|
|||
# a: v2beta1
|
||||
# b: v2beta1
|
||||
# c: v2beta1
|
||||
# d: v2beta1
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: crds-upgrade-test
|
||||
namespace: default
|
||||
spec:
|
||||
interval: 1m
|
||||
chart:
|
||||
spec:
|
||||
chart: config/testdata/charts/crds/v2
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: this
|
||||
namespace: default
|
||||
interval: 1m
|
||||
upgrade:
|
||||
upgradeCRDs: CreateReplace
|
||||
values:
|
||||
a: v2beta1
|
||||
b: v2beta2
|
||||
c: v2beta2
|
||||
d: v2beta1
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: crds-upgrade-test
|
||||
namespace: default
|
||||
spec:
|
||||
interval: 1m
|
||||
chart:
|
||||
spec:
|
||||
chart: config/testdata/charts/crds/v2
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: this
|
||||
namespace: default
|
||||
interval: 1m
|
||||
upgrade:
|
||||
upgradeCRDs: Create
|
||||
values:
|
||||
a: v2beta1
|
||||
b: v2beta1
|
||||
c: v2beta2
|
||||
d: v2beta1
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: crds-upgrade-test
|
||||
namespace: default
|
||||
spec:
|
||||
interval: 1m
|
||||
chart:
|
||||
spec:
|
||||
chart: config/testdata/charts/crds/v1
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: this
|
||||
namespace: default
|
||||
interval: 1m
|
||||
|
|
@ -351,6 +351,14 @@ HelmReleaseStatus
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="helm.toolkit.fluxcd.io/v2beta1.CRDsChangePolicy">CRDsChangePolicy
|
||||
(<code>string</code> alias)</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#helm.toolkit.fluxcd.io/v2beta1.Upgrade">Upgrade</a>)
|
||||
</p>
|
||||
<p>CRDsUpgradePolicy defines the upgrade approach to use for CRDs when upgrading
|
||||
a HelmRelease.</p>
|
||||
<h3 id="helm.toolkit.fluxcd.io/v2beta1.CrossNamespaceObjectReference">CrossNamespaceObjectReference
|
||||
</h3>
|
||||
<p>
|
||||
|
@ -1794,6 +1802,29 @@ bool
|
|||
upgrade action when it fails.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>upgradeCRDs</code><br>
|
||||
<em>
|
||||
<a href="#helm.toolkit.fluxcd.io/v2beta1.CRDsChangePolicy">
|
||||
CRDsChangePolicy
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>UpgradeCRDs upgrade CRDs from the Helm Chart’s crds directory according
|
||||
to the CRD upgrade policy provided here. Valid values are <code>Create</code> or
|
||||
<code>CreateReplace</code>. If omitted (the default) CRDs
|
||||
are not upgraded.</p>
|
||||
<p>Create: new CRDs are created, existing CRDs are neither updated nor deleted.</p>
|
||||
<p>CreateReplace: new CRDs are created, existing CRDs are updated (replaced)
|
||||
but not deleted.</p>
|
||||
<p>By default, CRDs are not applied during Helm upgrade action. With this
|
||||
option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm.
|
||||
<a href="https://helm.sh/docs/chart_best_practices/custom_resource_definitions">https://helm.sh/docs/chart_best_practices/custom_resource_definitions</a>.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -268,6 +268,24 @@ type Upgrade struct {
|
|||
// upgrade action when it fails.
|
||||
// +optional
|
||||
CleanupOnFail bool `json:"cleanupOnFail,omitempty"`
|
||||
|
||||
// UpgradeCRDs upgrade CRDs from the Helm Chart's crds directory according
|
||||
// to the CRD upgrade policy provided here. Valid values are `Create` or
|
||||
// `CreateReplace`. If omitted (the default) CRDs
|
||||
// are not upgraded.
|
||||
//
|
||||
// Create: new CRDs are created, existing CRDs are neither updated nor deleted.
|
||||
//
|
||||
// CreateReplace: new CRDs are created, existing CRDs are updated (replaced)
|
||||
// but not deleted.
|
||||
//
|
||||
// By default, CRDs are not applied during Helm upgrade action. With this
|
||||
// option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm.
|
||||
// https://helm.sh/docs/chart_best_practices/custom_resource_definitions.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=Create;CreateReplace
|
||||
// +optional
|
||||
UpgradeCRDs CRDsChangePolicy `json:"upgradeCRDs,omitempty"`
|
||||
}
|
||||
|
||||
// UpgradeRemediation holds the configuration for Helm upgrade remediation.
|
||||
|
@ -1109,6 +1127,53 @@ spec:
|
|||
newTag: 0.4.1-debian-10-r54
|
||||
```
|
||||
|
||||
## CRDs
|
||||
|
||||
Helm does support [installing CRDs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#method-1-let-helm-do-it-for-you),
|
||||
but it has no native support for [upgrading CRDs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#some-caveats-and-explanations):
|
||||
|
||||
> There is no support at this time for upgrading or deleting CRDs using Helm.
|
||||
> This was an explicit decision after much community discussion due to the danger for unintentional
|
||||
> data loss. Furthermore, there is currently no community consensus around how to handle CRDs and
|
||||
> their lifecycle. As this evolves, Helm will add support for those use cases.
|
||||
|
||||
If your write your own Helm Charts you can work-around this limitation by putting your CRDs into
|
||||
the `templates` instead of the `crds` directory or by out-factoring them into a separate Helm
|
||||
Chart as suggested by the
|
||||
[offical Helm documentation](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#method-2-separate-charts).
|
||||
|
||||
However, if you have to integrate and use many existing (upstream) Helm Charts, not being able to
|
||||
upgrade the CRDs via FluxCD `HelmRelease` objects might become a cumbersome limitation within your GitOps
|
||||
workflow. Therefore, FluxCD allows you to opt-in to upgrading CRDs by setting the `UpgradeCRDs` policy on
|
||||
the `HelmRelease.spec.upgrade` object. The following UpgradeCRDs policies are supported:
|
||||
|
||||
- `Create` Only create new CRDs which doe not yet exist, neither update nor delete any existing CRDs.
|
||||
- `CreateReplace` Create new CRDs, update (replace) existing ones, but do **not** delete CRDs which
|
||||
no longer exist in the current helm release.
|
||||
|
||||
**Example**:
|
||||
|
||||
```yaml
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: my-operator
|
||||
namespace: default
|
||||
spec:
|
||||
interval: 1m
|
||||
chart:
|
||||
spec:
|
||||
chart: my-operator
|
||||
version: "1.0.1"
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: my-operator-repo
|
||||
namespace: default
|
||||
interval: 1m
|
||||
upgrade:
|
||||
upgradeCRDs: CreateReplace
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
When the controller completes a reconciliation, it reports the result in the status sub-resource.
|
||||
|
|
|
@ -17,17 +17,29 @@ limitations under the License.
|
|||
package runner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/postrender"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
apiextension "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
v2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
)
|
||||
|
@ -58,7 +70,7 @@ type Runner struct {
|
|||
// namespace configured to the provided values.
|
||||
func NewRunner(getter genericclioptions.RESTClientGetter, storageNamespace string, logger logr.Logger) (*Runner, error) {
|
||||
runner := &Runner{
|
||||
logBuffer: NewLogBuffer(NewDebugLog(logger).Log, 0),
|
||||
logBuffer: NewLogBuffer(NewDebugLog(logger).Log, 100),
|
||||
}
|
||||
runner.config = new(action.Configuration)
|
||||
if err := runner.config.Init(getter, storageNamespace, "secret", runner.logBuffer.Log); err != nil {
|
||||
|
@ -131,14 +143,168 @@ func (r *Runner) Upgrade(hr v2.HelmRelease, chart *chart.Chart, values chartutil
|
|||
upgrade.Devel = true
|
||||
renderer, err := postRenderers(hr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, wrapActionErr(r.logBuffer, err)
|
||||
}
|
||||
upgrade.PostRenderer = renderer
|
||||
|
||||
// If user opted-in to upgrade CRDs, upgrade them first.
|
||||
cRDsChangePolicy, err := r.validateCRDsChangePolicy(hr.Spec.GetUpgrade().UpgradeCRDs)
|
||||
if err != nil {
|
||||
return nil, wrapActionErr(r.logBuffer, err)
|
||||
}
|
||||
if cRDsChangePolicy != "" {
|
||||
crds := chart.CRDObjects()
|
||||
if len(crds) > 0 {
|
||||
if err := r.upgradeCRDs(cRDsChangePolicy, hr, chart); err != nil {
|
||||
return nil, wrapActionErr(r.logBuffer, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
rel, err := upgrade.Run(hr.GetReleaseName(), chart, values.AsMap())
|
||||
return rel, wrapActionErr(r.logBuffer, err)
|
||||
}
|
||||
|
||||
func (r *Runner) validateCRDsChangePolicy(policy v2.CRDsChangePolicy) (v2.CRDsChangePolicy, error) {
|
||||
switch policy {
|
||||
case "":
|
||||
break
|
||||
case v2.Create:
|
||||
break
|
||||
case v2.CreateReplace:
|
||||
break
|
||||
default:
|
||||
return policy, errors.New(
|
||||
fmt.Sprintf("Invalid CRD upgrade policy '%s' defined in field upgradeCRDs, valid values are '%s' or '%s'",
|
||||
policy, v2.Create, v2.CreateReplace,
|
||||
))
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
type rootScoped struct{}
|
||||
|
||||
func (*rootScoped) Name() meta.RESTScopeName {
|
||||
return meta.RESTScopeNameRoot
|
||||
}
|
||||
|
||||
// This has been adapte from https://github.com/helm/helm/blob/v3.5.4/pkg/action/install.go#L127
|
||||
func (r *Runner) upgradeCRDs(policy v2.CRDsChangePolicy, hr v2.HelmRelease, chart *chart.Chart) error {
|
||||
cfg := r.config
|
||||
cfg.Log("upgrade CRDs with policy %s", policy)
|
||||
// Collect all CRDs from all files in `crds` directory.
|
||||
allCrds := make(kube.ResourceList, 0)
|
||||
for _, obj := range chart.CRDObjects() {
|
||||
// Read in the resources
|
||||
res, err := cfg.KubeClient.Build(bytes.NewBuffer(obj.File.Data), false)
|
||||
if err != nil {
|
||||
cfg.Log("failed to parse CRDs from %s: %s", obj.Name, err)
|
||||
return errors.New(fmt.Sprintf("failed to parse CRDs from %s: %s", obj.Name, err))
|
||||
}
|
||||
allCrds = append(allCrds, res...)
|
||||
}
|
||||
totalItems := []*resource.Info{}
|
||||
if policy == v2.Create {
|
||||
for i := range allCrds {
|
||||
if rr, err := cfg.KubeClient.Create(allCrds[i : i+1]); err != nil {
|
||||
crdName := allCrds[i].Name
|
||||
// If the error is CRD already exists, continue.
|
||||
if apierrors.IsAlreadyExists(err) {
|
||||
cfg.Log("CRD %s is already present. Skipping.", crdName)
|
||||
if rr != nil && rr.Created != nil {
|
||||
totalItems = append(totalItems, rr.Created...)
|
||||
}
|
||||
continue
|
||||
}
|
||||
cfg.Log("failed to upgrade CRD %s: %s", crdName, err)
|
||||
return errors.New(fmt.Sprintf("failed to upgrade CRD %s: %s", crdName, err))
|
||||
} else {
|
||||
if rr != nil && rr.Created != nil {
|
||||
totalItems = append(totalItems, rr.Created...)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if policy == v2.CreateReplace {
|
||||
config, err := r.config.RESTClientGetter.ToRESTConfig()
|
||||
if err != nil {
|
||||
r.logBuffer.Log("Error while creating Kubernetes client config: %s", err)
|
||||
return err
|
||||
}
|
||||
clientset, err := apiextension.NewForConfig(config)
|
||||
if err != nil {
|
||||
r.logBuffer.Log("Error while creating Kubernetes clientset for apiextension: %s", err)
|
||||
return err
|
||||
}
|
||||
client := clientset.ApiextensionsV1().CustomResourceDefinitions()
|
||||
original := make(kube.ResourceList, 0)
|
||||
// Note, we build the originals from the current set of CRDs
|
||||
// and therefore this upgrade will never delete CRDs that existed in the former release
|
||||
// but no longer exist in the current release.
|
||||
for _, r := range allCrds {
|
||||
if o, err := client.Get(context.TODO(), r.Name, v1.GetOptions{}); err == nil && o != nil {
|
||||
o.GetResourceVersion()
|
||||
original = append(original, &resource.Info{
|
||||
Client: clientset.ApiextensionsV1().RESTClient(),
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{
|
||||
Group: "apiextensions.k8s.io",
|
||||
Version: r.Mapping.GroupVersionKind.Version,
|
||||
Resource: "customresourcedefinition",
|
||||
},
|
||||
GroupVersionKind: schema.GroupVersionKind{
|
||||
Kind: "CustomResourceDefinition",
|
||||
Group: "apiextensions.k8s.io",
|
||||
Version: r.Mapping.GroupVersionKind.Version,
|
||||
},
|
||||
Scope: &rootScoped{},
|
||||
},
|
||||
Namespace: o.ObjectMeta.Namespace,
|
||||
Name: o.ObjectMeta.Name,
|
||||
Object: o,
|
||||
ResourceVersion: o.ObjectMeta.ResourceVersion,
|
||||
})
|
||||
} else if !apierrors.IsNotFound(err) {
|
||||
cfg.Log("failed to get CRD %s: %s", r.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Send them to Kube
|
||||
if rr, err := cfg.KubeClient.Update(original, allCrds, true); err != nil {
|
||||
cfg.Log("failed to upgrade CRD %s", err)
|
||||
return errors.New(fmt.Sprintf("failed to upgrade CRD %s", err))
|
||||
} else {
|
||||
if rr != nil {
|
||||
if rr.Created != nil {
|
||||
totalItems = append(totalItems, rr.Created...)
|
||||
}
|
||||
if rr.Updated != nil {
|
||||
totalItems = append(totalItems, rr.Updated...)
|
||||
}
|
||||
if rr.Deleted != nil {
|
||||
totalItems = append(totalItems, rr.Deleted...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(totalItems) > 0 {
|
||||
// Invalidate the local cache, since it will not have the new CRDs
|
||||
// present.
|
||||
discoveryClient, err := cfg.RESTClientGetter.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
cfg.Log("Error in cfg.RESTClientGetter.ToDiscoveryClient(): %s", err)
|
||||
return err
|
||||
}
|
||||
cfg.Log("Clearing discovery cache")
|
||||
discoveryClient.Invalidate()
|
||||
// Give time for the CRD to be recognized.
|
||||
if err := cfg.KubeClient.Wait(totalItems, 60*time.Second); err != nil {
|
||||
cfg.Log("Error waiting for items: %s", err)
|
||||
return err
|
||||
}
|
||||
// Make sure to force a rebuild of the cache.
|
||||
discoveryClient.ServerGroups()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Test runs an Helm test action for the given v2beta1.HelmRelease.
|
||||
func (r *Runner) Test(hr v2.HelmRelease) (*release.Release, error) {
|
||||
r.mu.Lock()
|
||||
|
|
Loading…
Reference in New Issue