Skip to content

Conversation

@sduranc
Copy link
Collaborator

@sduranc sduranc commented Nov 28, 2025

Description

  • can be tested in Kind with:
make test-e2e-kind
k apply -f config/samples/storage_v1alpha1_database.yaml
  • we're just missing the tf module apply that creates the subnets
  • it doesn't include the whole CD setup, so this should be taken in a different PR
  • The rbac roles for the DB resources are an LLM suggestion which I don't think it's bad, but it can surely be added in a different PR

Related Issue(s)

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)
  • All tests run green

Documentation

  • User documentation is updated with a separate linked PR in altinn-studio-docs. (if applicable)

Summary by CodeRabbit

  • New Features

    • Adds a Database custom resource for managing PostgreSQL-like databases with automatic subnet allocation.
    • Provides sample Database manifests and RBAC roles (Admin, Editor, Viewer) for access control.
    • Integrates Azure network discovery for subnet selection and adds Kind-friendly deployment/testing support.
  • Chores

    • Updated container build and test tooling (local image naming, podman support) and enhanced e2e test utilities and workflows.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 28, 2025

📝 Walkthrough

Walkthrough

Adds a Database CRD and controller that allocates VNet subnets, Azure subnet-fetch utilities and test fakes, Kind-based e2e workflow and image tooling, updated build/Dockerfile/Makefile for podman/Kind, RBAC and kustomize CRD manifests, and Go module dependency updates for Azure SDK and logging.

Changes

Cohort / File(s) Summary
API types & generated code
services/dis-pgsql-operator/api/v1alpha1/database_types.go, services/dis-pgsql-operator/api/v1alpha1/groupversion_info.go, services/dis-pgsql-operator/api/v1alpha1/zz_generated.deepcopy.go
Adds Database CRD types (spec, status, auth), GroupVersion/Scheme wiring, and autogenerated deepcopy implementations.
Controller & tests
services/dis-pgsql-operator/internal/controller/database_controller.go, services/dis-pgsql-operator/internal/controller/database_controller_test.go, services/dis-pgsql-operator/internal/controller/suite_test.go
New DatabaseReconciler that allocates SubnetCIDR using a SubnetCatalog, plus unit/integration tests and test suite scaffolding.
Network subnet utilities & tests
services/dis-pgsql-operator/internal/network/subnets.go, services/dis-pgsql-operator/internal/network/subnets_test.go
Implements SubnetCatalog, selection of first free subnet, validation (duplicates/non-empty), and tests for selection/validation behavior.
Azure test fakes
services/dis-pgsql-operator/test/azfakes/env.go, services/dis-pgsql-operator/test/azfakes/network_fakes.go
Test helpers producing fake ARM credentials/client options and a fake SubnetsServer that pages configured subnet CIDRs.
Main wiring & startup
services/dis-pgsql-operator/cmd/main.go, services/dis-pgsql-operator/PROJECT
Registers storage API, fetches SubnetCatalog at startup (real Azure creds or fakes), injects SubnetCatalog into DatabaseReconciler; PROJECT updated to include Database API resource.
CRD manifests & kustomize
services/dis-pgsql-operator/config/crd/bases/storage.dis.altinn.cloud_databases.yaml, services/dis-pgsql-operator/config/crd/*, services/dis-pgsql-operator/config/default/kustomization.yaml, services/dis-pgsql-operator/config/crd/kustomizeconfig.yaml
Adds Database CRD YAML with OpenAPI schema, kustomization entries and kustomizeconfig for CRD variable substitutions; enables CRD in default overlay.
RBAC manifests
services/dis-pgsql-operator/config/rbac/role.yaml, services/dis-pgsql-operator/config/rbac/database_admin_role.yaml, services/dis-pgsql-operator/config/rbac/database_editor_role.yaml, services/dis-pgsql-operator/config/rbac/database_viewer_role.yaml, services/dis-pgsql-operator/config/rbac/kustomization.yaml
Replaces previous pod rules with database permissions; adds Admin/Editor/Viewer ClusterRoles and updates rbac kustomization.
Kind / e2e tooling & patches
services/dis-pgsql-operator/Makefile, services/dis-pgsql-operator/.dockerignore, services/dis-pgsql-operator/Dockerfile, services/dis-pgsql-operator/config/kind/*, services/dis-pgsql-operator/config/manager/kustomization.yaml, services/dis-pgsql-operator/test/e2e/*, services/dis-pgsql-operator/test/utils/utils.go
Simplifies .dockerignore, narrows Dockerfile COPY, adds -trimpath and ldflags, switches default container tool to podman, adds Kind image archive support and kind-load/deploy-kind/test-e2e-kind targets, Kind deployment patch using fake Azure env, updates e2e image name and kind-loading to support KIND_IMAGE_ARCHIVE.
Samples & config scaffolding
services/dis-pgsql-operator/config/samples/*, services/dis-pgsql-operator/config/crd/kustomization.yaml
Adds sample Database resources and kustomize scaffolding for CRD patches/webhook placeholders.
Dependencies
services/dis-pgsql-operator/go.mod
Adds Azure SDK modules, azidentity, arm network client, and logging/telemetry dependency changes and upgrades.
Other
services/dis-pgsql-operator/config/rbac/manager_kind_patch.yaml
Adds Deployment patch for Kind with env vars and --use-az-fakes argument for manager container.

Sequence Diagram(s)

sequenceDiagram
    participant User as Client/API
    participant K8s as Kubernetes API
    participant Reconciler as DatabaseReconciler
    participant Catalog as SubnetCatalog

    User->>K8s: Create Database CR
    K8s->>Reconciler: Deliver Reconcile(Request)
    Reconciler->>K8s: Get Database
    alt SubnetCIDR present
        Reconciler-->>K8s: Done
    else
        Reconciler->>K8s: List Databases
        K8s-->>Reconciler: Database list
        Reconciler->>Catalog: FirstFreeSubnet(usedCIDRs)
        alt Subnet available
            Catalog-->>Reconciler: SubnetInfo(CIDR)
            Reconciler->>K8s: Update Database.Status.SubnetCIDR
            K8s-->>Reconciler: Status updated
        else
            Catalog-->>Reconciler: Error (none free)
            Reconciler-->>K8s: Requeue / Log error
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing focused review:
    • internal/controller/database_controller.go: concurrency, idempotency, status update vs. resource update semantics
    • internal/network/subnets.go: validation logic, duplicate detection, error messages, and behavior when catalog is empty or exhausted
    • cmd/main.go: startup credential selection, timeout handling for FetchSubnetCatalog, injection of SubnetCatalog into reconciler
    • Makefile/Dockerfile/.dockerignore and Kind-related scripts: verify podman defaults and kind image archive workflow
    • config/crd/bases/storage.dis.altinn.cloud_databases.yaml: schema correctness for required fields and conditions
    • go.mod: Azure SDK version compatibility and transitive upgrades
    • Tests: controller suite and e2e changes to ensure CI/Kind integration paths are sound

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.06% 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
Title check ✅ Passed The title 'feat(dis-psql-operator): add subnet management' clearly and specifically describes the main feature added in the pull request: subnet management functionality for the dis-pgsql-operator.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/alpha-dis-pgsql

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.

@sduranc sduranc requested a review from khanrn November 28, 2025 08:07
@sduranc sduranc requested a review from tjololo November 28, 2025 13:00
@sduranc sduranc marked this pull request as ready for review November 28, 2025 13:00
@sduranc sduranc requested a review from a team as a code owner November 28, 2025 13:00
@sduranc
Copy link
Collaborator Author

sduranc commented Nov 28, 2025

Tried as much as I could to make a simple PR, but there's still a lot of code, so please take a look, must be some mistakes here and there @tjololo.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (13)
services/dis-pgsql-operator/internal/network/subnets_test.go (2)

5-31: NewSubnetCatalog tests validate ordering and basic input well; consider asserting copy semantics.

The test nicely checks ordering and validation behavior. Since All() is intended to expose catalog contents, it may be worth adding a small test that mutates the returned slice and asserts the internal catalog isn’t affected (i.e., All() returns a defensive copy), to lock in that contract.


54-117: FirstFreeSubnet tests cover main paths; optional extra edge cases could harden behavior.

The three tests (no used, some used, all used) give good confidence in the selection logic and catalog ordering. If you want to further harden it, consider adding coverage for:

  • An empty catalog (NewSubnetCatalog(nil) / []SubnetInfo{}) passed to FirstFreeSubnet.
  • used containing CIDRs not present in the catalog (should effectively be ignored).
    These are minor, optional additions.
services/dis-pgsql-operator/config/rbac/database_admin_role.yaml (1)

1-27: Wildcard verbs are acceptable for an admin helper role but may be worth narrowing for clarity/compliance.

Given this is an admin-style helper role, verbs: ['*'] on databases is defensible, but it does trigger scanners and hides the exact operations needed. If you want to align with tools like Checkov and make intent explicit, you could replace '*' with the standard CRUD/watch verbs used by the manager role (create, get, list, watch, update, patch, delete), keeping databases/status to get only.

-  resources:
-  - databases
-  verbs:
-  - '*'
+  resources:
+  - databases
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
services/dis-pgsql-operator/config/manager/kustomization.yaml (1)

1-8: Kustomization wiring is correct; verify that the localhost e2e image is overridden outside Kind.

Defining this as a proper Kustomization with an images block is good. Since it defaults the controller image to localhost/dis-pgsql-operator:e2e, please confirm that your higher‑level environment overlays (Flux, prod kustomize, etc.) override this image so non‑Kind deployments don’t accidentally reference a localhost/e2e image. Based on learnings, this should normally be handled via environment‑specific kustomize overlays.

services/dis-pgsql-operator/config/rbac/kustomization.yaml (1)

21-27: Helper Database Admin/Editor/Viewer roles are wired correctly; consider separating them if you want a lean default.

The added roles and comments look good and will be available for cluster admins without affecting the operator directly. If you prefer to avoid installing helper ClusterRoles by default, you could move these into a separate kustomize overlay that operators can opt into.

services/dis-pgsql-operator/.dockerignore (1)

2-3: Consider ignoring more non-build directories to keep Docker context small

Only bin/ is ignored now, so .git, test artifacts, and other large directories (if present) will be sent in the build context. That’s harmless but can slow builds and cache invalidation. Consider optionally ignoring additional non-runtime paths (e.g. VCS metadata, local build/test output) if they’re not needed by the Docker build.

services/dis-pgsql-operator/Dockerfile (1)

14-18: Explicit COPY set looks reasonable; consider whether azfakes should ship in prod image

Copying only cmd/main.go, api/, internal/, and test/azfakes/ is a nice tightening of the build context. If the azfakes package is only ever used when running with --use-az-fakes in Kind/e2e and never in production, consider moving it behind a build tag or a separate test-only binary to keep the production image smaller. If you intentionally share the same image for both prod and Kind, current approach is fine.

services/dis-pgsql-operator/config/kind/manager_kind_patch.yaml (1)

1-20: Patch is suitable for Kind; consider adding basic securityContext

Using imagePullPolicy: Never, --use-az-fakes, and fake Azure env values is exactly what you want for a Kind/e2e overlay and clearly avoids real cloud calls.

Given the static-analysis hints about privilege escalation/root, you might optionally add a minimal securityContext to this container here (if not already set in the base Deployment), e.g.:

securityContext:
  allowPrivilegeEscalation: false
  runAsNonRoot: true

to keep linters and cluster policies happy, even in test environments.

services/dis-pgsql-operator/internal/controller/database_controller_test.go (1)

16-125: Well-structured controller tests; consider minor resilience tweaks

The tests exercise the reconciler end-to-end and use Eventually with explicit timeouts/polling, which is solid. Two small, optional improvements:

  • To reduce duplication and make future refactors easier, you could derive types.NamespacedName from the Database objects (db.Name, db.Namespace) instead of hardcoding "db1"/"db2" in the Get calls.
  • If you ever enable Ginkgo parallelism for this suite, consider either making resource names unique per spec or adding explicit cleanup to avoid cross-spec interference.
services/dis-pgsql-operator/test/utils/utils.go (1)

136-162: Archive-based kind image loading is sound; minor doc nit

The KIND_IMAGE_ARCHIVE branch is a clean extension and keeps the old docker-image path intact. One small, optional tweak:

  • The function name/comment still suggest it always uses the image “name”, but in archive mode the name parameter is ignored. You might clarify the comment to mention that behavior explicitly to avoid confusion for future callers.
services/dis-pgsql-operator/internal/controller/suite_test.go (1)

38-92: Envtest suite and subnet catalog wiring look good; avoid double directory scan

The suite wiring (scheme registration, CRD loading, fixed SubnetCatalog, and manager startup) looks solid and should make the controller tests reliable.

A tiny, optional improvement: getFirstFoundEnvTestBinaryDir() is called twice in the if at Lines 55–58, which re-reads the directory and can log the same error twice. Caching the result in a local variable avoids the duplicate work/logging:

-    // Retrieve the first found binary directory to allow running tests from IDEs
-    if getFirstFoundEnvTestBinaryDir() != "" {
-        testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir()
-    }
+    // Retrieve the first found binary directory to allow running tests from IDEs
+    if dir := getFirstFoundEnvTestBinaryDir(); dir != "" {
+        testEnv.BinaryAssetsDirectory = dir
+    }

Also applies to: 101-115

services/dis-pgsql-operator/internal/network/subnets.go (2)

70-76: Consider adding a clarifying comment.

The logic to skip empty strings in the used set is correct, but a brief comment explaining why (e.g., "empty strings represent databases without allocated subnets") would improve readability.


120-128: Consider logging skipped subnets.

Subnets with nil properties or AddressPrefix are silently skipped. While this is defensive, it could hide Azure configuration issues. Consider logging a warning when skipping subnets.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d513dc and fc57cf0.

⛔ Files ignored due to path filters (1)
  • services/dis-pgsql-operator/go.sum is excluded by !**/*.sum
📒 Files selected for processing (33)
  • services/dis-pgsql-operator/.dockerignore (1 hunks)
  • services/dis-pgsql-operator/Dockerfile (1 hunks)
  • services/dis-pgsql-operator/Makefile (5 hunks)
  • services/dis-pgsql-operator/PROJECT (1 hunks)
  • services/dis-pgsql-operator/api/v1alpha1/database_types.go (1 hunks)
  • services/dis-pgsql-operator/api/v1alpha1/groupversion_info.go (1 hunks)
  • services/dis-pgsql-operator/api/v1alpha1/zz_generated.deepcopy.go (1 hunks)
  • services/dis-pgsql-operator/cmd/main.go (6 hunks)
  • services/dis-pgsql-operator/config/crd/bases/storage.dis.altinn.cloud_databases.yaml (1 hunks)
  • services/dis-pgsql-operator/config/crd/kustomization.yaml (1 hunks)
  • services/dis-pgsql-operator/config/crd/kustomizeconfig.yaml (1 hunks)
  • services/dis-pgsql-operator/config/default/kustomization.yaml (1 hunks)
  • services/dis-pgsql-operator/config/kind/kustomization.yaml (1 hunks)
  • services/dis-pgsql-operator/config/kind/manager_kind_patch.yaml (1 hunks)
  • services/dis-pgsql-operator/config/manager/kustomization.yaml (1 hunks)
  • services/dis-pgsql-operator/config/rbac/database_admin_role.yaml (1 hunks)
  • services/dis-pgsql-operator/config/rbac/database_editor_role.yaml (1 hunks)
  • services/dis-pgsql-operator/config/rbac/database_viewer_role.yaml (1 hunks)
  • services/dis-pgsql-operator/config/rbac/kustomization.yaml (1 hunks)
  • services/dis-pgsql-operator/config/rbac/role.yaml (1 hunks)
  • services/dis-pgsql-operator/config/samples/kustomization.yaml (1 hunks)
  • services/dis-pgsql-operator/config/samples/storage_v1alpha1_database.yaml (1 hunks)
  • services/dis-pgsql-operator/go.mod (5 hunks)
  • services/dis-pgsql-operator/internal/controller/database_controller.go (1 hunks)
  • services/dis-pgsql-operator/internal/controller/database_controller_test.go (1 hunks)
  • services/dis-pgsql-operator/internal/controller/suite_test.go (1 hunks)
  • services/dis-pgsql-operator/internal/network/subnets.go (1 hunks)
  • services/dis-pgsql-operator/internal/network/subnets_test.go (1 hunks)
  • services/dis-pgsql-operator/test/azfakes/env.go (1 hunks)
  • services/dis-pgsql-operator/test/azfakes/network_fakes.go (1 hunks)
  • services/dis-pgsql-operator/test/e2e/e2e_suite_test.go (1 hunks)
  • services/dis-pgsql-operator/test/e2e/e2e_test.go (1 hunks)
  • services/dis-pgsql-operator/test/utils/utils.go (1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-05-08T06:08:49.970Z
Learnt from: tjololo
Repo: Altinn/altinn-platform PR: 1606
File: services/dis-identity-operator/config/manager/manager.yaml:66-66
Timestamp: 2025-05-08T06:08:49.970Z
Learning: In Altinn Kubernetes operators, image references in deployment manifests are handled through Kustomize customization rather than being hardcoded in the base YAML templates, which keeps the base manifests generic and enables environment-specific configuration.

Applied to files:

  • services/dis-pgsql-operator/config/kind/kustomization.yaml
  • services/dis-pgsql-operator/config/crd/kustomization.yaml
  • services/dis-pgsql-operator/config/manager/kustomization.yaml
  • services/dis-pgsql-operator/config/crd/kustomizeconfig.yaml
  • services/dis-pgsql-operator/config/default/kustomization.yaml
  • services/dis-pgsql-operator/Makefile
📚 Learning: 2025-10-27T14:29:47.584Z
Learnt from: tjololo
Repo: Altinn/altinn-platform PR: 2405
File: flux/blackbox-exporter/apps/helmrelease-patch.yaml:1-18
Timestamp: 2025-10-27T14:29:47.584Z
Learning: In the Altinn platform repository, FluxCD Kustomization resources (kind: Kustomization, apiVersion: kustomize.toolkit.fluxcd.io/v1) use postBuild.substitute for variable replacement, with variables referenced using ${VAR_NAME} syntax in patch files and manifests. The Flux Kustomization resources are typically defined in flux-kustomize.yaml files separate from the kustomize kustomization.yaml files.

Applied to files:

  • services/dis-pgsql-operator/config/crd/kustomization.yaml
  • services/dis-pgsql-operator/config/crd/kustomizeconfig.yaml
📚 Learning: 2025-08-12T11:30:54.035Z
Learnt from: tjololo
Repo: Altinn/altinn-platform PR: 2041
File: flux/dis-apim/flux-kustomize.yaml:21-21
Timestamp: 2025-08-12T11:30:54.035Z
Learning: In the Altinn dis-apim-operator, the image tag `latest` is used temporarily in flux/dis-apim/flux-kustomize.yaml until proper versioned releases of the controller are available, at which point they plan to implement version pinning.

Applied to files:

  • services/dis-pgsql-operator/config/manager/kustomization.yaml
  • services/dis-pgsql-operator/Makefile
📚 Learning: 2025-09-05T07:37:27.185Z
Learnt from: tjololo
Repo: Altinn/altinn-platform PR: 2169
File: flux/linkerd/helmrelease.yaml:102-102
Timestamp: 2025-09-05T07:37:27.185Z
Learning: In Altinn's Kubernetes setup, environment variable substitution in YAML manifests (like ${DEFAULT_INBOUND_POLICY:=all-unauthenticated}) is handled by Kustomize, not by shell expansion or Flux directly.

Applied to files:

  • services/dis-pgsql-operator/config/crd/kustomizeconfig.yaml
📚 Learning: 2025-08-15T08:58:01.562Z
Learnt from: tjololo
Repo: Altinn/altinn-platform PR: 2072
File: services/dis-apim-operator/config/default/kustomization.yaml:127-163
Timestamp: 2025-08-15T08:58:01.562Z
Learning: Kubebuilder 4.7.1 scaffolds kustomize fieldPath entries with leading dots (e.g., `.metadata.name`, `.spec.dnsNames.0`). Both formats with and without leading dots are valid in kustomize, but the scaffold-generated format should be trusted as it's verified upstream by kubebuilder maintainers.

Applied to files:

  • services/dis-pgsql-operator/config/crd/kustomizeconfig.yaml
📚 Learning: 2025-08-28T13:59:14.257Z
Learnt from: sduranc
Repo: Altinn/altinn-platform PR: 2150
File: services/lakmus/cmd/main.go:99-99
Timestamp: 2025-08-28T13:59:14.257Z
Learning: The scanAndUpdateSecretGauges function in services/lakmus/cmd/main.go takes exactly 3 parameters: ctx, subscriptionID, and cred. The armOpts and secOpts are handled as global variables within the function implementation, not passed as parameters.

Applied to files:

  • services/dis-pgsql-operator/cmd/main.go
📚 Learning: 2025-08-15T08:44:09.677Z
Learnt from: tjololo
Repo: Altinn/altinn-platform PR: 2072
File: services/dis-apim-operator/.devcontainer/devcontainer.json:3-3
Timestamp: 2025-08-15T08:44:09.677Z
Learning: In the Altinn dis-apim-operator project, the team prefers using unpinned/latest Go image tags (e.g., "golang:1.24") in devcontainer configurations over pinned versions, prioritizing convenience and automatic updates over strict reproducibility.

Applied to files:

  • services/dis-pgsql-operator/Makefile
🧬 Code graph analysis (6)
services/dis-pgsql-operator/internal/controller/database_controller_test.go (1)
services/dis-pgsql-operator/api/v1alpha1/database_types.go (3)
  • Database (58-72)
  • DatabaseSpec (19-30)
  • DatabaseAuth (8-16)
services/dis-pgsql-operator/internal/network/subnets_test.go (1)
services/dis-pgsql-operator/internal/network/subnets.go (2)
  • SubnetInfo (14-17)
  • NewSubnetCatalog (31-56)
services/dis-pgsql-operator/internal/controller/suite_test.go (2)
services/dis-pgsql-operator/api/v1alpha1/groupversion_info.go (1)
  • AddToScheme (35-35)
services/dis-pgsql-operator/internal/network/subnets.go (3)
  • NewSubnetCatalog (31-56)
  • SubnetInfo (14-17)
  • SubnetCatalog (24-26)
services/dis-pgsql-operator/api/v1alpha1/database_types.go (2)
services/lakmus/manifests/imports/k8s.ts (2)
  • ObjectMeta (11399-11519)
  • ListMeta (11711-11740)
services/dis-pgsql-operator/api/v1alpha1/groupversion_info.go (1)
  • SchemeBuilder (32-32)
services/dis-pgsql-operator/internal/controller/database_controller.go (2)
services/dis-pgsql-operator/internal/network/subnets.go (1)
  • SubnetCatalog (24-26)
services/dis-pgsql-operator/api/v1alpha1/database_types.go (2)
  • Database (58-72)
  • DatabaseList (77-81)
services/dis-pgsql-operator/api/v1alpha1/zz_generated.deepcopy.go (1)
services/dis-pgsql-operator/api/v1alpha1/database_types.go (5)
  • Database (58-72)
  • DatabaseAuth (8-16)
  • DatabaseList (77-81)
  • DatabaseSpec (19-30)
  • DatabaseStatus (33-52)
🪛 Checkov (3.2.334)
services/dis-pgsql-operator/config/rbac/database_admin_role.yaml

[medium] 8-27: Minimize wildcard use in Roles and ClusterRoles

(CKV_K8S_49)

services/dis-pgsql-operator/config/kind/manager_kind_patch.yaml

[medium] 1-20: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[medium] 1-20: Minimize the admission of root containers

(CKV_K8S_23)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build, scan and release / Build and scan Docker image
  • GitHub Check: Run tests on Ubuntu
  • GitHub Check: Run linter on Ubuntu
  • GitHub Check: Analyze (go)
🔇 Additional comments (35)
services/dis-pgsql-operator/go.mod (4)

12-14: Version alignment is correct — no compatibility issues found.

The web search confirms that controller-runtime v0.22.x (including v0.22.4) targets k8s.io/* v0.34.x. The pinned versions in go.mod (k8s.io/apimachinery v0.34.1, k8s.io/client-go v0.34.1, and sigs.k8s.io/controller-runtime v0.22.4) follow the documented compatibility matrix and are properly aligned.


6-8: Azure SDK versions are mutually compatible—no action needed.

Based on verification, the three Azure SDK modules specified (azcore v1.20.0, azidentity v1.13.1, and armnetwork/v7 v7.2.0) are compatible:

  • All three versions are present in the go.sum file with successful resolution, confirming go mod tidy executed without conflicts.
  • azidentity v1.13.1 is built against azcore v1.x and is compatible with v1.20.0 (both maintain v1.x semver stability).
  • armnetwork v7.2.0 and azidentity v1.13.1 are from the same Azure SDK release cycle and are designed to work together.
  • Transitive dependencies (azidentity/cache v0.3.2, internal v1.11.2, resourcemanager/internal v3.1.0) are consistent and properly resolved.

No version conflicts, incompatibilities, or alignment issues exist.


6-9: No known unpatched security vulnerabilities detected in the specified dependency versions.

The web search confirms:

  1. golang.org/x/crypto v0.42.0 — Not affected by tracked CVEs. It contains the patch for CVE-2023-48795 (fixed in v0.17.0) and later vulnerability fixes.

  2. golang.org/x/net v0.44.0 — Not affected by the listed advisories (CVE-2025-22872, CVE-2025-22870, CVE-2024-45338). All patched versions are earlier than v0.44.0.

  3. Azure SDK versions (azcore v1.20.0, azidentity v1.13.1, armnetwork/v7 v7.2.0) — No specific vulnerabilities identified.

While newer patch releases may be available (e.g., golang.org/x/crypto v0.52+), the current versions contain known security patches and are not known to have unpatched CVEs. The versions are mature and stable, though they lack the latest enhancements and bug fixes available in more recent releases. Updating to the latest versions is a maintenance best practice for staying within the active support window, but not an urgent security requirement based on current vulnerability data.


3-3: Consider upgrading to Go 1.25.4, the current stable version.

Go 1.24.6 is a valid release (released Aug 6, 2025), but Go 1.25.4 is the current stable version as of October 31, 2025. While 1.24.6 remains supported, upgrading aligns with keeping dependencies current. Given the project's preference for latest versions (as seen in other operators), consider updating the go directive from 1.24.6 to 1.25.4.

services/dis-pgsql-operator/config/rbac/role.yaml (1)

7-26: Manager RBAC for Database CRD looks appropriately scoped.

The rule set (CRUD+watch on databases and get/patch/update on databases/status) is consistent with typical controller needs and avoids unnecessary wildcards. No issues from my side.

services/dis-pgsql-operator/test/e2e/e2e_test.go (1)

71-75: Switch to deploy-kind fits the Kind e2e flow; just confirm cleanup symmetry with undeploy.

Using make deploy-kind IMG=... here makes sense for Kind-based e2e. Please double‑check that deploy-kind uses the same kustomization/overlays as undeploy/uninstall, so the AfterAll cleanup fully removes what was deployed by this test suite.

services/dis-pgsql-operator/config/default/kustomization.yaml (1)

17-19: Including ../crd in default kustomization makes the deployment self‑contained.

Adding the CRD resource here ensures a config/default apply includes the Database CRD without relying on a separate step. The extra apply alongside make install is idempotent, so this looks good.

services/dis-pgsql-operator/config/samples/kustomization.yaml (1)

1-4: Sample kustomization for Database CRD looks standard.

Referencing storage_v1alpha1_database.yaml with the scaffold marker matches Kubebuilder patterns; no changes needed.

services/dis-pgsql-operator/test/e2e/e2e_suite_test.go (1)

43-46: Verify image tag consistency between build, kind load, and deploy-kind

projectImage is used both for make docker-build IMG=… and LoadImageToKindClusterWithName. Please double-check that any make deploy-kind or kustomize image overrides use the same tag (localhost/dis-pgsql-operator:v0.0.1) so the Deployment in the Kind cluster actually runs the image built and loaded here.

services/dis-pgsql-operator/config/samples/storage_v1alpha1_database.yaml (1)

1-23: Sample CRs look good; just confirm they match CRD validation

The two Database samples are well-formed and useful for quick testing. Please just confirm that:

  • spec.serverType: dev is one of the allowed values in the CRD schema, and
  • spec.version: 17 matches the accepted type/format (e.g. integer vs string)

so that kubectl apply on these examples won’t fail validation.

services/dis-pgsql-operator/Dockerfile (2)

20-26: Build flags look good; verify TARGETOS/TARGETARCH wiring in your build pipeline

go build -trimpath -ldflags="-s -w" -o manager cmd/main.go is fine and matches common patterns for minimal binaries. With:

RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} \
    go build ...

please ensure:

  • TARGETOS / TARGETARCH are correctly set by your Docker/Buildx invocation when doing multi-arch builds, and
  • for local single-arch docker build, the resulting GOOS/GOARCH combination still matches the platform you intend to run in Kind/cluster.

This is mostly a wiring/CI concern, but worth confirming so e2e images match cluster platform expectations.


1-3: The golang:1.24 base image exists and is properly aligned with your Go toolchain.

The Docker Hub golang image includes the golang:1.24 tag. Your go.mod declares go 1.24.6, which is compatible with the generic golang:1.24 tag in the Dockerfile. No version misalignment or build break risk has been identified.

services/dis-pgsql-operator/config/kind/kustomization.yaml (1)

1-12: Kind overlay wiring looks correct

Referencing ../default and applying manager_kind_patch.yaml to dis-pgsql-operator-controller-manager in the dis-pgsql-operator-system namespace matches the established pattern of using Kustomize overlays for environment-specific tweaks instead of hardcoding in base manifests. Based on learnings, this aligns well with how other Altinn operators are configured.

services/dis-pgsql-operator/config/crd/kustomization.yaml (1)

1-16: CRD kustomization matches kubebuilder conventions

This CRD kustomization referencing bases/storage.dis.altinn.cloud_databases.yaml and leaving webhook/config sections scaffolded but commented is in line with kubebuilder defaults and the pattern of invoking it from config/default. Looks good as a base for the Database CRD.

services/dis-pgsql-operator/test/azfakes/network_fakes.go (1)

1-43: Code implementation is correct and follows Azure SDK v7 fake patterns

Verification confirms SubnetsServerOneVNet correctly implements the fake pattern for the Azure Go SDK v7. The NewListPager callback signature matches the expected SubnetsClient.NewListPager interface, and azfake.PagerResponder[armnetwork.SubnetsClientListResponse] is the standard generic type from azcore/fake for building paginated fake responses. The single-page response via resp.AddPage(http.StatusOK, page, nil) follows the documented Azure SDK fake pager pattern.

The implementation is production-ready for test scenarios.

services/dis-pgsql-operator/config/crd/kustomizeconfig.yaml (1)

1-19: Kustomize CRD substitutions look correct

The name/namespace and annotation var-reference wiring for the CRD conversion webhook follows the expected kustomize patterns and should work fine with kubebuilder-scaffolded CRDs.

services/dis-pgsql-operator/config/rbac/database_viewer_role.yaml (1)

1-29: Viewer ClusterRole is appropriately scoped read-only access

The database-viewer-role rules (get/list/watch on databases, get on databases/status) provide the expected read-only surface without mutation verbs, which is appropriate for monitoring/visibility use-cases.

services/dis-pgsql-operator/config/rbac/database_editor_role.yaml (1)

1-33: Editor ClusterRole matches expected CRUD responsibilities

The database-editor-role grants the usual create/read/update/delete verbs on databases while limiting databases/status to get, which is a sensible balance between management capabilities and protection of controller-owned status.

services/dis-pgsql-operator/cmd/main.go (1)

59-101: Let me verify the Azure SDK documentation for nil options handling:

github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v7 NewSubnetsClient documentation
services/dis-pgsql-operator/test/azfakes/env.go (1)

10-38: Code patterns verified against Azure SDK documentation—implementation is correct.

The review's verification requests have been confirmed:

  1. armnetwork.NewSubnetsClient accepts nil *arm.ClientOptions
    Microsoft samples show calling the constructor with nil for options.

  2. Custom Transport via ClientOptions is the intended pattern
    The code correctly embeds azcore.ClientOptions in arm.ClientOptions and sets the Transport field, matching Azure SDK guidance.

  3. Non-nil *arm.ClientOptions in both branches is appropriate
    While the SDK accepts nil, returning a constructed (possibly empty) ClientOptions in both branches is a valid, defensive approach. The code ensures callers do not need nil checks.

The implementation is sound and the approval stands.

services/dis-pgsql-operator/PROJECT (1)

11-20: LGTM!

The Database CRD resource configuration follows standard Kubebuilder scaffolding conventions.

services/dis-pgsql-operator/api/v1alpha1/groupversion_info.go (1)

27-36: LGTM!

The API group version registration follows Kubebuilder conventions correctly.

services/dis-pgsql-operator/config/crd/bases/storage.dis.altinn.cloud_databases.yaml (1)

1-159: LGTM!

The CRD definition is well-structured with appropriate validation rules and follows Kubernetes API conventions.

services/dis-pgsql-operator/internal/controller/database_controller.go (3)

34-66: LGTM - Good concurrency control.

The reconcile logic is correct. The single-threaded reconciler (Line 109) prevents concurrent subnet allocations, and standard Kubernetes optimistic concurrency handles any race conditions during status updates.


68-102: LGTM!

The subnet allocation logic correctly lists all Databases, collects used subnets, and allocates the first available subnet. Error handling is appropriate.


104-112: LGTM - Appropriate concurrency design.

The single-threaded reconciler is a reasonable design choice for subnet allocation, prioritizing correctness over scalability. For database provisioning (an infrequent operation), this trade-off is appropriate.

services/dis-pgsql-operator/Makefile (2)

2-3: LGTM!

The image tag and container tool changes align with Kind-based testing infrastructure and team preferences for using podman in development environments.

Also applies to: 18-18


92-95: LGTM!

The new Kind-based testing targets provide a complete workflow for building, loading, and testing the operator in an isolated environment.

Also applies to: 159-163, 184-187

services/dis-pgsql-operator/internal/network/subnets.go (3)

31-56: LGTM!

The subnet catalog constructor includes appropriate validation for empty CIDRs and duplicates, and correctly preserves input order.


88-92: LGTM!

Defensive copying prevents external modifications to the internal catalog state.


7-9: Azure SDK versions are current and secure—no vulnerabilities detected.

The declared versions in go.mod (azcore v1.20.0, armnetwork/v7 v7.2.0) are the latest stable releases. Azcore v1.20.0 was released in November 2025 with no known CVEs. Armnetwork/v7 v7.2.0 is the current stable version with no security advisories. The also-declared azidentity v1.13.1 is well past the CVE-2024-35255 fix threshold (v1.6.0+). All dependencies are secure.

services/dis-pgsql-operator/api/v1alpha1/database_types.go (3)

8-30: LGTM!

The DatabaseAuth and DatabaseSpec types have appropriate validation markers and follow Kubernetes CRD best practices.


33-52: LGTM!

The DatabaseStatus structure correctly uses optional fields and standard metav1.Condition for status reporting.


58-85: LGTM!

The Database and DatabaseList types follow standard Kubernetes CRD patterns with proper kubebuilder markers for root objects and status subresources.

services/dis-pgsql-operator/api/v1alpha1/zz_generated.deepcopy.go (1)

1-138: LGTM!

Auto-generated deepcopy methods follow standard Kubernetes code generation patterns and correctly handle all fields including slices.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
services/dis-pgsql-operator/cmd/main.go (2)

251-258: LGTM! Controller initialization is correct.

The DatabaseReconciler is properly initialized with the fetched subnet catalog. The static catalog approach (loaded once at startup) is intentional per the controller design.

Consider adding a brief comment before Line 245 explaining the design decision:

// Fetch the subnet catalog once at startup. The catalog is static for the lifetime
// of the operator to avoid repeated Azure API calls during reconciliation.
subnetCatalog, err := network.FetchSubnetCatalog(ctx, subscriptionID, resourceGroup, vnetName, cred, armOpts)

63-63: Consider extracting Azure setup logic into a helper function.

The main() function continues to grow in complexity with this PR. While the existing gocyclo suppression indicates this is a known issue, extracting the Azure authentication and subnet catalog setup (lines 216-249) into a helper function would improve readability and testability.

Example refactor:

// setupAzureSubnetCatalog initializes Azure credentials and fetches the subnet catalog.
func setupAzureSubnetCatalog(
	useFakes bool,
	subscriptionID, resourceGroup, vnetName string,
) (*network.SubnetCatalog, error) {
	var cred azcore.TokenCredential
	var armOpts *arm.ClientOptions
	var err error

	if useFakes {
		srv := azfakes.SubnetsServerOneVNet([]string{
			"10.100.1.0/28",
			"10.100.1.16/28",
			"10.100.1.32/28",
		})
		env := azfakes.NewNetworkEnv(srv)
		cred = env.Cred
		armOpts = env.ARM
		setupLog.Info("running with Azure SDK fakes for armnetwork")
	} else {
		cred, err = azidentity.NewDefaultAzureCredential(nil)
		if err != nil {
			return nil, fmt.Errorf("azure credential error: %w", err)
		}
		armOpts = nil
	}

	if subscriptionID == "" || resourceGroup == "" || vnetName == "" {
		return nil, fmt.Errorf("subscription-id, resource-group and vnet-name are required")
	}

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	return network.FetchSubnetCatalog(ctx, subscriptionID, resourceGroup, vnetName, cred, armOpts)
}

Then in main():

subnetCatalog, err := setupAzureSubnetCatalog(useFakes, subscriptionID, resourceGroup, vnetName)
if err != nil {
	setupLog.Error(err, "failed to setup Azure subnet catalog")
	os.Exit(1)
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc57cf0 and 954572e.

📒 Files selected for processing (4)
  • services/dis-pgsql-operator/cmd/main.go (6 hunks)
  • services/dis-pgsql-operator/internal/controller/database_controller.go (1 hunks)
  • services/dis-pgsql-operator/internal/network/subnets_test.go (1 hunks)
  • services/dis-pgsql-operator/test/azfakes/network_fakes.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • services/dis-pgsql-operator/internal/network/subnets_test.go
  • services/dis-pgsql-operator/internal/controller/database_controller.go
  • services/dis-pgsql-operator/test/azfakes/network_fakes.go
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-28T13:59:14.257Z
Learnt from: sduranc
Repo: Altinn/altinn-platform PR: 2150
File: services/lakmus/cmd/main.go:99-99
Timestamp: 2025-08-28T13:59:14.257Z
Learning: The scanAndUpdateSecretGauges function in services/lakmus/cmd/main.go takes exactly 3 parameters: ctx, subscriptionID, and cred. The armOpts and secOpts are handled as global variables within the function implementation, not passed as parameters.

Applied to files:

  • services/dis-pgsql-operator/cmd/main.go
🧬 Code graph analysis (1)
services/dis-pgsql-operator/cmd/main.go (5)
services/dis-pgsql-operator/api/v1alpha1/groupversion_info.go (1)
  • AddToScheme (35-35)
services/dis-pgsql-operator/test/azfakes/network_fakes.go (1)
  • SubnetsServerOneVNet (15-46)
services/dis-pgsql-operator/test/azfakes/env.go (1)
  • NewNetworkEnv (19-38)
services/dis-pgsql-operator/internal/network/subnets.go (2)
  • FetchSubnetCatalog (96-138)
  • SubnetCatalog (24-26)
services/dis-pgsql-operator/internal/controller/database_controller.go (1)
  • DatabaseReconciler (21-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build, scan and release / Build and scan Docker image
  • GitHub Check: Analyze (go)
🔇 Additional comments (5)
services/dis-pgsql-operator/cmd/main.go (5)

19-49: LGTM! Clean import additions.

The new imports are well-organized and all appear necessary for the Azure integration and subnet catalog functionality.


59-59: LGTM! Correct schema registration.

Registering the v1alpha1 storage API types follows the standard kubebuilder pattern.


96-116: LGTM! Good flag design with environment variable fallbacks.

The Azure configuration flags follow Go best practices by allowing environment variables as defaults with flag overrides. The use-az-fakes flag is particularly useful for local development and testing.


216-234: LGTM! Clean credential handling for fake and production modes.

The conditional Azure credential setup is well-designed:

  • Fake mode provides a controlled test environment with predefined subnets
  • Production mode uses NewDefaultAzureCredential, which is the Azure SDK best practice
  • Error handling is appropriate

241-249: Verify the 30-second timeout is adequate for production.

The subnet catalog fetch logic is correct with proper context handling and error management. However, the 30-second timeout may be insufficient if Azure API experiences throttling or network delays.

Consider monitoring startup times in production to confirm this timeout is appropriate. If timeouts occur, you may need to increase it or add retry logic.

Comment on lines +236 to +239
if subscriptionID == "" || resourceGroup == "" || vnetName == "" {
setupLog.Error(fmt.Errorf("missing config"), "subscription-id, resource-group and vnet-name are required")
os.Exit(1)
}
Copy link
Contributor

@coderabbitai coderabbitai bot Nov 28, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Improve error message specificity.

The validation correctly checks required Azure parameters, but the error message doesn't indicate which specific parameter(s) are missing, making troubleshooting harder.

Consider this improvement:

-	if subscriptionID == "" || resourceGroup == "" || vnetName == "" {
-		setupLog.Error(fmt.Errorf("missing config"), "subscription-id, resource-group and vnet-name are required")
+	var missing []string
+	if subscriptionID == "" {
+		missing = append(missing, "subscription-id")
+	}
+	if resourceGroup == "" {
+		missing = append(missing, "resource-group")
+	}
+	if vnetName == "" {
+		missing = append(missing, "vnet-name")
+	}
+	if len(missing) > 0 {
+		setupLog.Error(fmt.Errorf("missing required Azure configuration: %v", missing), "config validation failed")
 		os.Exit(1)
 	}
📝 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
if subscriptionID == "" || resourceGroup == "" || vnetName == "" {
setupLog.Error(fmt.Errorf("missing config"), "subscription-id, resource-group and vnet-name are required")
os.Exit(1)
}
var missing []string
if subscriptionID == "" {
missing = append(missing, "subscription-id")
}
if resourceGroup == "" {
missing = append(missing, "resource-group")
}
if vnetName == "" {
missing = append(missing, "vnet-name")
}
if len(missing) > 0 {
setupLog.Error(fmt.Errorf("missing required Azure configuration: %v", missing), "config validation failed")
os.Exit(1)
}
🤖 Prompt for AI Agents
In services/dis-pgsql-operator/cmd/main.go around lines 236 to 239, the
validation error message is generic; change the logic to collect which of
subscriptionID, resourceGroup, and vnetName are empty, build a concise "missing
config" error that includes the names of the missing parameters (e.g., join them
into a comma-separated string), and pass that detailed message to setupLog.Error
(or create an error with fmt.Errorf including the missing list) before
os.Exit(1) so the log clearly identifies exactly which parameter(s) are missing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this suggestion sounds too verbose to me, I'll leave it up to you to decide @tjololo

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@sduranc sduranc self-assigned this Nov 28, 2025
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.

1 participant