Skip to content

Commit 06a0da3

Browse files
authored
🤖 fix: add CoderTemplate.spec.files to aggregated OpenAPI (#81)
## Summary Expose `CoderTemplate.spec.files` in the aggregated API server OpenAPI schema so server-side apply (SSA) can manage template source updates. ## Background SSA relies on the server’s OpenAPI schema/type info. The aggregated API server’s manually-built OpenAPI definition for `CoderTemplate` omitted `spec.files`, which prevents apply from properly managing that field. ## Implementation - Added `spec.files` to the `CoderTemplate` OpenAPI schema as a typed map (`type: object` + `additionalProperties: { type: string }`). - Marked the map as `x-kubernetes-map-type: atomic` to avoid per-file ownership churn in managedFields. - Added a unit test to assert `spec.files` is present and correctly typed in OpenAPI. ## Validation - `make test` - `make build` - `make verify-vendor` - `make lint` ## Risks Low. This only changes the OpenAPI schema served by the aggregated API server and adds coverage. --- _Generated with `mux` • Model: `openai:gpt-5.2` • Thinking: `xhigh` • Cost: `$4.71`_ <!-- mux-attribution: model=openai:gpt-5.2 thinking=xhigh costs=4.71 -->
1 parent ccfe0eb commit 06a0da3

2 files changed

Lines changed: 52 additions & 0 deletions

File tree

‎internal/app/apiserverapp/apiserverapp.go‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,20 @@ func getOpenAPIDefinitions(_ openapicommon.ReferenceCallback) map[string]openapi
349349
dateTimeSchema := spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{"string"}, Format: "date-time"}}
350350
int64Schema := spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{"integer"}, Format: "int64"}}
351351
stringSchema := spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{"string"}}}
352+
filesSchema := spec.Schema{
353+
VendorExtensible: spec.VendorExtensible{
354+
Extensions: spec.Extensions{
355+
"x-kubernetes-map-type": "atomic",
356+
},
357+
},
358+
SchemaProps: spec.SchemaProps{
359+
Type: []string{"object"},
360+
AdditionalProperties: &spec.SchemaOrBool{
361+
Allows: true,
362+
Schema: &stringSchema,
363+
},
364+
},
365+
}
352366

353367
workspaceSchema := spec.Schema{
354368
SchemaProps: spec.SchemaProps{
@@ -399,6 +413,7 @@ func getOpenAPIDefinitions(_ openapicommon.ReferenceCallback) map[string]openapi
399413
"displayName": stringSchema,
400414
"description": stringSchema,
401415
"icon": stringSchema,
416+
"files": filesSchema,
402417
"running": boolSchema,
403418
},
404419
},

‎internal/app/apiserverapp/apiserverapp_test.go‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"k8s.io/apimachinery/pkg/runtime/schema"
1515
"k8s.io/apimachinery/pkg/runtime/serializer"
1616
genericoptions "k8s.io/apiserver/pkg/server/options"
17+
openapiutil "k8s.io/kube-openapi/pkg/util"
1718

1819
aggregationv1alpha1 "github.com/coder/coder-k8s/api/aggregation/v1alpha1"
1920
coderhelper "github.com/coder/coder-k8s/internal/aggregated/coder"
@@ -39,6 +40,42 @@ func TestNewSchemeRegistersAggregationKinds(t *testing.T) {
3940
}
4041
}
4142

43+
func TestOpenAPIDefinitionsIncludeTemplateFiles(t *testing.T) {
44+
t.Helper()
45+
46+
defs := getOpenAPIDefinitions(nil)
47+
templateDefinitionName := openapiutil.GetCanonicalTypeName(&aggregationv1alpha1.CoderTemplate{})
48+
49+
def, ok := defs[templateDefinitionName]
50+
if !ok {
51+
t.Fatalf("expected OpenAPI definition for %s", templateDefinitionName)
52+
}
53+
54+
templateSpecSchema, ok := def.Schema.Properties["spec"]
55+
if !ok {
56+
t.Fatal("expected template schema to include spec")
57+
}
58+
59+
filesSchema, ok := templateSpecSchema.Properties["files"]
60+
if !ok {
61+
t.Fatal("expected template schema to include spec.files")
62+
}
63+
64+
if got := filesSchema.Type; len(got) != 1 || got[0] != "object" {
65+
t.Fatalf("expected spec.files type [object], got %v", got)
66+
}
67+
if filesSchema.AdditionalProperties == nil || filesSchema.AdditionalProperties.Schema == nil {
68+
t.Fatal("expected spec.files additionalProperties schema")
69+
}
70+
if got := filesSchema.AdditionalProperties.Schema.Type; len(got) != 1 || got[0] != "string" {
71+
t.Fatalf("expected spec.files additionalProperties type [string], got %v", got)
72+
}
73+
74+
if got, ok := filesSchema.Extensions["x-kubernetes-map-type"]; !ok || got != "atomic" {
75+
t.Fatalf("expected spec.files x-kubernetes-map-type atomic, got %v", got)
76+
}
77+
}
78+
4279
func TestInstallAPIGroupRegistersDiscovery(t *testing.T) {
4380
t.Helper()
4481

0 commit comments

Comments
 (0)