Skip to content

Commit 7be4033

Browse files
committed
Adding take over of existing crds
1 parent 781d0ab commit 7be4033

File tree

21 files changed

+434
-200
lines changed

21 files changed

+434
-200
lines changed

CLAUDE.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
- Build provider: `make build_provider`
8+
- Generate all SDKs: `make generate`
9+
- Build all SDKs: `make build`
10+
- Run Go tests: `go test ./...`
11+
- Run specific Go test: `go test ./examples -run=TestSimpleCertManagerGo`
12+
- Ensure dependencies consistency: `make ensure`
13+
- Format Go code: `go fmt ./...`
14+
- Lint Go code: `golangci-lint run`
15+
- Generate code for SDK after schema changes: `make codegen && make generate`
16+
17+
## Code Style Guidelines
18+
19+
- Go code follows standard Go formatting conventions (`go fmt`)
20+
- Copyright header required on all source files
21+
- Use descriptive variable names following Go's camelCase convention
22+
- Proper error handling with propagation or logging
23+
- Imports should be grouped by standard lib, external, then internal packages
24+
- Follow existing patterns in the code for component implementations
25+
- For TypeScript code: Use types everywhere, follow Pulumi naming conventions
26+
- Maintain consistent structure between language SDKs (Go, .NET, Python, TypeScript)
27+
- Write comprehensive test cases for any added functionality
28+
29+
## Development Notes
30+
31+
- When modifying schema.json or provider code, use `make codegen` to generate SDK code
32+
- Do not manually edit files in the sdk/ directory as they are auto-generated
33+
- Updates to schema.json will propagate to all language SDKs through codegen
34+
- Provider implementation is in provider/pkg/provider/ directory
35+
- CRD handling is managed through the cert-manager Helm chart values and Helm options:
36+
- The `importExistingCRDs` parameter (default: true) controls CRD adoption behavior
37+
- Helm options like `Replace`, `ForceUpdate`, and `CleanupOnFail` manage CRD conflicts
38+
- CRD annotations should use simple values, not Helm template variables like `{{.Release.Name}}`
39+
- Test CRD adoption functionality with `TestTsDestroyRecreate` test
40+
- Never commit changes unless explicitly instructed to do so

README.md

Lines changed: 40 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,29 @@ 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+
This provider addresses this issue through dynamic CRD discovery and Pulumi's resource import capabilities:
178+
179+
1. **Dynamic CRD Finding and Adoption**: Following the same pattern as Pulumi's Kubernetes SDK, the provider:
180+
- Lists all CustomResourceDefinitions in the cluster
181+
- Filters the results to identify cert-manager CRDs (by label or known patterns)
182+
- Imports each identified CRD into Pulumi's state management
183+
- Removes problematic Helm ownership annotations, allowing for clean installation
184+
185+
2. **Helm Configuration**: Through the `importExistingCRDs` option (default: `true`), the provider also:
186+
- Enables Helm's resource replacement functionality to take ownership of existing CRDs
187+
- Sets special annotations to help resolve ownership conflicts:
188+
- `helm.sh/resource-policy: keep` to preserve CRDs on uninstall
189+
- `meta.helm.sh/release-name` and `meta.helm.sh/release-namespace` for Helm ownership
190+
- Configures `keep: true` for CRDs to ensure they persist between installations
191+
192+
This approach dynamically discovers and adopts any cert-manager CRDs that already exist in your cluster, without needing manual intervention. It's based on the same patterns used in the Pulumi Kubernetes SDK v4.22.2 for handling existing CRDs.
193+
154194
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+
}

examples/simple-cert-manager-ts/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
},
66
"dependencies": {
77
"@pulumi/kubernetes": "4.22.2",
8+
"@pulumi/kubernetes-cert-manager": "^0.2.0",
89
"@pulumi/pulumi": "3.163.0",
9-
"@pulumi/kubernetes-cert-manager": "latest",
1010
"@pulumi/random": "4.18.0",
1111
"google-protobuf": "3.21.4"
1212
}
13-
}
13+
}

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"

0 commit comments

Comments
 (0)