Skip to content

feat: introduce Helm and Kustomization templates for module management - #33

Merged
iasthc merged 2 commits into
mainfrom
remove-flux
Mar 4, 2026
Merged

feat: introduce Helm and Kustomization templates for module management#33
iasthc merged 2 commits into
mainfrom
remove-flux

Conversation

@iasthc

@iasthc iasthc commented Mar 4, 2026

Copy link
Copy Markdown
Member
  • Added HelmChartTemplate and KustomizationTemplate types to define how modules are deployed via Helm and Kustomize.
  • Updated Module and ModuleTemplate to support HelmChart and Kustomization specifications, ensuring mutual exclusivity.
  • Enhanced ModuleStatus to capture the observed state of Helm releases and Kustomizations.
  • Implemented deepcopy functions for new types to ensure compatibility.
  • Updated CRD definitions to reflect changes in module management and resource tracking.

- Added HelmChartTemplate and KustomizationTemplate types to define how modules are deployed via Helm and Kustomize.
- Updated Module and ModuleTemplate to support HelmChart and Kustomization specifications, ensuring mutual exclusivity.
- Enhanced ModuleStatus to capture the observed state of Helm releases and Kustomizations.
- Implemented deepcopy functions for new types to ensure compatibility.
- Updated CRD definitions to reflect changes in module management and resource tracking.
Copilot AI review requested due to automatic review settings March 4, 2026 05:34
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a fundamental shift in how modules are managed by directly embedding Helm chart and Kustomization specifications into the ModuleTemplate and Module custom resources. This change moves away from abstract FluxCD resource references to native definitions, offering more direct control and detailed status reporting for module deployments. The update streamlines the operator's logic by managing Helm releases and Kustomizations directly, providing a more integrated and observable module lifecycle.

Highlights

  • New API Types for Module Deployment: Introduced HelmChartTemplate and KustomizationTemplate types to define how modules are deployed using Helm charts and Kustomize, respectively.
  • Enhanced Module and ModuleTemplate CRDs: Updated the Module and ModuleTemplate custom resources to directly incorporate these new Helm and Kustomization specifications, ensuring mutual exclusivity between the two deployment methods.
  • Improved Module Status Reporting: The ModuleStatus has been enhanced to capture the observed state of Helm releases and Kustomizations, providing detailed insights into the deployment lifecycle and including an inventory of managed resources.
  • Automatic Deepcopy Generation: Implemented deepcopy functions for all newly introduced types, ensuring proper handling of object copies within the controller.
  • Updated CRD Definitions: The Custom Resource Definitions (CRDs) for Module and ModuleTemplate have been updated to reflect these new fields and their validation rules.
Changelog
  • addons/v1alpha1/helm_types.go
    • Added HelmChartTemplate struct to define Helm chart deployment parameters.
    • Added HelmUpgradeStrategy struct for configuring Helm upgrade behavior.
    • Added HelmReleaseStatus struct to capture the observed state of a Helm release.
  • addons/v1alpha1/kustomization_types.go
    • Added KustomizationTemplate struct for defining Kustomize-based deployments.
    • Added GitReference struct to specify Git repository references.
    • Added KustomizePatch and PatchSelector structs for applying patches.
    • Added KustomizationStatus struct to track the observed state of a Kustomization.
    • Added SecretReference struct for referencing Kubernetes secrets.
    • Added InventoryEntry struct for tracking individual Kubernetes resources.
  • addons/v1alpha1/module_types.go
    • Updated ModuleSpec.Values description to clarify its use for Helm-based modules.
    • Removed the ResourceReference type.
    • Revised ModuleStatus comments for clarity and to reflect direct management.
    • Updated AppliedTemplateGeneration and Namespace descriptions.
    • Replaced HelmReleaseRef with an embedded HelmReleaseStatus object.
    • Replaced KustomizationRef with an embedded KustomizationStatus object.
    • Introduced an Inventory field in ModuleStatus to track managed resources.
    • Updated Conditions description to include "UpgradeAvailable".
    • Added +kubebuilder:storageversion marker to Module.
    • Adjusted the main comment for Module to reflect direct management.
  • addons/v1alpha1/moduletemplate_types.go
    • Updated ModuleTemplateSpec description to reflect direct Helm/Kustomization specifications.
    • Modified the XValidation rule to refer to helmChart instead of helmRelease.
    • Updated Namespace description.
    • Replaced HelmRelease with HelmChart of type HelmChartTemplate.
    • Replaced Kustomization with KustomizationTemplate.
    • Added +kubebuilder:storageversion marker to ModuleTemplate.
    • Adjusted the main comment for ModuleTemplate.
  • addons/v1alpha1/zz_generated.deepcopy.go
    • Added DeepCopyInto and DeepCopy methods for GitReference, HelmChartTemplate, HelmReleaseStatus, HelmUpgradeStrategy, InventoryEntry, KustomizationStatus, KustomizationTemplate, KustomizePatch, PatchSelector, and SecretReference.
    • Modified ModuleStatus.DeepCopyInto to handle the new HelmRelease, Kustomization, and Inventory fields.
    • Modified ModuleTemplateSpec.DeepCopyInto to handle the new HelmChart and Kustomization fields.
    • Removed ResourceReference deepcopy methods.
  • config/crd/bases/addons.otterscale.io_modules.yaml
    • Updated descriptions for Module, ModuleSpec.Values, ModuleStatus.AppliedTemplateGeneration, ModuleStatus.Conditions, and ModuleStatus.Namespace.
    • Replaced helmReleaseRef with the full schema for helmRelease (type HelmReleaseStatus).
    • Replaced kustomizationRef with the full schema for kustomization (type KustomizationStatus).
    • Added the schema for inventory (array of InventoryEntry).
  • config/crd/bases/addons.otterscale.io_moduletemplates.yaml
    • Updated descriptions for ModuleTemplate and ModuleTemplateSpec.Namespace.
    • Updated the x-kubernetes-validations rule to reference helmChart.
    • Replaced the helmRelease field with the full schema for helmChart (type HelmChartTemplate).
    • Updated the kustomization field with the full schema for KustomizationTemplate.
  • go.mod
    • Updated github.com/davecgh/go-spew to v1.1.2-0.20180830191138-d8f796af33cc.
    • Added github.com/kr/pretty, github.com/pmezard/go-difflib, github.com/spf13/pflag, gopkg.in/check.v1.
    • Updated golang.org/x/net and golang.org/x/text.
    • Updated k8s.io/kube-openapi and k8s.io/utils.
    • Updated sigs.k8s.io/structured-merge-diff/v6.
  • go.sum
    • Updated checksums for various dependencies to reflect version changes and additions.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and valuable refactoring by replacing the dependency on FluxCD CRDs with native Helm and Kustomize management within the operator. The introduction of HelmChartTemplate and KustomizationTemplate with strongly-typed schemas is a great improvement over using runtime.RawExtension, enhancing validation and usability. However, there are significant discrepancies in the generated CRD files. Specifically, several validation patterns are missing in the ModuleTemplate CRD, and a field name mismatch in the Module CRD's InventoryEntry will likely break resource tracking and garbage collection, which could lead to security and reliability issues. Additionally, a confusing comment in the HelmChartTemplate regarding version constraints was identified. Addressing these points will ensure the new API is robust, consistent, secure, and reliable.

Comment on lines +222 to +242
ref:
description: |-
Ref specifies the Git reference to check out.
If not set, the default branch (usually main) is used.
properties:
branch:
description: Branch is the Git branch to check out.
type: string
commit:
description: Commit is the Git commit SHA to check out.
pattern: ^[a-f0-9]{7,40}$
type: string
semver:
description: |-
Semver is a semver range expression used to select the latest
matching Git tag.
type: string
tag:
description: Tag is the Git tag to check out.
type: string
type: object

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The GitReference type has a CEL validation to ensure at most one of branch, tag, commit, or semver is set. This validation is missing from the generated CRD for the ref field. This means the API server will not enforce this important constraint. It's possible make manifests was not run after adding the validation rule, or there's a configuration issue with controller-gen.

                   ref:
                    description: |-
                      Ref specifies the Git reference to check out.
                      If not set, the default branch (usually main) is used.
                    properties:
                      branch:
                        description: Branch is the Git branch to check out.
                        type: string
                      commit:
                        description: Commit is the Git commit SHA to check out.
                        pattern: ^[a-f0-9]{7,40}$
                        type: string
                      semver:
                        description: |-
                          Semver is a semver range expression used to select the latest
                          matching Git tag.
                        type: string
                      tag:
                        description: Tag is the Git tag to check out.
                        type: string
                    type: object
                    x-kubernetes-validations:
                    - message: at most one of branch, tag, commit, or semver may be set
                      rule: '[has(self.branch) && self.branch != '''', has(self.tag) && self.tag != '''', has(self.commit) && self.commit != '''', has(self.semver) && self.semver != ''''].filter(x, x).size() <= 1'

Comment on lines +83 to +273
releaseName:
description: |-
ReleaseName overrides the Helm release name.
Defaults to the Module name if not specified.
maxLength: 53
type: string
repoURL:
description: |-
RepoURL is the URL of the Helm chart repository.
Supports HTTP/HTTPS Helm repositories and OCI registries (oci://).
minLength: 1
type: string
secretRef:
description: |-
SecretRef references a Secret in the Module's resolved namespace
containing credentials for the Helm repository.
Supported keys: username, password (Basic Auth), caFile, certFile, keyFile (TLS).
properties:
name:
description: Name is the name of the Secret.
minLength: 1
type: string
required:
- name
type: object
timeout:
description: |-
Timeout is the maximum duration for any single Helm operation.
Defaults to 5m if not specified.
type: string
upgrade:
description: Upgrade configures the Helm upgrade strategy.
properties:
cleanupOnFail:
description: CleanupOnFail rolls back changes on upgrade failure.
type: boolean
enableRollback:
description: EnableRollback triggers an automatic rollback
when an upgrade fails.
type: boolean
force:
description: Force forces resource updates through a replacement
strategy.
type: boolean
maxRetries:
description: |-
MaxRetries is the maximum number of retries before marking the release as failed.
Defaults to 0 (no retries).
minimum: 0
type: integer
type: object
values:
description: |-
Values holds the default Helm chart values as arbitrary JSON.
Module.Spec.Values can override these on a per-instance basis.
type: object
x-kubernetes-preserve-unknown-fields: true
version:
description: |-
Version is the chart version to install, following semver constraints.
If empty, the latest version is used.
type: string
required:
- chart
- interval
- repoURL
type: object
x-kubernetes-preserve-unknown-fields: true
kustomization:
description: |-
Kustomization defines the FluxCD Kustomization spec template.
The actual schema is composed at runtime by the Schema RPC from the FluxCD Kustomization CRD.
Mutually exclusive with HelmRelease (enforced via CEL).
Kustomization defines a Kustomize-based module.
The operator clones the source, builds the kustomization, and applies
the manifests using server-side apply.
Mutually exclusive with HelmChart (enforced via CEL).
properties:
force:
description: |-
Force instructs the operator to recreate resources that have
immutable field changes, instead of failing the apply.
type: boolean
interval:
description: Interval at which the operator re-reconciles this
kustomization.
type: string
patches:
description: |-
Patches is a list of strategic merge or JSON6902 patches to apply
on top of the kustomization output before sending to the cluster.
items:
description: KustomizePatch defines an inline strategic merge
or JSON6902 patch.
properties:
patch:
description: Patch is the inline YAML patch content.
type: string
target:
description: |-
Target selects which resources to apply the patch to.
If not set, the patch is applied to all matching resources.
properties:
annotationSelector:
description: AnnotationSelector filters resources by
annotations (e.g. "app=nginx").
type: string
group:
description: Group is the API group of the target resource.
type: string
kind:
description: Kind is the kind of the target resource.
type: string
labelSelector:
description: LabelSelector filters resources by labels
(e.g. "app=nginx").
type: string
name:
description: Name of the target resource.
type: string
namespace:
description: Namespace of the target resource.
type: string
version:
description: Version is the API version of the target
resource.
type: string
type: object
required:
- patch
type: object
type: array
path:
description: |-
Path is the directory path within the repository where
kustomization.yaml is located. Defaults to the repository root.
type: string
prune:
description: |-
Prune enables garbage collection: resources that were previously
applied but are no longer present in the kustomization output
will be deleted from the cluster.
type: boolean
ref:
description: |-
Ref specifies the Git reference to check out.
If not set, the default branch (usually main) is used.
properties:
branch:
description: Branch is the Git branch to check out.
type: string
commit:
description: Commit is the Git commit SHA to check out.
pattern: ^[a-f0-9]{7,40}$
type: string
semver:
description: |-
Semver is a semver range expression used to select the latest
matching Git tag.
type: string
tag:
description: Tag is the Git tag to check out.
type: string
type: object
secretRef:
description: |-
SecretRef references a Secret containing credentials for the Git
repository. Supported keys: username + password (HTTPS), identity +
identity.pub + known_hosts (SSH).
properties:
name:
description: Name is the name of the Secret.
minLength: 1
type: string
required:
- name
type: object
targetNamespace:
description: |-
TargetNamespace overrides the namespace for all resources in the
kustomization output. If empty, each resource keeps its own namespace.
maxLength: 63
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?)$
type: string
timeout:
description: |-
Timeout is the maximum duration for the build and apply operation.
Defaults to 5m if not specified.
type: string
url:
description: |-
URL is the Git repository URL containing the kustomization.
Supports HTTPS and SSH URLs.
minLength: 1
type: string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The ModuleTemplate CRD is missing validation patterns for several critical fields, including repoURL, releaseName, and url. While these patterns are defined in the Go structs using kubebuilder annotations, they are not reflected in the generated CRD. This allows potentially malicious or malformed input to bypass API-level validation, which could lead to injection vulnerabilities if the operator uses these values in sensitive operations (e.g., shell commands for Helm or Git).

Comment on lines +219 to +225
v:
description: Version is the API version of the resource (e.g.
"v1", "apps/v1").
type: string
required:
- id
- v

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

There is a critical field name mismatch in the InventoryEntry definition within the Module CRD. The Go struct defines the field as Version with a JSON tag of version, but the CRD incorrectly uses the name v. This discrepancy will cause serialization issues and prevent the API version of managed resources from being correctly persisted or retrieved, which will likely break the operator's garbage collection (pruning) logic and could lead to resource leakage (DoS).

                    version:
                      description: Version is the API version of the resource (e.g.
                        "v1", "apps/v1").
                      type: string
                  required:
                  - id
                  - version

Comment on lines +40 to +41
// Version is the exact chart version to install (e.g. "1.2.3").
// If empty, the latest version is used.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment for Version states it must be an "exact chart version", which contradicts the generated CRD which mentions support for "semver constraints". This is confusing for API users. Please clarify if semver constraints are supported and update the comment accordingly for consistency.

Suggested change
// Version is the exact chart version to install (e.g. "1.2.3").
// If empty, the latest version is used.
// Version is the chart version to install, which can be a fixed version
// or a semver constraint (e.g., "~1.2.3", "^1.2.3").
// If empty, the latest version is used.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces first-class Helm chart and Kustomize (Git-based) templates for ModuleTemplate, updates Module/ModuleStatus to track the observed state of the underlying deployment, and updates generated artifacts (CRDs + deepcopy) to match the new API surface.

Changes:

  • Add typed HelmChartTemplate / KustomizationTemplate (and related status + helper types) under addons/v1alpha1.
  • Replace FluxCD HelmRelease/Kustomization raw-extension templates with strongly typed Helm/Kustomize specs and updated status fields (including inventory tracking).
  • Update generated CRDs and deepcopy implementations to reflect the new fields.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
go.mod / go.sum Dependency updates from API + controller-gen regeneration.
config/crd/bases/addons.otterscale.io_moduletemplates.yaml CRD schema updated for helmChart and typed kustomization template fields + CEL exclusivity rule.
config/crd/bases/addons.otterscale.io_modules.yaml CRD schema updated for new status fields (helmRelease, kustomization, inventory) and revised descriptions.
addons/v1alpha1/moduletemplate_types.go API change: helmChart / kustomization are now typed templates and mutually exclusive.
addons/v1alpha1/module_types.go API change: status now stores observed Helm/Kustomize state + inventory (instead of refs).
addons/v1alpha1/helm_types.go New Helm template + status types (repo/chart/version/values/upgrade/etc).
addons/v1alpha1/kustomization_types.go New Kustomization template + status types (git URL/ref/path/patches/etc) + inventory/secret refs.
addons/v1alpha1/zz_generated.deepcopy.go Regenerated deep-copies for the newly introduced types and updated status/spec fields.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

status:
description: Status is the Helm release status (e.g. deployed,
failed, pending-upgrade).
type: string

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

status.helmRelease.status is missing the enum validation in the generated CRD, even though the Go type (HelmReleaseStatus.Status) declares an explicit Enum. Without this, invalid statuses can be persisted and consumers can’t rely on a bounded set of values. Please regenerate the CRD (or update the schema) so the enum is present.

Suggested change
type: string
type: string
enum:
- unknown
- deployed
- superseded
- failed
- uninstalling
- uninstalled
- pending-install
- pending-upgrade
- pending-rollback

Copilot uses AI. Check for mistakes.
description: |-
ReleaseName overrides the Helm release name.
Defaults to the Module name if not specified.
maxLength: 53

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spec.helmChart.releaseName is missing the regex pattern validation in the CRD schema. The Go type declares a DNS-label style pattern, and without it users can submit values that Helm will later reject at runtime. Regenerate the CRDs (or add the pattern) so it matches the Go markers.

Suggested change
maxLength: 53
maxLength: 53
pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'

Copilot uses AI. Check for mistakes.
description: |-
RepoURL is the URL of the Helm chart repository.
Supports HTTP/HTTPS Helm repositories and OCI registries (oci://).
minLength: 1

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spec.helmChart.repoURL is missing the URL scheme pattern validation in the CRD schema (despite the Go type specifying one). This allows invalid repository URLs to be accepted and only fail later during reconciliation. Please regenerate the CRDs (or add the pattern) so it enforces https?:// or oci:// as intended.

Suggested change
minLength: 1
minLength: 1
pattern: ^(https?://|oci://).*

Copilot uses AI. Check for mistakes.
description: |-
URL is the Git repository URL containing the kustomization.
Supports HTTPS and SSH URLs.
minLength: 1

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spec.kustomization.url in the CRD schema is missing the URL pattern validation that the Go type declares. Without it, obviously invalid Git URLs will be admitted and only fail during reconciliation. Regenerate the CRDs (or add the pattern) so the published schema matches the Go API.

Suggested change
minLength: 1
minLength: 1
pattern: ^((https?|ssh|git|file)://|git@[-\w.]+:[\w./-]+)$

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +41
// Version is the exact chart version to install (e.g. "1.2.3").
// If empty, the latest version is used.

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Version field docs say this is an exact chart version, but the generated CRD description for spec.helmChart.version says it supports semver constraints. Please align the API documentation (and any intended validation) so users don’t get conflicting guidance about whether ranges like >=1.2.0 are supported.

Suggested change
// Version is the exact chart version to install (e.g. "1.2.3").
// If empty, the latest version is used.
// Version is the Helm chart version or semantic version constraint to use
// (e.g. "1.2.3" for an exact version, or ">=1.2.0 <2.0.0" for a range).
// If empty, the latest chart version matching the constraint is used.

Copilot uses AI. Check for mistakes.
Comment on lines +219 to +225
v:
description: Version is the API version of the resource (e.g.
"v1", "apps/v1").
type: string
required:
- id
- v

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CRD schema for status.inventory uses field name v (and requires it), but the Go type InventoryEntry is json:"version". This mismatch will cause controllers/clients to write version while the CRD only allows v, breaking validation and/or dropping data. Regenerate the CRDs from the Go types (or rename the JSON tag / schema) so the CRD property is version and the required list matches.

Suggested change
v:
description: Version is the API version of the resource (e.g.
"v1", "apps/v1").
type: string
required:
- id
- v
version:
description: Version is the API version of the resource (e.g.
"v1", "apps/v1").
type: string
required:
- id
- version

Copilot uses AI. Check for mistakes.
- Updated various indirect dependencies in go.mod and go.sum for better compatibility and security.
- Modified validation rules in KustomizationTemplate and related CRDs to enhance clarity and enforce constraints.
- Added new validation patterns for fields in module templates to ensure proper formatting and requirements.
@iasthc
iasthc merged commit e26237f into main Mar 4, 2026
4 checks passed
@iasthc
iasthc deleted the remove-flux branch March 4, 2026 05:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants