Skip to content

Feat/webhook workload monitors - #1448

Merged
Andrei Kvapil (kvaps) merged 3 commits into
mainfrom
feat/webhook-workload-monitors
Sep 24, 2025
Merged

Andrei Kvapil (kvaps) merged 3 commits into
mainfrom
feat/webhook-workload-monitors

Conversation

@lllamnyp

@lllamnyp Timofei Larkin (lllamnyp) commented Sep 23, 2025 •

Copy link
Copy Markdown
Member

What this PR does

Many resources created as part of managed apps in cozystack (pods, secrets, etc) do not carry predictable labels that unambiguously indicate which app originally triggered their creation. Some resources are managed by controllers and other custom resources and this indirection can lead to loss of information. Other controllers sometimes simply do not allow setting labels on controlled resources and the latter do not inherit labels from the owner. This patch implements a webhook that sidesteps this problem with a universal solution. On creation of a pod/secret/PVC etc it walks through the owner references until a HelmRelease is found that can be matched with a managed app dynamically registered in the Cozystack API server. The pod is mutated with labels identifying the managed app. This resubmission of the PR now includes semantics to compare secrets to label selectors in CozystackResourceDefinitions to determine, whether they should be marked as user-facing or not.

Release note

[cozystack-controller] Add a mutating webhook to identify the Cozystack
managed app that ultimately owns low-level resources created in the
cluster and label these resources with a reference to said app.

Summary by CodeRabbit

  • New Features

    • Added lineage mutating webhook with service, certificates, and deployment integration to auto-label resources.
    • Introduced secrets include/exclude selectors in resource definitions to control tenant-visible secrets.
  • Bug Fixes

    • Fixed a Kubernetes standard label key typo in the Postgres chart.
  • Chores

    • Updated numerous images and values to latest tags (controller, API, dashboard, Cilium, KubeOVN, Kamaji, Metallb, SeaweedFS, Kubernetes add-ons, databases, and utilities).
    • Added webhook-related manifests (MutatingWebhookConfiguration, cert-manager resources).

@coderabbitai

coderabbitai Bot commented Sep 23, 2025 •

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Introduces a new LineageControllerWebhook with controller and mutating webhook setup, lineage traversal utilities, and secret classification via CRD spec.secrets. Adds webhook manifests (service, certificates, deployment changes, configuration). Extends deepcopy for secrets. Updates multiple image tags to latest and fixes a label typo.

Changes

Cohort / File(s) Summary
API deepcopy updates
api/v1alpha1/zz_generated.deepcopy.go
Adds DeepCopy methods for CozystackResourceDefinitionSecrets; integrates deep copy into Spec; imports metav1.
Lineage webhook package
internal/lineagecontrollerwebhook/types.go, internal/lineagecontrollerwebhook/controller.go, internal/lineagecontrollerwebhook/webhook.go, internal/lineagecontrollerwebhook/config.go, internal/lineagecontrollerwebhook/matcher.go
New webhook/controller type; controller builds in-memory config from CRDs; webhook handles admission, resolves lineage, applies labels; adds mapping, matching helpers, decoder injection, dynamic client/RESTMapper setup, and errors.
Controller entrypoint registration
cmd/cozystack-controller/main.go
Registers LineageControllerWebhook as both controller and webhook with manager.
Lineage utilities
pkg/lineage/lineage.go, pkg/lineage/lineage_test.go, pkg/lineage/mapper.go
Adds ownership graph walker, ObjectID type, HelmRelease constants, AppMapper interface with stub mapper, and a test that exercises traversal.
CRD and API templates for secrets
packages/system/cozystack-controller/templates/crds/cozystack.io_cozystackresourcedefinitions.yaml, packages/system/cozystack-api/templates/cozystack-resource-definitions.yaml
Adds spec.secrets with include/exclude label selectors to CRD; populates secrets blocks in resource definition templates.
Webhook deployment manifests
packages/system/cozystack-controller/templates/service.yaml, .../mutatingwebhookconfiguration.yaml, .../certmanager.yaml, .../deployment.yaml
Adds Service for 9443, MutatingWebhookConfiguration for /mutate-lineage, cert-manager Issuers/Certificates, mounts webhook certs, exposes webhook port.
Image/tag bumps (tag files)
packages/apps/*/images/*.tag, packages/system/*/images/*.tag
Switches multiple image references to :latest with new digests (clickhouse-backup, nginx-cache, cluster-autoscaler, kubevirt-* drivers, ubuntu-container-disk, mariadb-backup, matchbox, grafana, objectstorage-sidecar, s3manager).
Image/tag bumps (values/config)
packages/core/installer/values.yaml, packages/core/testing/values.yaml, packages/system/cozystack-controller/values.yaml, packages/system/dashboard/values.yaml, packages/system/dashboard/charts/kubeapps/templates/dashboard/configmap.yaml, packages/system/kamaji/values.yaml, packages/system/kubeovn-plunger/values.yaml, packages/system/kubeovn-webhook/values.yaml, packages/system/kubeovn/values.yaml, packages/system/metallb/values.yaml, packages/system/objectstorage-controller/values.yaml, packages/system/seaweedfs/values.yaml, packages/system/cilium/values.yaml, packages/system/cozystack-api/values.yaml
Updates images/tags/digests and version strings to latest; adjusts related args where present; no structural changes.
Postgres label key fix
packages/apps/postgres/templates/db.yaml
Corrects label key from app.kubernets.io/instance to app.kubernetes.io/instance.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant K8s API Server as API Server
  participant Webhook as LineageControllerWebhook
  participant Dyn as Dynamic Client
  participant Mapper as RESTMapper
  participant Lineage as pkg/lineage

  User->>API Server: CREATE/UPDATE (Pod/Secret/Service/PVC/VMCluster)
  API Server->>Webhook: AdmissionReview (/mutate-lineage)
  Webhook->>Webhook: decodeUnstructured(req)
  Webhook->>Dyn: Fetch object/owners via computeLabels
  Webhook->>Mapper: Resolve GVKs
  Webhook->>Lineage: WalkOwnershipGraph(obj, appMapper)
  alt No ancestors
    Webhook-->>API Server: allow (no-op patch), warning
  else Ambiguous ancestry
    Webhook-->>API Server: allow (no-op patch), warning
  else Resolved
    Webhook->>Webhook: applyLabels (incl. VMCluster special path)
    Webhook-->>API Server: PatchResponse (labels added)
  end
Loading
sequenceDiagram
  autonumber
  participant Manager
  participant Controller as LineageControllerWebhook (Controller)
  participant API as K8s API
  participant Store as runtimeConfig (atomic.Value)

  Manager->>Controller: SetupWithManagerAsController()
  loop On CRD changes
    Controller->>API: List CozystackResourceDefinitions (cozy-system)
    Controller->>Controller: Build chartAppMap, appCRDMap (dedupe)
    Controller->>Store: Store config (atomic.Value)
  end

  Manager->>Controller: SetupWithManagerAsWebhook()
  Controller->>Manager: Register /mutate-lineage
  note right of Controller: Uses stored config for HelmRelease→App mapping
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • klinch0
  • kvaps

Poem

In burrows of code I thump with delight,
A webhook awakens to label the night.
Lineage traced through roots and leaves,
Secrets sifted by tidy sieves.
Images hop to “latest” with grace—
Bravo, dear stack, on this swift embrace! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Feat/webhook workload monitors" is related to the PR’s primary change (adding a webhook that inspects and annotates workload resources) and therefore conveys the main topic, but it is terse and uses a slash plus the ambiguous term "workload monitors," which reduces clarity about the exact behavior being added.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00dd909 and 4620f7d.

📒 Files selected for processing (43)
  • api/v1alpha1/zz_generated.deepcopy.go (2 hunks)
  • cmd/cozystack-controller/main.go (2 hunks)
  • internal/lineagecontrollerwebhook/config.go (1 hunks)
  • internal/lineagecontrollerwebhook/controller.go (1 hunks)
  • internal/lineagecontrollerwebhook/matcher.go (1 hunks)
  • internal/lineagecontrollerwebhook/types.go (1 hunks)
  • internal/lineagecontrollerwebhook/webhook.go (1 hunks)
  • packages/apps/clickhouse/images/clickhouse-backup.tag (1 hunks)
  • packages/apps/http-cache/images/nginx-cache.tag (1 hunks)
  • packages/apps/kubernetes/images/cluster-autoscaler.tag (1 hunks)
  • packages/apps/kubernetes/images/kubevirt-cloud-provider.tag (1 hunks)
  • packages/apps/kubernetes/images/kubevirt-csi-driver.tag (1 hunks)
  • packages/apps/kubernetes/images/ubuntu-container-disk.tag (1 hunks)
  • packages/apps/mysql/images/mariadb-backup.tag (1 hunks)
  • packages/apps/postgres/templates/db.yaml (2 hunks)
  • packages/core/installer/values.yaml (1 hunks)
  • packages/core/testing/values.yaml (1 hunks)
  • packages/extra/bootbox/images/matchbox.tag (1 hunks)
  • packages/extra/monitoring/images/grafana.tag (1 hunks)
  • packages/extra/seaweedfs/images/objectstorage-sidecar.tag (1 hunks)
  • packages/system/bucket/images/s3manager.tag (1 hunks)
  • packages/system/cilium/values.yaml (1 hunks)
  • packages/system/cozystack-api/templates/cozystack-resource-definitions.yaml (23 hunks)
  • packages/system/cozystack-api/values.yaml (1 hunks)
  • packages/system/cozystack-controller/templates/certmanager.yaml (1 hunks)
  • packages/system/cozystack-controller/templates/crds/cozystack.io_cozystackresourcedefinitions.yaml (1 hunks)
  • packages/system/cozystack-controller/templates/deployment.yaml (1 hunks)
  • packages/system/cozystack-controller/templates/mutatingwebhookconfiguration.yaml (1 hunks)
  • packages/system/cozystack-controller/templates/service.yaml (1 hunks)
  • packages/system/cozystack-controller/values.yaml (1 hunks)
  • packages/system/dashboard/charts/kubeapps/templates/dashboard/configmap.yaml (1 hunks)
  • packages/system/dashboard/values.yaml (2 hunks)
  • packages/system/kamaji/values.yaml (2 hunks)
  • packages/system/kubeovn-plunger/values.yaml (1 hunks)
  • packages/system/kubeovn-webhook/values.yaml (1 hunks)
  • packages/system/kubeovn/values.yaml (1 hunks)
  • packages/system/kubevirt-csi-node/values.yaml (1 hunks)
  • packages/system/metallb/values.yaml (1 hunks)
  • packages/system/objectstorage-controller/values.yaml (1 hunks)
  • packages/system/seaweedfs/values.yaml (1 hunks)
  • pkg/lineage/lineage.go (1 hunks)
  • pkg/lineage/lineage_test.go (1 hunks)
  • pkg/lineage/mapper.go (1 hunks)

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello Timofei Larkin (@lllamnyp), 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 significant new feature to the Cozystack controller: a mutating admission webhook designed to ensure proper lineage tracking for Kubernetes resources. Many resources created by managed applications often lack clear labels linking them back to their parent app, especially when intermediate controllers are involved. This webhook addresses this by inspecting the ownership chain of newly created or updated resources (like pods, secrets, and PVCs) and injecting labels that identify the ultimate Cozystack managed application. Furthermore, it extends the CozystackResourceDefinition API to allow for flexible classification of secrets as "user-facing" or not, using label selectors, providing finer-grained control over secret visibility.

Highlights

  • Resource Lineage Webhook: Implemented a mutating admission webhook that automatically labels Kubernetes resources (Pods, Secrets, Services, PVCs, VMClusters) with their originating Cozystack managed application.
  • Ownership Graph Traversal: The webhook identifies the owning application by traversing the Kubernetes owner reference graph, ultimately linking resources back to a HelmRelease and then to a CozystackResourceDefinition.
  • Secret Classification: Enhanced CozystackResourceDefinition with Secrets selectors (include and exclude) to dynamically mark secrets as user-facing (internal.cozystack.io/tenantsecret) based on their labels.
  • Webhook Infrastructure: Added necessary cert-manager resources (Issuers, Certificates) and a MutatingWebhookConfiguration to enable and secure the new webhook.
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

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 (@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 valuable feature: a mutating webhook to label resources with their owning Cozystack application, which will greatly improve resource tracking and visibility. The implementation, which includes a new controller for CozystackResourceDefinition and the webhook logic, is generally well-structured. However, I've identified a critical issue that could lead to a panic in the webhook, potentially blocking resource creations and updates. I've also found a few high-severity issues, including a broken test and some brittle code that could also panic under certain conditions. Additionally, there are several medium-severity suggestions aimed at improving code quality, robustness, and maintainability. Please review the detailed comments for each point.

Comment on lines +136 to +139
crd := cfg.appCRDMap[appRef{gv.Group, obj.GetKind()}]
if matchLabelsToExcludeInclude(o.GetLabels(), crd.Spec.Secrets.Exclude, crd.Spec.Secrets.Include) {
labels["internal.cozystack.io/tenantsecret"] = ""
}

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.

critical

There's a potential for a nil pointer dereference here. If cfg.appCRDMap does not contain the appRef key, crd will be nil. The subsequent access to crd.Spec on the next line will cause the webhook to panic. This is a critical issue as it could block the creation or update of any resource the webhook is configured to watch. You should add a check to ensure crd is not nil before attempting to access its fields.

if crd, ok := cfg.appCRDMap[appRef{gv.Group, obj.GetKind()}]; ok {
	if matchLabelsToExcludeInclude(o.GetLabels(), crd.Spec.Secrets.Exclude, crd.Spec.Secrets.Include) {
		labels["internal.cozystack.io/tenantsecret"] = ""
	}
}

if err != nil {
t.Fatal(err)
}
nodes := WalkOwnershipGraph(ctx, dynClient, mapper, obj)

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.

high

This test will not compile because the call to WalkOwnershipGraph is missing the required appMapper argument. Additionally, this test relies on command-line arguments (os.Args) to specify the resource to test against, which makes it a manual test rather than an automated unit test. It should be updated to be a proper, self-contained unit test using mock objects or a test environment.

Suggested change
nodes := WalkOwnershipGraph(ctx, dynClient, mapper, obj)
nodes := WalkOwnershipGraph(ctx, dynClient, mapper, &stubMapper{}, obj)

Comment thread pkg/lineage/mapper.go
Comment on lines +47 to +48
split := strings.Split(val, "/")
return strings.Join(split[:2], "/"), split[2], split[3], nil

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.

high

The string splitting logic here is brittle. If a value in stubMapperMap does not contain at least three / characters, strings.Split will produce a slice with fewer than four elements, and accessing split[2] or split[3] will cause a panic. You should add a check to validate the length of the split slice before accessing its elements to prevent this.

split := strings.Split(val, "/")
if len(split) < 4 {
	return "", "", "", fmt.Errorf("invalid format in stubMapperMap for key %s: %s", hr.Spec.Chart.Spec.SourceRef.Name+"/"+hr.Spec.Chart.Spec.Chart, val)
}
return strings.Join(split[:2], "/"), split[2], split[3], nil

Comment on lines +11 to +13
if err != nil {
return false
}

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 error from metav1.LabelSelectorAsSelector is silently ignored, which can make debugging invalid LabelSelectors in CozystackResourceDefinition resources difficult. As the TODO on line 9 suggests, this error should be handled, preferably by logging it. This would likely require passing a logger (or a context from which a logger can be retrieved) into this function and its callers (matchLabelsToSelectorArray and matchLabelsToExcludeInclude).

Comment on lines +123 to +127
s = s[:63]
for b := s[62]; !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')); s = s[:len(s)-1] {
b = s[len(s)-1]
}
return s

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 logic in this for loop to truncate the string and ensure it ends with an alphanumeric character is a bit convoluted and hard to follow. A simpler and more idiomatic Go implementation would improve readability and maintainability.

s = s[:63]
for len(s) > 0 {
	b := s[len(s)-1]
	if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') {
		break
	}
	s = s[:len(s)-1]
}
return s

Comment on lines 1 to +5
cozystackController:
image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.36.1@sha256:6a6144430bdec901b4046840a484f0d9effc570f4f7cef2e5740332b98e3077a
image: ghcr.io/cozystack/cozystack/cozystack-controller:latest@sha256:d01d35592a95ca2425a43fd1d2b64143838722b8d10c882d651dbe5602b68ce1
debug: false
disableTelemetry: false
cozystackVersion: "v0.36.1"
cozystackVersion: "latest"

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

Using the latest tag for both the container image and the cozystackVersion is not recommended for production or even development environments, as it makes deployments non-deterministic and harder to reproduce or roll back. It's a best practice to pin to a specific, immutable version tag (e.g., v0.37.0) and its corresponding SHA256 digest.

cozystackController:
  image: ghcr.io/cozystack/cozystack/cozystack-controller:vX.Y.Z@sha256:d01d35592a95ca2425a43fd1d2b64143838722b8d10c882d651dbe5602b68ce1
  debug: false
  disableTelemetry: false
  cozystackVersion: "vX.Y.Z"

Base automatically changed from feat/cozyrd-secret-selectors to main September 23, 2025 16:29

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

Actionable comments posted: 11

🧹 Nitpick comments (34)
packages/extra/monitoring/images/grafana.tag (1)

1-1: Avoid using the latest tag; prefer explicit version tag or digest-only

Replace the reference in packages/extra/monitoring/images/grafana.tag (uses :latest with a pinned digest) with a digest-only or explicit version reference:

-ghcr.io/cozystack/cozystack/grafana:latest@sha256:cc1843297f2dadb1740ebc4dfdc3dbe5b4ac840d1da37ed6fe93549d3000196c
+ghcr.io/cozystack/cozystack/grafana@sha256:cc1843297f2dadb1740ebc4dfdc3dbe5b4ac840d1da37ed6fe93549d3000196c
packages/system/cozystack-api/values.yaml (1)

2-2: Avoid :latest tag; keep digest-only to prevent AlwaysPull and ambiguous rollouts.

Using :latest forces imagePullPolicy: Always by default and can surprise rollouts/rate‑limit pulls. You already pin by digest—drop the tag for reproducible, predictable behavior.

-  image: ghcr.io/cozystack/cozystack/cozystack-api:latest@sha256:be6aca4df4b769538454d6c65b7045b2bef81b2a15d70eeddbfdf55d6bd17d6c
+  image: ghcr.io/cozystack/cozystack/cozystack-api@sha256:be6aca4df4b769538454d6c65b7045b2bef81b2a15d70eeddbfdf55d6bd17d6c
packages/system/objectstorage-controller/values.yaml (1)

3-3: Avoid ":latest" tags in production charts; keep semver tag + digest for traceability.

"latest" obscures the running version in labels/telemetry. Prefer a specific version tag pinned by digest.

Confirm the digest corresponds to the intended controller release, and consider restoring a versioned tag.

packages/apps/clickhouse/images/clickhouse-backup.tag (1)

1-1: Use versioned tag rather than "latest" for backup tooling images.

Backups/restores are sensitive to version drift. Keep a semver tag with the digest for deterministic rollbacks and audits.

packages/system/seaweedfs/values.yaml (1)

121-121: Sidecar image should use explicit versioned tag + digest, not "latest".

Prevents ambiguous versioning in labels and improves SBOM/compliance tracking.

packages/system/cilium/values.yaml (1)

17-18: Cilium image tag set to "latest" while digest is pinned — prefer semver tag for clarity and compatibility checks.

Many components/readiness checks and labels surface the tag; "latest" can mask real version, hindering debugging and drift detection. Keep a concrete version tag with the digest.

Make sure chart/cluster features configured here are compatible with the actual digest’s Cilium version.

packages/core/installer/values.yaml (1)

2-2: Installer image should remain on a versioned tag for reproducibility.

Switching to "latest" reduces ability to bisect/rollback installs reliably. Recommend semver tag + digest.

packages/system/dashboard/charts/kubeapps/templates/dashboard/configmap.yaml (1)

79-79: Don’t hardcode appVersion to "latest" in user‑facing config; derive from chart/image.

This degrades UX and debug value. Use the chart/image version templating already available in this chart.

Apply this change to keep it consistent with labels:

-      "appVersion": "latest",
+      "appVersion": {{ include "common.images.version" ( dict "imageRoot" .Values.dashboard.image "chart" .Chart ) | toJson }},
packages/system/kubevirt-csi-node/values.yaml (1)

3-3: Pin kubevirt-csi-driver to a specific version tag with digest; avoid "latest".

CSI components are critical; explicit versions help with compatibility and incident response.

packages/system/kubeovn/values.yaml (1)

67-67: Replace 'latest' tags (even with digest) with semver tags + digest

Using latest@sha256:... pins content but keeps the "latest" tag in labels/tooling — use a versioned tag (e.g., vX.Y.Z@sha256:0891335d...) for provenance, rollbacks and SBOMs.
Change packages/system/kubeovn/values.yaml (line 67)
tag: latest@sha256:0891335d3938dc604b47ae310f9bea815c352c15bea34dee111526c6aa6a48de
to a semver tag with the same digest; repo-wide grep returned many other latest occurrences — update them or gate latest behind a dev overlay.

packages/apps/http-cache/images/nginx-cache.tag (1)

1-1: Avoid using the "latest" tag even with a pinned digest for traceability and pull behavior.

  • "latest" defaults imagePullPolicy to Always, which can cause unnecessary pulls even though the digest is immutable.
  • Prefer an explicit version tag matching the digest, or use repo@sha256 without a tag if supported by your tooling.

Can you confirm the digest corresponds to the intended release (and that you want Always pulls here)?

packages/core/testing/values.yaml (1)

2-2: e2e image set to "latest": confirm intent and stability.

Pinned digest ensures reproducibility, but "latest" will still default pullPolicy to Always. For CI stability and speed, consider an explicit version tag or explicitly setting pullPolicy in the chart.

Is Always pull desired in your CI runners for this image?

packages/extra/seaweedfs/images/objectstorage-sidecar.tag (1)

1-1: Prefer versioned tag or digest-only to avoid "latest".

  • Keeps provenance clear in audits and release notes.
  • Avoids implicit Always pulls from using "latest".

Please confirm the digest maps to the target release.

packages/extra/bootbox/images/matchbox.tag (1)

1-1: Use an explicit version tag (or digest-only) rather than "latest".

Improves auditability and avoids implicit Always pulls.

Confirm the digest is the intended version.

packages/apps/kubernetes/images/ubuntu-container-disk.tag (1)

1-1: For VM base images, avoid "latest" to prevent unexpected pull behavior and ease provenance.

  • Prefer a semver tag aligned with the digest or digest-only.
  • If consumers run this with "latest", Kubernetes will default to Always pulling unless overridden.

Is the consuming chart setting pullPolicy explicitly for this image?

packages/system/dashboard/values.yaml (1)

51-52: kubeapps-apis: avoid latest — set pullPolicy or use a versioned tag.

packages/system/dashboard/values.yaml sets kubeappsapis.image.tag: latest (digest present). Prefer a fixed version tag or add an explicit pullPolicy so imagePullPolicy is deterministic (templates read .Values.kubeappsapis.image.pullPolicy).

Suggested change:
image:
registry: ghcr.io/cozystack/cozystack
repository: kubeapps-apis
tag: latest
digest: "sha256:7f6a822c80dd84ee77276183b59ffe073ececff056bddd71e686b2d542f6fde8"
pullPolicy: IfNotPresent

cmd/cozystack-controller/main.go (1)

218-231: Registering the webhook + controller: LGTM; add explicit webhook server settings and a success log.

  • Minor: log successful setup to ease troubleshooting.
  • Recommended: set explicit Port and CertDir for the webhook server (defaults work but being explicit avoids chart/template drift).

Apply this within the selected block to add a success log:

 if err := lineageControllerWebhook.SetupWithManagerAsWebhook(mgr); err != nil {
   setupLog.Error(err, "unable to setup webhook", "webhook", "LineageWebhook")
   os.Exit(1)
 }
+setupLog.Info("Lineage webhook and controller registered")

Additionally, set explicit webhook server options (outside this block, around Lines 128-131):

webhookServer := webhook.NewServer(webhook.Options{
    TLSOpts: tlsOpts,
    Port:    9443,
    CertDir: "/tmp/k8s-webhook-server/serving-certs",
})

Ensure your Helm chart mounts certs to the same path or update CertDir accordingly.

Can you confirm the chart/template mounts serving certs at /tmp/k8s-webhook-server/serving-certs (or update CertDir to match)?

packages/apps/kubernetes/images/kubevirt-csi-driver.tag (1)

1-1: Avoid :latest tag; use digest-only or a versioned tag + digest.

Using latest@sha256:... is confusing and can mislead humans/tools. Prefer either digest-only or a specific version tag plus the digest.

Apply:

-ghcr.io/cozystack/cozystack/kubevirt-csi-driver:latest@sha256:35684db82ef7d5ad0cb39fa2568aa09cf60c50f3307099dbb89521aefad5baf6
+ghcr.io/cozystack/cozystack/kubevirt-csi-driver@sha256:35684db82ef7d5ad0cb39fa2568aa09cf60c50f3307099dbb89521aefad5baf6
pkg/lineage/lineage_test.go (1)

29-42: Avoid init-time real cluster dependencies in unit tests.

init() with GetConfigOrDie() and real discovery makes this a manual/integration test. Consider moving setup into the test and using envtest or the dynamic fake client with a static RESTMapper.

packages/system/cozystack-controller/templates/deployment.yaml (1)

31-42: Add basic probes and hardening for the webhook container.

Readiness/liveness on 9443 (tcp) improves rollout safety; minimal securityContext/resources help harden defaults.

Apply:

         - name: webhook
           containerPort: 9443
+        readinessProbe:
+          tcpSocket:
+            port: 9443
+          initialDelaySeconds: 2
+          periodSeconds: 10
+        livenessProbe:
+          tcpSocket:
+            port: 9443
+          initialDelaySeconds: 10
+          periodSeconds: 20
         volumeMounts:
           - name: webhook-certs
             mountPath: /tmp/k8s-webhook-server/serving-certs
             readOnly: true
+        securityContext:
+          runAsNonRoot: true
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
+        resources:
+          requests:
+            cpu: 50m
+            memory: 64Mi
+          limits:
+            cpu: 500m
+            memory: 256Mi
       volumes:
         - name: webhook-certs
           secret:
             secretName: cozystack-controller-webhook-cert
             defaultMode: 0400
internal/lineagecontrollerwebhook/controller.go (2)

12-12: RBAC: include get to be safe.

Controller cache typically needs list/watch; adding get is a low-cost hedge if future code uses direct Get.

Apply:

-// +kubebuilder:rbac:groups=cozystack.io,resources=cozystackresourcedefinitions,verbs=list;watch
+// +kubebuilder:rbac:groups=cozystack.io,resources=cozystackresourcedefinitions,verbs=get;list;watch

23-23: Make the namespace configurable with a default.

Hard-coding "cozy-system" limits portability.

Apply:

- if err := c.List(ctx, crds, &client.ListOptions{Namespace: "cozy-system"}); err != nil {
+ ns := "cozy-system"
+ if v := ctrl.GetConfigOrDie(); v != nil {
+   // optionally: pick from env var if provided
+   if n := os.Getenv("COZYSTACK_SYSTEM_NAMESPACE"); n != "" {
+     ns = n
+   }
+ }
+ if err := c.List(ctx, crds, &client.ListOptions{Namespace: ns}); err != nil {

Add os to imports.

internal/lineagecontrollerwebhook/matcher.go (1)

8-15: Handle nil selectors and log invalid LabelSelectors.

Avoid treating nil as match-all and emit a warning on parse errors to aid CRD debugging.

Apply:

 func matchLabelsToSelector(l map[string]string, s *metav1.LabelSelector) bool {
-	// TODO: emit warning if error
-	sel, err := metav1.LabelSelectorAsSelector(s)
-	if err != nil {
-		return false
-	}
-	return sel.Matches(labels.Set(l))
+	if s == nil {
+		return false
+	}
+	sel, err := metav1.LabelSelectorAsSelector(s)
+	if err != nil {
+		log.Log.Error(err, "invalid LabelSelector in CozystackResourceDefinition", "selector", s)
+		return false
+	}
+	return sel.Matches(labels.Set(l))
 }

And add import:

 import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
+	"sigs.k8s.io/controller-runtime/pkg/log"
 )
packages/system/cozystack-controller/templates/mutatingwebhookconfiguration.yaml (2)

22-22: Style: add spaces after commas in the list.

Resolves yamllint warning; improves readability.

-        resources: ["pods","secrets", "services", "persistentvolumeclaims"]
+        resources: ["pods", "secrets", "services", "persistentvolumeclaims"]

18-27: Consider adding timeoutSeconds and objectSelector guardrails.

  • Set timeoutSeconds (e.g., 10) explicitly.
  • If your webhook skips SA token secrets internally, fine; otherwise consider an objectSelector to reduce blast radius (labels only; SA token detection likely needs in-handler checks).

Can you confirm the webhook handler explicitly ignores service account token secrets (type kubernetes.io/service-account-token) and other control-plane managed objects?

internal/lineagecontrollerwebhook/config.go (1)

39-43: Key may collide across SourceRef namespaces.

chartRef uses only SourceRef.Name and Chart. If multiple HelmRepository/OCIRepository objects share the same Name in different namespaces, mappings will collide.

Consider including SourceRef.Namespace in chartRef (and the controller that populates the map) to avoid collisions.

packages/system/cozystack-controller/templates/certmanager.yaml (1)

43-45: Add FQDN to DNS names for webhook TLS.

Including the cluster-local FQDN improves compatibility with resolvers that use FQDNs.

   dnsNames:
     - cozystack-controller
     - cozystack-controller.{{ .Release.Namespace }}.svc
+    - cozystack-controller.{{ .Release.Namespace }}.svc.cluster.local
api/v1alpha1/cozystackresourcedefinitions_types.go (1)

93-103: Validation hints for label selectors (optional).

If you want to enforce non-empty items: add kubebuilder validation (MinItems) to Include/Exclude arrays. Optional.

internal/lineagecontrollerwebhook/types.go (1)

14-14: Align webhook name with deployed MWC (optional).

Marker uses name=mlineage.cozystack.io while Helm MWC uses lineage.cozystack.io. If you rely on generated manifests elsewhere, consider aligning to avoid duplicate MWCs.

-// +kubebuilder:webhook:path=/mutate-lineage,mutating=true,failurePolicy=Fail,sideEffects=None,groups="",resources=pods,secrets,services,persistentvolumeclaims,verbs=create;update,versions=v1,name=mlineage.cozystack.io,admissionReviewVersions={v1}
+// +kubebuilder:webhook:path=/mutate-lineage,mutating=true,failurePolicy=Fail,sideEffects=None,groups="",resources=pods,secrets,services,persistentvolumeclaims,verbs=create;update,versions=v1,name=lineage.cozystack.io,admissionReviewVersions={v1}
pkg/lineage/lineage.go (2)

88-91: Use structured logger instead of stderr.

Keeps logs consistent with controller-runtime and avoids noisy stderr writes.

-			fmt.Fprintf(os.Stderr, "Could not fetch owner %s/%s (%s): %v\n", obj.GetNamespace(), owner.Name, owner.Kind, err)
+			l.Error(err, "could not fetch owner",
+				"namespace", obj.GetNamespace(), "name", owner.Name, "kind", owner.Kind)

Also remove the unused os import after this change.


117-121: Optional: log when prefix trimming is ineffective.

HelmRelease name not starting with prefix results in a likely miss; a debug log helps troubleshooting.

-		ownerObj, err := getUnstructuredObject(ctx, client, mapper, a, k, obj.GetNamespace(), strings.TrimPrefix(obj.GetName(), p))
+		targetName := strings.TrimPrefix(obj.GetName(), p)
+		if targetName == obj.GetName() {
+			l.V(1).Info("helmrelease name did not have expected prefix", "name", obj.GetName(), "prefix", p)
+		}
+		ownerObj, err := getUnstructuredObject(ctx, client, mapper, a, k, obj.GetNamespace(), targetName)
internal/lineagecontrollerwebhook/webhook.go (3)

73-89: Simplify error handling; remove redundant for-loop.

Logic is single-pass; a straight if/switch is clearer.

-	labels, err := h.computeLabels(ctx, obj)
-	for {
-		if err != nil && errors.Is(err, NoAncestors) {
-			return admission.Allowed("object not managed by app")
-		}
-		if err != nil && errors.Is(err, AncestryAmbiguous) {
-			warn = append(warn, "object ancestry ambiguous, using first ancestor found")
-			break
-		}
-		if err != nil {
-			logger.Error(err, "error computing lineage labels")
-			return admission.Errored(500, fmt.Errorf("error computing lineage labels: %w", err))
-		}
-		if err == nil {
-			break
-		}
-	}
+	labels, err := h.computeLabels(ctx, obj)
+	if err != nil {
+		if errors.Is(err, NoAncestors) {
+			return admission.Allowed("object not managed by app")
+		}
+		if errors.Is(err, AncestryAmbiguous) {
+			warn = append(warn, "object ancestry ambiguous, using first ancestor found")
+		} else {
+			logger.Error(err, "error computing lineage labels")
+			return admission.Errored(500, fmt.Errorf("error computing lineage labels: %w", err))
+		}
+	}

145-148: Handle potential error from SetNestedStringMap (best-effort).

Consider capturing/logging the error to aid debugging; current code silently ignores failures.

Example:

if err := unstructured.SetNestedStringMap(o.Object, labels, "spec", "managedMetadata", "labels"); err != nil {
	// TODO: add logging if a logger is available here
}

27-27: Comment/name mismatch.

Update the comment to match the function name.

-// SetupWithManager registers the handler with the webhook server.
+// SetupWithManagerAsWebhook registers the handler with the webhook server.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ac83ac and 00dd909.

📒 Files selected for processing (42)
  • api/v1alpha1/cozystackresourcedefinitions_types.go (3 hunks)
  • api/v1alpha1/zz_generated.deepcopy.go (2 hunks)
  • cmd/cozystack-controller/main.go (2 hunks)
  • internal/lineagecontrollerwebhook/config.go (1 hunks)
  • internal/lineagecontrollerwebhook/controller.go (1 hunks)
  • internal/lineagecontrollerwebhook/matcher.go (1 hunks)
  • internal/lineagecontrollerwebhook/types.go (1 hunks)
  • internal/lineagecontrollerwebhook/webhook.go (1 hunks)
  • packages/apps/clickhouse/images/clickhouse-backup.tag (1 hunks)
  • packages/apps/http-cache/images/nginx-cache.tag (1 hunks)
  • packages/apps/kubernetes/images/cluster-autoscaler.tag (1 hunks)
  • packages/apps/kubernetes/images/kubevirt-cloud-provider.tag (1 hunks)
  • packages/apps/kubernetes/images/kubevirt-csi-driver.tag (1 hunks)
  • packages/apps/kubernetes/images/ubuntu-container-disk.tag (1 hunks)
  • packages/apps/mysql/images/mariadb-backup.tag (1 hunks)
  • packages/core/installer/values.yaml (1 hunks)
  • packages/core/testing/values.yaml (1 hunks)
  • packages/extra/bootbox/images/matchbox.tag (1 hunks)
  • packages/extra/monitoring/images/grafana.tag (1 hunks)
  • packages/extra/seaweedfs/images/objectstorage-sidecar.tag (1 hunks)
  • packages/system/bucket/images/s3manager.tag (1 hunks)
  • packages/system/cilium/values.yaml (1 hunks)
  • packages/system/cozystack-api/values.yaml (1 hunks)
  • packages/system/cozystack-controller/templates/certmanager.yaml (1 hunks)
  • packages/system/cozystack-controller/templates/crds/cozystack.io_cozystackresourcedefinitions.yaml (1 hunks)
  • packages/system/cozystack-controller/templates/deployment.yaml (1 hunks)
  • packages/system/cozystack-controller/templates/mutatingwebhookconfiguration.yaml (1 hunks)
  • packages/system/cozystack-controller/templates/service.yaml (1 hunks)
  • packages/system/cozystack-controller/values.yaml (1 hunks)
  • packages/system/dashboard/charts/kubeapps/templates/dashboard/configmap.yaml (1 hunks)
  • packages/system/dashboard/values.yaml (2 hunks)
  • packages/system/kamaji/values.yaml (2 hunks)
  • packages/system/kubeovn-plunger/values.yaml (1 hunks)
  • packages/system/kubeovn-webhook/values.yaml (1 hunks)
  • packages/system/kubeovn/values.yaml (1 hunks)
  • packages/system/kubevirt-csi-node/values.yaml (1 hunks)
  • packages/system/metallb/values.yaml (1 hunks)
  • packages/system/objectstorage-controller/values.yaml (1 hunks)
  • packages/system/seaweedfs/values.yaml (1 hunks)
  • pkg/lineage/lineage.go (1 hunks)
  • pkg/lineage/lineage_test.go (1 hunks)
  • pkg/lineage/mapper.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-11T16:05:52.112Z
Learnt from: lllamnyp
PR: cozystack/cozystack#1400
File: packages/system/cozystack-controller/templates/certmanager.yaml:39-45
Timestamp: 2025-09-11T16:05:52.112Z
Learning: In Cozystack, `cluster.local` is not the cluster domain, and the Kubernetes API server doesn't use the cluster domain when contacting webhooks. DNS names for webhook certificates should match the actual service names without assuming `cluster.local` suffix.

Applied to files:

  • packages/system/cozystack-controller/templates/service.yaml
🧬 Code graph analysis (9)
api/v1alpha1/zz_generated.deepcopy.go (1)
api/v1alpha1/cozystackresourcedefinitions_types.go (2)
  • CozystackResourceDefinitionSecrets (93-103)
  • CozystackResourceDefinitionSpec (46-53)
internal/lineagecontrollerwebhook/controller.go (2)
internal/lineagecontrollerwebhook/types.go (1)
  • LineageControllerWebhook (15-23)
api/v1alpha1/cozystackresourcedefinitions_types.go (3)
  • CozystackResourceDefinition (26-31)
  • CozystackResourceDefinitionList (36-40)
  • SourceRef (62-71)
internal/lineagecontrollerwebhook/config.go (2)
api/v1alpha1/cozystackresourcedefinitions_types.go (2)
  • CozystackResourceDefinition (26-31)
  • SourceRef (62-71)
internal/lineagecontrollerwebhook/types.go (1)
  • LineageControllerWebhook (15-23)
pkg/lineage/mapper.go (1)
api/v1alpha1/cozystackresourcedefinitions_types.go (1)
  • SourceRef (62-71)
pkg/lineage/lineage.go (1)
pkg/lineage/mapper.go (1)
  • AppMapper (10-12)
pkg/lineage/lineage_test.go (1)
pkg/lineage/lineage.go (1)
  • WalkOwnershipGraph (40-146)
internal/lineagecontrollerwebhook/types.go (1)
pkg/apiserver/apiserver.go (1)
  • Scheme (41-41)
internal/lineagecontrollerwebhook/webhook.go (2)
internal/lineagecontrollerwebhook/types.go (1)
  • LineageControllerWebhook (15-23)
pkg/lineage/lineage.go (1)
  • WalkOwnershipGraph (40-146)
cmd/cozystack-controller/main.go (1)
internal/lineagecontrollerwebhook/types.go (1)
  • LineageControllerWebhook (15-23)
🪛 YAMLlint (1.37.1)
packages/system/cozystack-controller/templates/mutatingwebhookconfiguration.yaml

[warning] 22-22: too few spaces after comma

(commas)


[error] 6-6: syntax error: expected , but found ''

(syntax)

🔇 Additional comments (16)
packages/system/metallb/values.yaml (2)

7-7: Digest‑pinned tag looks good; verify chart handles tag@sha256.

Many charts render image as ":". Using "v0.15.2@sha256:…" as the tag is valid for K8s/CRI, but confirm the Metallb chart/template doesn’t also append a separate digest field (to avoid double-specifying) and that pulls succeed.


11-11: Consistent pin for speaker; same verification applies.

Good to pin to digest. Please confirm the chart renders "repository:tag" only and that "v0.15.2@sha256:…" pulls successfully for the speaker as well. Keep controller/speaker in lockstep (which you do here).

packages/system/kamaji/values.yaml (2)

6-6: Don't use ':latest' even when pinned by digest

Replace in packages/system/kamaji/values.yaml (line 6):
tag: latest@sha256:3ac3ed8f4b77d59745c89b215bf498b430a338c0ce39652ae0c389e36551fac9
with an immutable semver tag that matches the digest (e.g., vX.Y.Z@sha256:...).

Ensure Helm templates render the full image string "repository:tag@sha256:…" and do not split/normalize the digest.

Verify there are no other ':latest' tags repo-wide (policy scanners often flag ':latest').


16-16: Validate --migrate-image compatibility and pin image (avoid :latest)

  • Kamaji supports --migrate-image and repo[:tag]Shamim Hasnath (@sha256): is a valid image reference; replace :latest@sha256:... with a pinned release tag or name@sha256 and ensure the digest matches the controller/migrator image to avoid upgrade breakage.
  • Ensure CRDs installed (kamaji-crds) match that controller image and test upgrades — edge/latest artifacts can change and migrator/controller skew can brick upgrades.

packages/system/kamaji/values.yaml (line 16): - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:latest@sha256:3ac3ed8f4b77d59745c89b215bf498b430a338c0ce39652ae0c389e36551fac9

packages/system/dashboard/values.yaml (1)

22-23: No change required — kubeapps subchart already sets dashboard.image.pullPolicy to IfNotPresent.

packages/system/dashboard/charts/kubeapps/values.yaml defines dashboard.image.pullPolicy: IfNotPresent and the chart templates read .Values.dashboard.image.pullPolicy, so adding pullPolicy in the top-level values.yaml is unnecessary.

cmd/cozystack-controller/main.go (1)

41-41: Import of lineage webhook/controller: LGTM.

Alias and package selection look correct.

packages/system/cozystack-controller/values.yaml (1)

2-5: Do not ship controller as latest; pin version and set cozystackVersion to the actual release.

Controller images and version identifiers should be immutable for traceability and safer rollbacks. This mirrors a prior review note and still applies.

Run this to inventory all latest@sha256 usages to assess blast radius:

#!/bin/bash
set -euo pipefail
rg -nP ':[Ll]atest@sha256:[0-9a-f]{64}\b' -S -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' -C2
packages/apps/mysql/images/mariadb-backup.tag (1)

1-1: Avoid latest — pin a concrete version tag or use digest-only

packages/apps/mysql/images/mariadb-backup.tag references
ghcr.io/cozystack/cozystack/mariadb-backup:latest@sha256:abfc43aed08fbbeed8f090e90158108fe59ed6279db93d169c3b1b1656af0064; replace with an explicit version + digest (e.g., vX.Y.Z@sha256:...) or digest-only (ghcr.io/...Shamim Hasnath (@sha256):...) for reproducible deployments and safer rollbacks.

If this latest usage is intentional, confirm — I can run a repo-wide search for other occurrences.

api/v1alpha1/zz_generated.deepcopy.go (1)

141-176: Deepcopy implementations for Secrets are correct and nil-safe.

Slices are deep-copied with per-element allocation, and Spec.DeepCopyInto delegates to Secrets.DeepCopyInto as expected.

Also applies to: 183-184

packages/system/cozystack-controller/templates/crds/cozystack.io_cozystackresourcedefinitions.yaml (1)

109-223: CRD schema for spec.secrets looks consistent with k8s LabelSelector semantics.

Include/exclude arrays, matchExpressions/matchLabels, and list/map hints are correctly defined.

pkg/lineage/lineage_test.go (1)

44-53: Test won’t compile (missing appMapper arg); also relies on os.Args making it non-automated.

Pass an AppMapper (e.g., &stubMapper{}) and guard manual invocation to avoid breaking CI.

Apply:

 func TestWalkingOwnershipGraph(t *testing.T) {
-	obj, err := dynClient.Resource(schema.GroupVersionResource{"", "v1", "pods"}).Namespace(os.Args[1]).Get(ctx, os.Args[2], metav1.GetOptions{})
+	if len(os.Args) < 3 {
+		t.Skip("manual test: usage `go test -args <namespace> <name>`")
+	}
+	obj, err := dynClient.Resource(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}).
+		Namespace(os.Args[1]).
+		Get(ctx, os.Args[2], metav1.GetOptions{})
 	if err != nil {
 		t.Fatal(err)
 	}
-	nodes := WalkOwnershipGraph(ctx, dynClient, mapper, obj)
+	nodes := WalkOwnershipGraph(ctx, dynClient, mapper, &stubMapper{}, obj)
 	for _, node := range nodes {
 		fmt.Printf("%#v\n", node)
 	}
 }

Longer-term: convert this into a proper unit/integration test using envtest or the dynamic fake client instead of real cluster access.

packages/system/cozystack-controller/templates/service.yaml (1)

9-15: Approve — webhook SANs and service refs match (no cluster.local).

cert-manager Certificate for the webhook includes dnsNames "cozystack-controller" and "cozystack-controller.{{ .Release.Namespace }}.svc"; MutatingWebhookConfiguration clientConfig.service entries reference name: cozystack-controller and namespace: {{ .Release.Namespace }}.

pkg/lineage/mapper.go (1)

42-49: Guard against malformed mapping strings to avoid panic.

Accessing split[2]/split[3] without length check can panic. This was raised previously.

 func (s *stubMapper) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
-	val, ok := stubMapperMap[hr.Spec.Chart.Spec.SourceRef.Name+"/"+hr.Spec.Chart.Spec.Chart]
+	key := hr.Spec.Chart.Spec.SourceRef.Name + "/" + hr.Spec.Chart.Spec.Chart
+	val, ok := stubMapperMap[key]
 	if !ok {
 		return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
 	}
 	split := strings.Split(val, "/")
-	return strings.Join(split[:2], "/"), split[2], split[3], nil
+	if len(split) < 4 {
+		return "", "", "", fmt.Errorf("invalid format in stubMapperMap for key %s: %s", key, val)
+	}
+	return strings.Join(split[:2], "/"), split[2], split[3], nil
 }
internal/lineagecontrollerwebhook/types.go (1)

18-18: Use a pointer for the admission decoder.
Controller-runtime injects *admission.Decoder; use a pointer to avoid copies and match InjectDecoder signature.
internal/lineagecontrollerwebhook/types.go:18

-	decoder   admission.Decoder
+	decoder   *admission.Decoder

Confirm your InjectDecoder implementation assigns to this field.

internal/lineagecontrollerwebhook/webhook.go (2)

136-140: Fix nil-deref when CRD mapping is absent (gate before dereferencing).

Guard crd access; currently panics if the key is missing.

-	cfg := h.config.Load().(*runtimeConfig)
-	crd := cfg.appCRDMap[appRef{gv.Group, obj.GetKind()}]
-	if matchLabelsToExcludeInclude(o.GetLabels(), crd.Spec.Secrets.Exclude, crd.Spec.Secrets.Include) {
-		labels["internal.cozystack.io/tenantsecret"] = ""
-	}
+	cfg := h.config.Load().(*runtimeConfig)
+	if crd, ok := cfg.appCRDMap[appRef{gv.Group, obj.GetKind()}]; ok && crd != nil {
+		if matchLabelsToExcludeInclude(o.GetLabels(), crd.Spec.Secrets.Exclude, crd.Spec.Secrets.Include) {
+			labels["internal.cozystack.io/tenantsecret"] = ""
+		}
+	}

121-129: Make truncation safe; current loop can panic when trimming.

The loop indexes s[len(s)-1] without guarding empty string; simplify and guard length.

-		"apps.cozystack.io/application.group": func(s string) string {
-			if len(s) < 63 {
-				return s
-			}
-			s = s[:63]
-			for b := s[62]; !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')); s = s[:len(s)-1] {
-				b = s[len(s)-1]
-			}
-			return s
-		}(gv.Group),
+		"apps.cozystack.io/application.group": func(s string) string {
+			if len(s) > 63 {
+				s = s[:63]
+			}
+			for len(s) > 0 {
+				b := s[len(s)-1]
+				if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') {
+					break
+				}
+				s = s[:len(s)-1]
+			}
+			return s
+		}(gv.Group),

Comment on lines +51 to 53
// Secret selectors
Secrets CozystackResourceDefinitionSecrets `json:"secrets,omitempty"`
}

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.

🛠️ Refactor suggestion

Make Secrets optional (pointer) to honor omitempty and keep API clean.

As a non-pointer struct, omitempty won’t omit the field, producing secrets: {}. Use a pointer so absent values are omitted.

-	// Secret selectors
-	Secrets CozystackResourceDefinitionSecrets `json:"secrets,omitempty"`
+	// Secret selectors
+	Secrets *CozystackResourceDefinitionSecrets `json:"secrets,omitempty"`

Follow-up: regenerate deepcopy and CRDs (controller-gen) after this change.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Secret selectors
Secrets CozystackResourceDefinitionSecrets `json:"secrets,omitempty"`
}
// Secret selectors
Secrets *CozystackResourceDefinitionSecrets `json:"secrets,omitempty"`
}
🤖 Prompt for AI Agents
In api/v1alpha1/cozystackresourcedefinitions_types.go around lines 51 to 53, the
Secrets field is declared as a non-pointer struct which prevents
json:"omitempty" from omitting it (resulting in empty objects like secrets:{});
change the field type to a pointer (e.g., *CozystackResourceDefinitionSecrets)
so nil values are omitted, update any constructors/initializers that set or
check this field to handle nil, then regenerate deepcopy functions and CRDs by
running controller-gen (and go generate / make target as used in the repo) to
update the generated code and manifests.

Comment on lines +25 to +31
func (l *LineageControllerWebhook) initConfig() {
l.initOnce.Do(func() {
if l.config.Load() == nil {
l.config.Store(&runtimeConfig{chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition)})
}
})
}

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.

⚠️ Potential issue

Initialize all maps in runtimeConfig.

appCRDMap remains nil; future reads will NPE. Initialize both.

 func (l *LineageControllerWebhook) initConfig() {
 	l.initOnce.Do(func() {
 		if l.config.Load() == nil {
-			l.config.Store(&runtimeConfig{chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition)})
+			l.config.Store(&runtimeConfig{
+				chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition),
+				appCRDMap:   make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition),
+			})
 		}
 	})
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (l *LineageControllerWebhook) initConfig() {
l.initOnce.Do(func() {
if l.config.Load() == nil {
l.config.Store(&runtimeConfig{chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition)})
}
})
}
func (l *LineageControllerWebhook) initConfig() {
l.initOnce.Do(func() {
if l.config.Load() == nil {
l.config.Store(&runtimeConfig{
chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition),
appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition),
})
}
})
}
🤖 Prompt for AI Agents
In internal/lineagecontrollerwebhook/config.go around lines 25-31, the
runtimeConfig is only initializing chartAppMap and leaves appCRDMap nil which
can cause NPEs later; update the stored runtimeConfig to initialize both maps
(e.g. chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition)
and appCRDMap: make(map[string]*cozyv1alpha1.CozystackResourceDefinition) or the
correct key/type used elsewhere) so both maps are non-nil when stored.

Comment on lines +33 to +44
func (l *LineageControllerWebhook) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
cfg, ok := l.config.Load().(*runtimeConfig)
if !ok {
return "", "", "", fmt.Errorf("failed to load chart-app mapping from config")
}
s := hr.Spec.Chart.Spec
val, ok := cfg.chartAppMap[chartRef{s.SourceRef.Name, s.Chart}]
if !ok {
return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
}
return "apps.cozystack.io/v1alpha1", val.Spec.Application.Kind, val.Spec.Release.Prefix, nil
}

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.

⚠️ Potential issue

Guard against uninitialized atomic.Value (call initConfig) and improve error.

Load on zero-value atomic.Value can be problematic; ensure initialization, then check nil before type assertion.

 func (l *LineageControllerWebhook) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
-	cfg, ok := l.config.Load().(*runtimeConfig)
-	if !ok {
+	l.initConfig()
+	raw := l.config.Load()
+	if raw == nil {
+		return "", "", "", fmt.Errorf("runtime config not initialized")
+	}
+	cfg, ok := raw.(*runtimeConfig)
+	if !ok || cfg == nil {
 		return "", "", "", fmt.Errorf("failed to load chart-app mapping from config")
 	}
 	s := hr.Spec.Chart.Spec
 	val, ok := cfg.chartAppMap[chartRef{s.SourceRef.Name, s.Chart}]
 	if !ok {
 		return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
 	}
 	return "apps.cozystack.io/v1alpha1", val.Spec.Application.Kind, val.Spec.Release.Prefix, nil
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (l *LineageControllerWebhook) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
cfg, ok := l.config.Load().(*runtimeConfig)
if !ok {
return "", "", "", fmt.Errorf("failed to load chart-app mapping from config")
}
s := hr.Spec.Chart.Spec
val, ok := cfg.chartAppMap[chartRef{s.SourceRef.Name, s.Chart}]
if !ok {
return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
}
return "apps.cozystack.io/v1alpha1", val.Spec.Application.Kind, val.Spec.Release.Prefix, nil
}
func (l *LineageControllerWebhook) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
l.initConfig()
raw := l.config.Load()
if raw == nil {
return "", "", "", fmt.Errorf("runtime config not initialized")
}
cfg, ok := raw.(*runtimeConfig)
if !ok || cfg == nil {
return "", "", "", fmt.Errorf("failed to load chart-app mapping from config")
}
s := hr.Spec.Chart.Spec
val, ok := cfg.chartAppMap[chartRef{s.SourceRef.Name, s.Chart}]
if !ok {
return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
}
return "apps.cozystack.io/v1alpha1", val.Spec.Application.Kind, val.Spec.Release.Prefix, nil
}
🤖 Prompt for AI Agents
internal/lineagecontrollerwebhook/config.go around lines 33 to 44: the code
calls l.config.Load() without guaranteeing the atomic.Value has been
initialized, then asserts to *runtimeConfig which can panic on nil/zero-value;
ensure initConfig has been called (or lazily initialize) before using Load,
check that loaded value is non-nil before the type assertion, and return a
clearer error that includes the helm release namespace/name when config is
missing; i.e., call or verify initConfig earlier, after Load verify v :=
l.config.Load(); if v == nil return an error like "config not initialized for
helm release <ns>/<name>", then safely type-assert to *runtimeConfig and proceed
as before.

Comment on lines +93 to +99
mutated, err := json.Marshal(obj)
if err != nil {
return admission.Errored(500, fmt.Errorf("marshal mutated pod: %w", err))
}
logger.V(1).Info("mutated pod", "namespace", obj.GetNamespace(), "name", obj.GetName())
return admission.PatchResponseFromRaw(req.Object.Raw, mutated).WithWarnings(warn...)
}

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.

🧹 Nitpick

Fix log/error strings: not always a Pod.

Use “object” to avoid misleading logs.

  mutated, err := json.Marshal(obj)
  if err != nil {
-		return admission.Errored(500, fmt.Errorf("marshal mutated pod: %w", err))
+		return admission.Errored(500, fmt.Errorf("marshal mutated object: %w", err))
  }
-	logger.V(1).Info("mutated pod", "namespace", obj.GetNamespace(), "name", obj.GetName())
+	logger.V(1).Info("mutated object", "namespace", obj.GetNamespace(), "name", obj.GetName())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mutated, err := json.Marshal(obj)
if err != nil {
return admission.Errored(500, fmt.Errorf("marshal mutated pod: %w", err))
}
logger.V(1).Info("mutated pod", "namespace", obj.GetNamespace(), "name", obj.GetName())
return admission.PatchResponseFromRaw(req.Object.Raw, mutated).WithWarnings(warn...)
}
mutated, err := json.Marshal(obj)
if err != nil {
return admission.Errored(500, fmt.Errorf("marshal mutated object: %w", err))
}
logger.V(1).Info("mutated object", "namespace", obj.GetNamespace(), "name", obj.GetName())
return admission.PatchResponseFromRaw(req.Object.Raw, mutated).WithWarnings(warn...)
}
🤖 Prompt for AI Agents
In internal/lineagecontrollerwebhook/webhook.go around lines 93 to 99, the log
and error messages refer to "pod" but the code operates on a generic object;
change the wording to "object" (e.g., update the error from "marshal mutated
pod: %w" to "marshal mutated object: %w" and the logger message from "mutated
pod" to "mutated object") so logs/errors are accurate; update both strings
accordingly and run a quick build to ensure no formatting issues.

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.29.1@sha256:2d39989846c3579dd020b9f6c77e6e314cc81aa344eaac0f6d633e723c17196d
ghcr.io/cozystack/cozystack/cluster-autoscaler:latest@sha256:89f822343654ea66efb3ac50bf72b483a52c1a11d33497fdfac5bbd0f3715c2b

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.

⚠️ Potential issue

🧩 Analysis chain

Cluster Autoscaler must match the cluster minor version; don’t use latest.

Pin to the autoscaler version recommended for your Kubernetes minor (e.g., CA 1.29.x for K8s 1.29). Using latest can break scale operations.


🌐 Web query:

What is the version skew/matrix between Kubernetes and Cluster Autoscaler (which CA version should be used for each K8s minor)?

💡 Result:

Short answer: use the Cluster Autoscaler release whose minor version matches your Kubernetes control‑plane minor (i.e. for Kubernetes 1.X run cluster‑autoscaler v1.X). This is the recommended / tested pairing; running other combinations is possible in some cases but not guaranteed. [1][2][3]

Sources:

  • Cluster Autoscaler README / recommendations. [1]
  • Historic compatibility table / guidance (tested-on combinations; CA minor == K8s minor). [2]
  • Issues/discussions where maintainers and users state CA releases should follow the Kubernetes minor release. [3]

If you want, I can produce a concrete per‑minor table for specific Kubernetes versions you care about (e.g. 1.24 → CA v1.24, 1.25 → CA v1.25, etc.).


Pin Cluster Autoscaler to the Kubernetes minor version — do not use latest

Cluster Autoscaler releases are tested per Kubernetes minor; use CA v1.X for K8s 1.X (e.g., K8s 1.29 → cluster-autoscaler v1.29.x). Replace the image in packages/apps/kubernetes/images/cluster-autoscaler.tag (line 1) with the CA release tag/digest that matches your control‑plane minor.

🤖 Prompt for AI Agents
In packages/apps/kubernetes/images/cluster-autoscaler.tag around line 1, the
image references "latest" which must be pinned to a Cluster Autoscaler release
that matches your Kubernetes control-plane minor; determine the control-plane
minor (e.g., 1.29), pick the corresponding Cluster Autoscaler v1.<minor>.x
release (from the official autoscaler GitHub releases or GHCR), get its exact
image tag or digest
(ghcr.io/cozystack/cozystack/cluster-autoscaler:v1.<minor>.<patch>@sha256:<digest>),
and replace the current line with that exact tag@sha256 value so the CA version
is tied to your K8s minor.

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:899ea667b3e575244d512cade23f30cc93d768b070f9c2bebcb440e443444bdb
ghcr.io/cozystack/cozystack/s3manager:latest@sha256:377bc82c8404ae1ea201d8b9a64015952edfa06d88fb2678b7bf7c45b96fb968

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.

🛠️ Refactor suggestion

🧩 Analysis chain

Avoid using the 'latest' tag; pin by digest-only or version+digest for reproducibility

Using :latest undermines clarity and can confuse tooling, even though the digest ensures immutability. Prefer either digest-only or a semantic version tag plus the digest.

Apply this diff to remove the latest tag while preserving the exact image:

-ghcr.io/cozystack/cozystack/s3manager:latest@sha256:377bc82c8404ae1ea201d8b9a64015952edfa06d88fb2678b7bf7c45b96fb968
+ghcr.io/cozystack/cozystack/s3manager@sha256:377bc82c8404ae1ea201d8b9a64015952edfa06d88fb2678b7bf7c45b96fb968

If you prefer human-readable versioning, replace latest with the actual release tag that corresponds to this digest (and keep the digest).

Run this to find any other latest usages to correct:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Find image refs using ':latest' (with or without digest)
rg -nP ':[^@\s]*latest(@sha256:[0-9a-f]{64})?' -C0

Length of output: 54271


Remove the :latest tag — pin by digest-only or version+digest

Apply this diff in packages/system/bucket/images/s3manager.tag to preserve the exact image:

-ghcr.io/cozystack/cozystack/s3manager:latest@sha256:377bc82c8404ae1ea201d8b9a64015952edfa06d88fb2678b7bf7c45b96fb968
+ghcr.io/cozystack/cozystack/s3manager@sha256:377bc82c8404ae1ea201d8b9a64015952edfa06d88fb2678b7bf7c45b96fb968

Repository search shows multiple other :latest usages — run rg -nP ':[^@\s]*latest(@sha256:[0-9a-f]{64})?' -C0 and update other image refs similarly (use digest-only or version+digest).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ghcr.io/cozystack/cozystack/s3manager:latest@sha256:377bc82c8404ae1ea201d8b9a64015952edfa06d88fb2678b7bf7c45b96fb968
ghcr.io/cozystack/cozystack/s3manager@sha256:377bc82c8404ae1ea201d8b9a64015952edfa06d88fb2678b7bf7c45b96fb968
🤖 Prompt for AI Agents
In packages/system/bucket/images/s3manager.tag lines 1-1, the image reference
includes the :latest tag alongside a digest; remove the :latest tag so the image
is pinned by digest-only (e.g.,
ghcr.io/cozystack/cozystack/s3manager@sha256:377bc82c8404ae1ea201d8b9a64015952edfa06d88fb2678b7bf7c45b96fb968)
or replace with an explicit version+digest if you want a version label. Also
search the repo for other occurrences with rg -nP
':[^@\s]*latest(@sha256:[0-9a-f]{64})?' and update those image refs similarly to
use digest-only or version+digest, then run tests/build to verify no regressions
and commit the changes.

@@ -1,2 +1,2 @@
cozystackAPI:
image: ghcr.io/cozystack/cozystack/cozystack-api:v0.36.1@sha256:a9ce8848b0a46e52ce47ad10fcccc4e848740f5cf1d4e6cb78fc4a196e167e1b
image: ghcr.io/cozystack/cozystack/cozystack-api:latest@sha256:be6aca4df4b769538454d6c65b7045b2bef81b2a15d70eeddbfdf55d6bd17d6c

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.

🛠️ Refactor suggestion

🧩 Analysis chain

Verify chart defaults: imagePullPolicy and appVersion alignment.

Ensure the chart does not implicitly set imagePullPolicy: Always and that Chart.yaml appVersion matches the intended API version corresponding to this digest.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check imagePullPolicy in cozystack-api chart and templates =="
rg -n -C2 -i '\bimagePullPolicy\b' packages/system/cozystack-api || echo "No explicit imagePullPolicy found."

echo
echo "== Show Chart.yaml version and appVersion for cozystack-api =="
fd -a Chart.yaml packages/system/cozystack-api | while read -r f; do
  echo "--- $f"
  rg -n '^(version|appVersion):' -n "$f" || true
done

Length of output: 329


Ensure imagePullPolicy is explicit and Chart.appVersion is set

  • No explicit imagePullPolicy found; values image contains ":latest" (with a digest) so Kubernetes will default pullPolicy to Always. Add image.pullPolicy: IfNotPresent (and make templates use {{ .Values.image.pullPolicy | default "IfNotPresent" }}) or remove the ":latest" when pinning by digest — packages/system/cozystack-api/values.yaml and packages/system/cozystack-api/templates/.
  • Chart.yaml lacks appVersion (only has version: 0.0.0 placeholder). Add appVersion matching the API version for this pinned digest — packages/system/cozystack-api/Chart.yaml.
🤖 Prompt for AI Agents
In packages/system/cozystack-api/values.yaml around line 2, add an explicit
image.pullPolicy key (set to IfNotPresent) or remove the ":latest" tag when
using a pinned digest so Kubernetes doesn't implicitly use Always; also update
the chart templates under packages/system/cozystack-api/templates to reference
the pullPolicy via {{ .Values.image.pullPolicy | default "IfNotPresent" }}. In
packages/system/cozystack-api/Chart.yaml add an appVersion field matching the
API version for the pinned digest (set appVersion to the semantic version or tag
corresponding to that digest) so Chart.appVersion is populated. Ensure both
changes are committed together.

metadata:
name: lineage
annotations:
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/cozystack-controller-webhook

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.

🧹 Nitpick

Quote the CA injection annotation to avoid YAML/template parse issues.

Unquoted Go templates often trip YAML linters and some tooling. Quote the value.

Apply:

-    cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/cozystack-controller-webhook
+    cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/cozystack-controller-webhook"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/cozystack-controller-webhook
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/cozystack-controller-webhook"
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 6-6: syntax error: expected , but found ''

(syntax)

🤖 Prompt for AI Agents
In
packages/system/cozystack-controller/templates/mutatingwebhookconfiguration.yaml
around line 6, the cert-manager.io/inject-ca-from annotation value is unquoted
which can cause YAML/template parse or linter errors; wrap the templated value
in quotes (e.g., "..." ) so the entire annotation value is a quoted string,
preserving the template expression and avoiding parsing issues.

portSecurity: true
routes: ""
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.36.1@sha256:b38c0610e9648d21326be30a773173757897d2ba9ec438272c8ae3d738956b66
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:latest@sha256:96950f57d68ca2a911a4fa4ec0b36bfb95c87ed8dc1a27845ae4a9dc1cb959ad

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.

⚠️ Potential issue

Pin kube-ovn-plunger image; avoid latest.

Keep this aligned with a tested kube-ovn version to avoid drift between helpers and controllers.

🤖 Prompt for AI Agents
In packages/system/kubeovn-plunger/values.yaml around line 3, the image is
pinned to "latest" which can drift; replace the "latest" tag with the specific
kube-ovn-plunger version that matches your tested kube-ovn release and include
the corresponding sha256 digest (e.g.,
ghcr.io/.../kubeovn-plunger:vX.Y.Z@sha256:...) so the helper image is immutable
and aligned with controller versions; update the tag+digest, commit the new
values.yaml, and add a short comment noting the matched kube-ovn version for
future maintenance.

portSecurity: true
routes: ""
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.36.1@sha256:8fa755cdb024c7bdeff390d01d6b8569d60a4b244a7371209be4c4851df5fad4
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:latest@sha256:ee87101fdcaf86c339463390c6f12ef107726746da38566bd936c5dfe5b70047

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.

⚠️ Potential issue

Pin kube-ovn-webhook image; latest on networking webhooks is high risk.

Networking control-plane components should be pinned to a known-good version + digest. latest can lead to non-deterministic behavior and hard-to-debug outages.

🤖 Prompt for AI Agents
In packages/system/kubeovn-webhook/values.yaml around line 3, the image
reference uses the unstable "latest" tag with a digest which is risky for
networking control-plane components; replace the image value with a specific
released semantic version tag plus its corresponding digest (e.g.
ghcr.io/cozystack/cozystack/kubeovn-webhook:vX.Y.Z@sha256:<digest>) so the
deployment is pinned to a known-good immutable artifact, update any
documentation or Chart defaults that reference this value, and add a note or
automation (dependabot/cron) to track and propose controlled updates of the
tag+digest.

This patch expands the CozystackResourceDefinitions with new label
selector fields to include and exclude secrets by their labelsets.
This will enable application developers to selectively show or hide
application secrets to and from end-users.

```release-note
[platform] Add selectors for application secrets, offering developers
an API to control secret visibility for end users.
```

Signed-off-by: Timofei Larkin <[email protected]>
Many resources created as part of managed apps in cozystack (pods,
secrets, etc) do not carry predictable labels that unambiguously
indicate which app originally triggered their creation. Some resources
are managed by controllers and other custom resources and this
indirection can lead to loss of information. Other controllers sometimes
simply do not allow setting labels on controlled resources and the
latter do not inherit labels from the owner. This patch implements a
webhook that sidesteps this problem with a universal solution. On
creation of a pod/secret/PVC etc it walks through the owner references
until a HelmRelease is found that can be matched with a managed app
dynamically registered in the Cozystack API server. The pod is mutated
with labels identifying the managed app.

```release-note
[cozystack-controller] Add a mutating webhook to identify the Cozystack
managed app that ultimately owns low-level resources created in the
cluster and label these resources with a reference to said app.
```

Signed-off-by: Timofei Larkin <[email protected]>
This patch populates existing CozystackResourceDefinitions with minimal
working examples of secret selectors to take advantage of the newest
revision of the ancestor tracking webhook.

```release-note
[platform] Specify secret selectors for existing managed apps in their
respective CozystackResourceDefinitions, which provides the last bit of
information necessary for the lineage webhook to correctly mark secrets
as user-facing or not.
```

Signed-off-by: Timofei Larkin <[email protected]>
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