Skip to content

Commit 4a6c32a

Browse files
committed
Adding take over of existing crds
1 parent 781d0ab commit 4a6c32a

File tree

16 files changed

+285
-191
lines changed

16 files changed

+285
-191
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ const cm = new certmanager.CertManager("cert-manager-deployment", {
5454
// When both installCRDs and crds.enabled are specified, crds.enabled takes precedence
5555
// installCRDs: true,
5656

57+
// Automatically handle CRD conflicts by importing existing CRDs
58+
// This allows replacing a cert-manager installation without manual deletion
59+
// When true, Helm will adopt existing CRDs by setting proper annotations and ownership
60+
importExistingCRDs: true, // Default is true, set to false to disable
61+
5762
helmOptions: {
5863
namespace: ns.metadata.name,
5964
},
@@ -85,6 +90,11 @@ cm = certmanager.CertManager("cert-manager-deployment",
8590
# When both install_crds and crds.enabled are specified, crds.enabled takes precedence
8691
# install_crds=True,
8792

93+
# Automatically handle CRD conflicts by importing existing CRDs
94+
# This allows replacing a cert-manager installation without manual deletion
95+
# When True, Helm will adopt existing CRDs by setting proper annotations and ownership
96+
import_existing_crds=True, # Default is True, set to False to disable
97+
8898
helm_options={
8999
"namespace": ns.metadata["name"],
90100
})
@@ -130,6 +140,11 @@ func main() {
130140
// When both InstallCRDs and Crds.Enabled are specified, Crds.Enabled takes precedence
131141
// InstallCRDs: pulumi.BoolPtr(enabled),
132142

143+
// Automatically handle CRD conflicts by importing existing CRDs
144+
// This allows replacing a cert-manager installation without manual deletion
145+
// When true, Helm will adopt existing CRDs by setting proper annotations and ownership
146+
ImportExistingCRDs: pulumi.BoolPtr(true), // Default is true, set to false to disable
147+
133148
HelmOptions: &helmv3.ReleaseArgs{
134149
Namespace: ns.Metadata.Name(),
135150
},
@@ -151,4 +166,23 @@ if you need to override them, you may do so using the `helmOptions` parameter. R
151166
[the API docs for the `kubernetes:helm/v3:Release` Pulumi type](
152167
https://www.pulumi.com/docs/reference/pkg/kubernetes/helm/v3/release/#inputs) for a full set of choices.
153168

169+
### Handling CRD Ownership During Upgrades
170+
171+
A common issue when replacing cert-manager installations is conflicts with existing CRDs, which results in errors like:
172+
173+
```
174+
Unable to continue with install: CustomResourceDefinition "certificaterequests.cert-manager.io" in namespace "" exists and cannot be imported into the current release: invalid ownership metadata
175+
```
176+
177+
To address this, the component provides the `importExistingCRDs` option (default: `true`), which:
178+
179+
1. Enables Helm's resource replacement functionality to take ownership of existing CRDs
180+
2. Sets special annotations to help resolve ownership conflicts:
181+
- `helm.sh/resource-policy: keep` to preserve CRDs on uninstall
182+
- `meta.helm.sh/release-name` and `meta.helm.sh/release-namespace` for Helm ownership
183+
- `kubectl.kubernetes.io/last-applied-configuration` for tracking changes
184+
3. Configures `keep: true` for CRDs to ensure they persist between installations
185+
186+
This approach allows you to replace cert-manager installations without manually deleting the CRDs first, preserving any resources that depend on those CRDs (like Certificate, ClusterIssuer, etc.). To disable this behavior, set `importExistingCRDs: false`.
187+
154188
For complete details, refer to the Pulumi Package details within the Pulumi Registry.

examples/crds-tests-ts/Pulumi.yaml

Lines changed: 0 additions & 3 deletions
This file was deleted.

examples/crds-tests-ts/README.md

Lines changed: 0 additions & 36 deletions
This file was deleted.

examples/crds-tests-ts/index.ts

Lines changed: 0 additions & 105 deletions
This file was deleted.

examples/crds-tests-ts/package.json

Lines changed: 0 additions & 12 deletions
This file was deleted.

examples/crds-tests-ts/tsconfig.json

Lines changed: 0 additions & 19 deletions
This file was deleted.

examples/examples_ts_test.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@ func TestTsExamples(t *testing.T) {
1818
directoryName string
1919
additionalConfig map[string]string
2020
}{
21-
"TestSimpleCertManagerTs": {directoryName: "simple-cert-manager-ts"},
22-
"TestCrdsTs": {directoryName: "crds-tests-ts"},
23-
"TestCrdsPrecedenceTs": {directoryName: "crds-precedence-test-ts"},
21+
"TestSimpleCertManagerTs": {directoryName: "simple-cert-manager-ts"},
22+
"TestCrdsPrecedenceTs": {directoryName: "crds-precedence-test-ts"},
2423
}
2524
for name, test := range tests {
2625
t.Run(name, func(t *testing.T) {
@@ -51,3 +50,25 @@ func TestTsCertManagerPreview(t *testing.T) {
5150
p.Preview(t)
5251
})
5352
}
53+
54+
// TestTsDestroyRecreate tests that resources can be created, destroyed, and recreated successfully
55+
// to address https://github.com/pulumi/pulumi-kubernetes-cert-manager/issues/408
56+
func TestTsDestroyRecreate(t *testing.T) {
57+
t.Run("TestSimpleCertManagerDestroyRecreate", func(t *testing.T) {
58+
p := pulumitest.NewPulumiTest(t, "simple-cert-manager-ts",
59+
opttest.LocalProviderPath("pulumi-kubernetes-cert-manager", filepath.Join(getCwd(t), "..", "bin")),
60+
opttest.YarnLink("@pulumi/kubernetes-cert-manager"),
61+
)
62+
p.SetConfig(t, "repository", "public.ecr.aws/eks-anywhere-dev/cert-manager/cert-manager-controller")
63+
64+
// Initial deployment
65+
p.Up(t)
66+
67+
// Destroy the resources
68+
p.Destroy(t)
69+
70+
// Recreate the resources
71+
p.Install(t)
72+
p.Up(t)
73+
})
74+
}

provider/cmd/pulumi-resource-kubernetes-cert-manager/schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@
9292
"type": "boolean",
9393
"description": "⚠️ Deprecated: Use crds.enabled instead. When both installCRDs and crds.enabled are specified, crds.enabled takes precedence."
9494
},
95+
"importExistingCRDs": {
96+
"type": "boolean",
97+
"description": "When true (default), automatically import existing CRDs instead of failing on conflict",
98+
"default": true
99+
},
95100
"crds": {
96101
"$ref": "#/types/kubernetes-cert-manager:index:CertManagerCrds",
97102
"description": "Control CRDs installation and lifecycle"

provider/pkg/provider/chart.go

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,16 @@ func (c *CertManager) DefaultRepoURL() string { return "https
3636

3737
// CertManager contains the set of arguments for creating a CertManager component resource.
3838
type CertManagerArgs struct {
39-
Global kcm.CertManagerGlobalPtrInput `pulumi:"global"`
40-
// Deprecated: Use crds.enabled instead.
39+
Global kcm.CertManagerGlobalPtrInput `pulumi:"global"`
40+
// Deprecated: Use crds.enabled instead.
4141
// When both installCRDs and crds.enabled are specified, crds.enabled takes precedence.
42-
InstallCRDs *bool `pulumi:"installCRDs"`
43-
Crds kcm.CertManagerCrdsPtrInput `pulumi:"crds"`
44-
ReplicaCount *int `pulumi:"replicaCount"`
45-
Strategy *appsv1.DeploymentStrategy `pulumi:"strategy" pschema:"ref=/kubernetes/v4.21.0/schema.json#/types/kubernetes:apps/v1:DeploymentStrategy"`
42+
InstallCRDs *bool `pulumi:"installCRDs"`
43+
// Controls whether to import existing CRDs during installation
44+
// When true (default), Helm will adopt existing CRDs instead of failing on conflict
45+
ImportExistingCRDs *bool `pulumi:"importExistingCRDs"`
46+
Crds kcm.CertManagerCrdsPtrInput `pulumi:"crds"`
47+
ReplicaCount *int `pulumi:"replicaCount"`
48+
Strategy *appsv1.DeploymentStrategy `pulumi:"strategy" pschema:"ref=/kubernetes/v4.21.0/schema.json#/types/kubernetes:apps/v1:DeploymentStrategy"`
4649
// Comma separated list of feature gates that should be enabled on the controller pod.
4750
FeatureGates *string `pulumi:"featureGates"`
4851
Image kcm.CertManagerImagePtrInput `pulumi:"image"`
@@ -95,7 +98,63 @@ type CertManagerArgs struct {
9598
HelmOptions *helmbase.ReleaseType `pulumi:"helmOptions" pschema:"ref=#/types/chart-cert-manager:index:Release" json:"-"`
9699
}
97100

98-
func (args *CertManagerArgs) R() **helmbase.ReleaseType { return &args.HelmOptions }
101+
func (args *CertManagerArgs) R() **helmbase.ReleaseType {
102+
// Initialize HelmOptions if needed
103+
if args.HelmOptions == nil {
104+
args.HelmOptions = &helmbase.ReleaseType{}
105+
}
106+
107+
// Initialize Values if needed
108+
if args.HelmOptions.Values == nil {
109+
args.HelmOptions.Values = make(map[string]interface{})
110+
}
111+
112+
// Note: We can't directly access the Enabled field from CertManagerCrdsPtrInput type
113+
// Instead, we rely on the value provided in the ImportExistingCRDs field directly
114+
115+
// If ImportExistingCRDs is enabled (default: true) and CRDs are used,
116+
// configure Helm options for proper CRD handling
117+
if args.ImportExistingCRDs == nil || *args.ImportExistingCRDs {
118+
// Enable the 'replace' flag in Helm which allows taking ownership of existing resources
119+
// This is key for importing existing CRDs
120+
args.HelmOptions.Replace = pulumi.Bool(true)
121+
122+
// Configure CRDs to have the resource-policy=keep annotation which preserves
123+
// them if the release is uninstalled
124+
if _, ok := args.HelmOptions.Values["crds"]; !ok {
125+
args.HelmOptions.Values["crds"] = make(map[string]interface{})
126+
}
127+
128+
crdsMap, ok := args.HelmOptions.Values["crds"].(map[string]interface{})
129+
if !ok {
130+
crdsMap = make(map[string]interface{})
131+
args.HelmOptions.Values["crds"] = crdsMap
132+
}
133+
134+
// Use skipCrds=false to ensure CRDs are installed
135+
// and set advanced options for CRD adoption
136+
137+
// Don't skip CRDs - ensure they're installed
138+
args.HelmOptions.SkipCrds = pulumi.Bool(false)
139+
140+
// These options help with adopting existing CRDs
141+
args.HelmOptions.CleanupOnFail = pulumi.Bool(true)
142+
args.HelmOptions.ForceUpdate = pulumi.Bool(true)
143+
args.HelmOptions.Replace = pulumi.Bool(true)
144+
145+
// Set resource policy annotation to preserve CRDs on uninstall
146+
crdAnnotations := map[string]string{
147+
"helm.sh/resource-policy": "keep",
148+
}
149+
150+
crdsMap["annotations"] = crdAnnotations
151+
152+
// Set keep to true to preserve CRDs on uninstall
153+
crdsMap["keep"] = true
154+
}
155+
156+
return &args.HelmOptions
157+
}
99158

100159
type CertManagerGlobal struct {
101160
// Reference to one or more secrets to be used when pulling images.

0 commit comments

Comments
 (0)