Initial support for HelmRelease for upgrading CRDs

Signed-off-by: Alexander Berger <alex-berger@gmx.ch>
This commit is contained in:
Alexander Berger 2021-04-15 22:38:52 +02:00
parent 449a469ec4
commit a6cc150aa6
27 changed files with 803 additions and 3 deletions

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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}}

View File

@ -0,0 +1,3 @@
url: "https://github.com/fluxcd/helm-controller"
branch: null
tag: null

View File

@ -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

View File

@ -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

View File

@ -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: {}

View File

@ -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: {}

View File

@ -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: {}

View File

@ -0,0 +1,6 @@
---
kind: CrdUpgradeTesta
apiVersion: crd-upgrades.helmreleases.helm.toolkit.fluxcd.io/v2beta1
metadata:
name: a
spec: {}

View File

View File

@ -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

View File

@ -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

View File

@ -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: {}

View File

@ -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: {}

View File

@ -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: {}

View File

@ -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 }}

View File

@ -0,0 +1,4 @@
# a: v2beta1
# b: v2beta1
# c: v2beta1
# d: v2beta1

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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&rsquo;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>

View File

@ -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.

View File

@ -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()