Skip to content

feat: add intOrString type alias for map[string]intOrString annotations - #25

Merged
myasnikovdaniil merged 3 commits into
mainfrom
feat/intOrString-type-alias
May 21, 2026
Merged

myasnikovdaniil merged 3 commits into
mainfrom
feat/intOrString-type-alias

Conversation

@IvanHunters

@IvanHunters IvanHunters commented May 19, 2026 •

Copy link
Copy Markdown

Summary

  • Add intOrString as a new primitive type alias in both openapi and readme packages
  • Resolves to intstr.IntOrString (k8s.io/apimachinery/pkg/util/intstr) in generated Go code
  • Produces anyOf: [{type: integer}, {type: string}] + x-kubernetes-int-or-string: true in JSON Schema

Motivation

Some Helm chart parameters (e.g. PostgreSQL server parameters like max_connections) are naturally expressed as integers, but users sometimes supply them without quoting. The current {map[string]string} annotation rejects integer values at schema validation time, causing HelmRelease failures for existing clusters that have unquoted integers in their Application spec.

With {map[string]intOrString}, the schema accepts both max_connections: "100" (string) and max_connections: 100 (integer), while the Helm template already handles coercion via {{ $value | toString | quote }}.

Test plan

  • Unit test TestResolveSpecialAliases extended to verify intOrString → intstr.IntOrString resolution and import
  • New integration test TestIntOrStringSchemaGeneration verifies end-to-end schema generation produces x-kubernetes-int-or-string: true and anyOf
  • All existing tests pass (go test ./...)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Recognizes Kubernetes intOrString as a primitive for generation; generated code references the appropriate Go type and import.
    • Generated schemas mark intOrString maps with the x-kubernetes-int-or-string marker and represent map values as integer|string alternatives.
  • Documentation

    • README updated to list intOrString in supported value types and describe its mapping.
  • Tests

    • Added and expanded tests to validate intOrString handling, code references, README rendering, and schema output.

Review Change Stack

Add intOrString as a primitive type alias that resolves to
intstr.IntOrString (k8s.io/apimachinery/pkg/util/intstr). This allows
map[string]intOrString annotations in values.yaml, which generate a
JSON Schema with anyOf: [{type: integer}, {type: string}] for the
additionalProperties. This is needed for PostgreSQL server parameters
that users may provide as either quoted strings ("100") or bare
integers (100).

Signed-off-by: ohotnikov.ivan <[email protected]>
@coderabbitai

coderabbitai Bot commented May 19, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ee3ad07-4eaf-433e-9841-a3bcb135f1e2

📥 Commits

Reviewing files that changed from the base of the PR and between 826befc and dcc6ced.

📒 Files selected for processing (3)
  • README.md
  • internal/readme/readme.go
  • internal/readme/readme_test.go
✅ Files skipped from review due to trivial changes (1)
  • README.md

📝 Walkthrough

Walkthrough

Adds support for the intOrString alias: registers aliasIntOrString, classifies it as a primitive, resolves it to intstr.IntOrString with the required import, and verifies behavior via updated and new tests covering codegen and JSON schema annotations.

Changes

intOrString Type Alias Support

Layer / File(s) Summary
OpenAPI intOrString type alias and resolver
internal/openapi/openapi.go
Introduces aliasIntOrString, includes it in the primitive-type classifier, and extends type resolution to map intOrString to intstr.IntOrString while registering the k8s.io/apimachinery/pkg/util/intstr import.
Schema/readme primitive handling
internal/readme/readme.go, internal/readme/readme_test.go
Adds aliasIntOrString to the primitive alias switch so intOrString is handled as a primitive in schema normalization and rendering; updates default rendering to use string-like default and adds README-rendering tests for intOrString.
Tests: resolver and end-to-end schema generation
internal/openapi/openapi_test.go
Extends TestResolveSpecialAliases to assert resolution and import registration; adds TestIntOrStringSchemaGeneration to validate generated Go code, imports, and JSON schema markers (x-kubernetes-int-or-string and anyOf with integer and string).
Docs: README supported types
README.md
Adds documentation row for the intOrString annotation token mapping to intstr.IntOrString and an OpenAPI anyOf integer/string schema.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit hops through types so bright,
intOrString now joins the light.
Imports placed and tests in play,
Code and schema find their way.
🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a new intOrString type alias for map[string]intOrString annotations, which is the core functionality across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/intOrString-type-alias

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.

IvanHunters added a commit to cozystack/cozystack that referenced this pull request May 19, 2026
…schema

Switch the postgresql.parameters field annotation from
{map[string]string} to {map[string]intOrString}, which generates a
JSON Schema with anyOf: [{type: integer}, {type: string}] plus the
x-kubernetes-int-or-string extension. This lets users supply PostgreSQL
parameters as bare integers (max_connections: 100) or quoted strings
(max_connections: "100") without triggering schema validation errors.

The Helm template already coerces any value to a string via
{{ $value | toString | quote }}, so runtime behavior is unchanged.

Bump CI to cozyvalues-gen v1.3.1, which adds intOrString alias support
(see cozystack/cozyvalues-gen#25). The generated Go type changes from
map[string]string to map[string]intstr.IntOrString; the Application spec
is stored as raw JSON so there is no runtime unmarshaling impact.

Signed-off-by: IvanHunters <[email protected]>

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for the intOrString alias, mapping it to Kubernetes' intstr.IntOrString type. Changes include updating primitive checks and resolution logic in both the OpenAPI and README generation modules, along with adding relevant unit tests. Feedback suggests extending the implementation to handle default value emission in emitField for Go code generation and providing specific default values in defaultValueForType for README documentation.

aliasResources = "resources"
aliasRequest = "request"
aliasLimit = "limit"
aliasIntOrString = "intOrString"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The intOrString alias should also be handled in the emitField function (around line 812) to ensure that empty string defaults are correctly emitted as +kubebuilder:default:="" in the generated Go code. Currently, it only handles string and quantity explicitly.

Comment thread internal/readme/readme.go
aliasTime = "time"
aliasObject = "object"
aliasEmptyObject = "emptyobject"
aliasIntOrString = "intOrString"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The intOrString alias should be added to the defaultValueForType function (around line 1097) to ensure it provides a sensible default value (such as "" or 0) in the generated README table. Without this, it will fall back to the default {} which is incorrect for a type that resolves to a scalar integer or string.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/openapi/openapi_test.go (1)

432-434: ⚡ Quick win

Assert anyOf/x-kubernetes-int-or-string at the exact schema path.

Current string-contains checks can pass on unrelated schema locations; assert on properties.postgresql.properties.parameters.additionalProperties instead.

Suggested test hardening
 	schemaBytes, err := os.ReadFile(outfile)
 	require.NoError(t, err)
-	schema := string(schemaBytes)
-	require.Contains(t, schema, `"x-kubernetes-int-or-string": true`)
-	require.Contains(t, schema, `"anyOf"`)
+
+	var schema map[string]any
+	require.NoError(t, json.Unmarshal(schemaBytes, &schema))
+
+	props := schema["properties"].(map[string]any)
+	pg := props["postgresql"].(map[string]any)
+	pgProps := pg["properties"].(map[string]any)
+	parameters := pgProps["parameters"].(map[string]any)
+	additional := parameters["additionalProperties"].(map[string]any)
+
+	require.Equal(t, true, additional["x-kubernetes-int-or-string"])
+	anyOf, ok := additional["anyOf"].([]any)
+	require.True(t, ok)
+	require.NotEmpty(t, anyOf)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/openapi/openapi_test.go` around lines 432 - 434, The current
string-contains checks are too coarse; instead unmarshal schemaBytes (or use a
JSON-path helper) and navigate to the exact node at
properties.postgresql.properties.parameters.additionalProperties, then assert
that that node includes the keys/values `"x-kubernetes-int-or-string": true` and
an `"anyOf"` entry. Concretely: replace the two require.Contains(t, schema, ...)
calls with code that json.Unmarshal(schemaBytes, &doc) (or use gjson.GetBytes)
then locate
doc["properties"]["postgresql"]["properties"]["parameters"]["additionalProperties"]
and use require assertions on that map to confirm it has the
x-kubernetes-int-or-string boolean and an anyOf array. Ensure you reference the
existing variables schemaBytes/schema in the test and keep assertions specific
to that node.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/readme/readme.go`:
- Around line 873-874: The function defaultValueForType currently falls through
to an object default for types even though intOrString is marked primitive
alongside aliasQuantity, aliasDuration, aliasTime, aliasObject,
aliasEmptyObject, aliasIntOrString; add a scalar-default branch for the
intOrString case inside defaultValueForType so it returns an appropriate scalar
default (e.g., empty string or zero depending on desired README semantics)
instead of `{}`; locate the switch/if that handles
aliasQuantity/aliasDuration/.../aliasIntOrString and insert the
intOrString-specific return path there.

---

Nitpick comments:
In `@internal/openapi/openapi_test.go`:
- Around line 432-434: The current string-contains checks are too coarse;
instead unmarshal schemaBytes (or use a JSON-path helper) and navigate to the
exact node at properties.postgresql.properties.parameters.additionalProperties,
then assert that that node includes the keys/values
`"x-kubernetes-int-or-string": true` and an `"anyOf"` entry. Concretely: replace
the two require.Contains(t, schema, ...) calls with code that
json.Unmarshal(schemaBytes, &doc) (or use gjson.GetBytes) then locate
doc["properties"]["postgresql"]["properties"]["parameters"]["additionalProperties"]
and use require assertions on that map to confirm it has the
x-kubernetes-int-or-string boolean and an anyOf array. Ensure you reference the
existing variables schemaBytes/schema in the test and keep assertions specific
to that node.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 94e06504-0a9a-4038-a0af-cc5095e8a276

📥 Commits

Reviewing files that changed from the base of the PR and between af0052b and 14575d6.

📒 Files selected for processing (3)
  • internal/openapi/openapi.go
  • internal/openapi/openapi_test.go
  • internal/readme/readme.go

Comment thread internal/readme/readme.go Resolved
@IvanHunters

Copy link
Copy Markdown
Author

Closing: the upstream cozystack PR was simplified to a direct schema edit without requiring generator changes.

Address CodeRabbit review on #25: replace coarse string-contains
assertions with json.Unmarshal navigation to
properties.postgresql.properties.parameters.additionalProperties,
then assert x-kubernetes-int-or-string: true and that anyOf contains
exactly {type:integer} and {type:string}.

Signed-off-by: ohotnikov.ivan <[email protected]>
@IvanHunters

Copy link
Copy Markdown
Author

Reopening — the simplification path didn't work.

The simplified approach (direct schema edit in cozystack/cozystack#2687, no generator change) fails because make generate regenerates values.schema.json and postgres-rd/cozyrds/postgres.yaml from values.yaml, clobbering the manual anyOf back to {type: string}. Reproduced empirically — same scenario as the original 53caea3 revert in cozystack/cozystack#2466 that cozystack/cozystack#2597 flags.

Generator-level support (this PR) is the durable fix. Pushed an additional test commit addressing the CodeRabbit nitpick: the assertion now json.Unmarshals the schema and checks the exact properties.postgresql.properties.parameters.additionalProperties node, requiring x-kubernetes-int-or-string: true and anyOf containing exactly {type: integer} and {type: string}.

@sircthulhu

Copy link
Copy Markdown

Looks good overall, the alias is wired in symmetrically to quantity/duration/time/object, and the schema test asserts the real invariant (x-kubernetes-int-or-string: true + anyOf), not a proxy. Two things worth addressing before merge though:

  1. README is out of date. The "Supported value types" table doesn't mention intOrString. Without that, no one will discover the feature — please add a row with a map[string]intOrString example (PostgreSQL parameters is the obvious one).

  2. defaultValueForType("intOrString") falls through to {}. For a scalar ## @param {intOrString} foo with no value, the rendered README will show {}, which is misleading (it's not an object). For the map case the default is fine, but since the type is now in isPrimitive, the scalar branch should return something sensible (null, 0, or "").

Unrelated, but spotted while reading: in readme.go:797-803 both branches of the map[...] handling return the same "map[string]object" — looks like dead code that's currently masked by the early isPrimitive(base) return. Not introduced here, just flagging.

Address review feedback on #25:

- defaultValueForType now returns "" for scalar intOrString instead of
  falling through to the object default {}. Symmetric with string and
  quantity, both of which render as empty string when no value is set.
- README "Supported value types" table gains an intOrString row with
  PostgreSQL-style examples so the feature is discoverable.
- Add unit + end-to-end tests covering scalar intOrString rendering.

The unrelated dead-code observation in readme.go:797-803 (both map[...]
branches returning the same map[string]object) is left for a separate
change since it pre-dates this PR.

Signed-off-by: ohotnikov.ivan <[email protected]>
@IvanHunters

Copy link
Copy Markdown
Author

Addressed both blockers:

  1. defaultValueForType("intOrString") now returns "" (symmetric with string and quantity) — scalar ## @param {intOrString} foo renders as empty string instead of {}.
  2. README.md "Supported value types" table gains an intOrString row with PostgreSQL-style examples.

Added unit + end-to-end tests covering scalar rendering.

Re: readme.go:797-803 dead-code observation — left for a separate change since it pre-dates this PR.

@myasnikovdaniil
myasnikovdaniil merged commit 9de5dfc into main May 21, 2026
2 checks passed
myasnikovdaniil added a commit to cozystack/cozystack that referenced this pull request May 21, 2026
…erator

Ivan's original schema edits were correct in intent but lived in the
generated artifacts (values.schema.json and the embedded openAPISchema
in postgres-rd/cozyrds/postgres.yaml). `make generate` reverts them
because values.yaml's `@field {map[string]string}` annotation is the
source of truth -- same regeneration trap that caught #2466 (issue #2597).

cozyvalues-gen v1.4.0 (cozystack/cozyvalues-gen#25, merged 2026-05-21)
adds an `intOrString` type alias for map values that emits
`additionalProperties.anyOf: [integer, string]` plus
`x-kubernetes-int-or-string: true`. Move the change to source by
flipping the annotation, then regenerate:

- packages/apps/postgres/values.yaml: `@field {map[string]intOrString}`
  with updated description noting integer values are accepted.
- packages/apps/postgres/values.schema.json: regenerated -- now carries
  anyOf + x-kubernetes-int-or-string: true.
- packages/system/postgres-rd/cozyrds/postgres.yaml: embedded
  openAPISchema regenerated to match.
- packages/apps/postgres/README.md: regenerated type column.
- api/apps/v1alpha1/postgresql/types.go: Parameters is now
  `map[string]intstr.IntOrString`.

The result survives future regenerations. The template's
`{{ \$value | toString | quote }}` already coerces both inputs to
quoted strings, verified end-to-end with helm template against both
`max_connections: 100` (int) and `max_connections: "100"` (string) --
identical render output. Dangerous-param block list still rejects
archive_cleanup_command and recovery_end_command additions from the
prior commit.

Co-Authored-By: IvanHunters <[email protected]>
Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit to cozystack/cozystack that referenced this pull request May 21, 2026
…erator

Ivan's original schema edits were correct in intent but lived in the
generated artifacts (values.schema.json and the embedded openAPISchema
in postgres-rd/cozyrds/postgres.yaml). `make generate` reverts them
because values.yaml's `@field {map[string]string}` annotation is the
source of truth -- same regeneration trap that caught #2466 (issue #2597).

cozyvalues-gen v1.4.0 (cozystack/cozyvalues-gen#25, merged 2026-05-21)
adds an `intOrString` type alias for map values that emits
`additionalProperties.anyOf: [integer, string]` plus
`x-kubernetes-int-or-string: true`. Move the change to source by
flipping the annotation, then regenerate:

- packages/apps/postgres/values.yaml: `@field {map[string]intOrString}`
  with updated description noting integer values are accepted.
- packages/apps/postgres/values.schema.json: regenerated -- now carries
  anyOf + x-kubernetes-int-or-string: true.
- packages/system/postgres-rd/cozyrds/postgres.yaml: embedded
  openAPISchema regenerated to match.
- packages/apps/postgres/README.md: regenerated type column.
- api/apps/v1alpha1/postgresql/types.go: Parameters is now
  `map[string]intstr.IntOrString`.

The result survives future regenerations. The template's
`{{ \$value | toString | quote }}` already coerces both inputs to
quoted strings, verified end-to-end with helm template against both
`max_connections: 100` (int) and `max_connections: "100"` (string) --
identical render output. Dangerous-param block list still rejects
archive_cleanup_command and recovery_end_command additions from the
prior commit.

Co-Authored-By: IvanHunters <[email protected]>
Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit to cozystack/cozystack that referenced this pull request May 22, 2026
…ema (#2687)

## What this PR does

PostgreSQL server parameters like `max_connections` are natural
integers,
but the chart's schema declared them as `{type: string}` only. When
users
supply bare integers (e.g. `max_connections: 100`) — either directly in
Application CR specs or via the UI — Helm rejects the values at schema
validation time before template rendering, causing HelmRelease failures.

The Helm template already handles both types via `{{ $value | toString |
quote }}`,
so the fix is to teach the schema to accept both.

### Approach

The schema files (`values.schema.json` and the embedded `openAPISchema`
in `postgres-rd/cozyrds/postgres.yaml`) are generated from `values.yaml`
annotations via `cozyvalues-gen`. Hand-editing them does not survive
`make generate` — same regeneration trap that caught #2466 (issue
#2597).

`cozyvalues-gen` v1.4.0 (cozystack/cozyvalues-gen#25) adds an
`intOrString` type alias for map values that emits
`additionalProperties.anyOf: [integer, string]` plus the Kubernetes
`x-kubernetes-int-or-string: true` marker.

### Changes

- `packages/apps/postgres/values.yaml`: annotation flipped from
  `@field {map[string]string}` to `@field {map[string]intOrString}`,
  description updated to note integer values are accepted.
- Regenerated artifacts: `values.schema.json`, embedded `openAPISchema`
  in `packages/system/postgres-rd/cozyrds/postgres.yaml`,
`packages/apps/postgres/README.md`, and
`api/apps/v1alpha1/postgresql/types.go`
  (Parameters is now `map[string]intstr.IntOrString`).
- `packages/apps/postgres/templates/db.yaml`: `$dangerousParams` list
  extended with `archive_cleanup_command` and `recovery_end_command`
  (same RCE surface as `archive_command` / `restore_command`).

### Verification

- `helm template` with `max_connections: 200` (int) and
`max_connections: "200"`
  (string) produce identical render output.
- Dangerous-param block list rejects `archive_cleanup_command` and
  `recovery_end_command` with the expected `SECURITY:` error.
- `make test` in `packages/apps/postgres/`: 13/13 pass.
- `make generate` is idempotent — schema files don't drift on regen.

### Release note

```release-note
fix(postgres): accept integer values for postgresql.parameters to prevent HelmRelease failures when parameters are supplied as unquoted integers; also block archive_cleanup_command and recovery_end_command (RCE surface).
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* PostgreSQL parameters now accept integer or string values; integers
are automatically coerced to strings.

* **Security**
* Enhanced parameter filtering blocks additional dangerous settings:
archive_cleanup_command and recovery_end_command.

* **Documentation**
* Updated docs and charts to describe int-or-string parameter support
and revised blocked-parameter guidance.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2687?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.

3 participants