Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ run = "controller-gen object:headerFile='../hack/generators/boilerplate.go.txt'

[tasks.test-crdsvalidation]
description = "Run CRD validation tests"
dir = "./{{env.DIR}}"
# Update test paths below if more API groups are added
run = '''
export KUBEBUILDER_ASSETS=$(setup-envtest use -p path)
Expand Down
1 change: 1 addition & 0 deletions api/x-konnect/v1alpha1/konnecteventcontrolplane_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type KonnectEventControlPlaneAPISpec struct {
//
//
// +optional
// +kubebuilder:validation:MaxProperties=50
Labels Labels `json:"labels,omitempty"`

// The minimum runtime version supported by the API.
Expand Down
1 change: 1 addition & 0 deletions api/x-konnect/v1alpha1/portal_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ type PortalAPISpec struct {
//
//
// +optional
// +kubebuilder:validation:MaxProperties=50
Labels LabelsUpdate `json:"labels,omitempty"`

// The name of the portal, used to distinguish it from other portals.
Expand Down
23 changes: 21 additions & 2 deletions api/x-konnect/v1alpha1/schema_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ type CreateDcrProviderRequestAuth0 struct {
//
//
// +optional
// +kubebuilder:validation:MaxProperties=50
Labels Labels `json:"labels,omitempty"`
// The name of the DCR provider.
// This is used to identify the DCR provider in the Konnect UI.
Expand Down Expand Up @@ -213,6 +214,7 @@ type CreateDcrProviderRequestAzureAd struct {
//
//
// +optional
// +kubebuilder:validation:MaxProperties=50
Labels Labels `json:"labels,omitempty"`
// The name of the DCR provider.
// This is used to identify the DCR provider in the Konnect UI.
Expand Down Expand Up @@ -260,6 +262,7 @@ type CreateDcrProviderRequestCurity struct {
//
//
// +optional
// +kubebuilder:validation:MaxProperties=50
Labels Labels `json:"labels,omitempty"`
// The name of the DCR provider.
// This is used to identify the DCR provider in the Konnect UI.
Expand Down Expand Up @@ -306,6 +309,7 @@ type CreateDcrProviderRequestHTTP struct {
//
//
// +optional
// +kubebuilder:validation:MaxProperties=50
Labels Labels `json:"labels,omitempty"`
// The name of the DCR provider.
// This is used to identify the DCR provider in the Konnect UI.
Expand Down Expand Up @@ -352,6 +356,7 @@ type CreateDcrProviderRequestOkta struct {
//
//
// +optional
// +kubebuilder:validation:MaxProperties=50
Labels Labels `json:"labels,omitempty"`
// The name of the DCR provider.
// This is used to identify the DCR provider in the Konnect UI.
Expand Down Expand Up @@ -459,12 +464,26 @@ type GatewayDescription string
// GatewayName The name of the Gateway.
type GatewayName string

// LabelsValue is the value type for Labels.
//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=63
// +kubebuilder:validation:Pattern=`^[a-z0-9A-Z]{1}([a-z0-9A-Z-._]*[a-z0-9A-Z]+)?$`
type LabelsValue string

// Labels store metadata of an entity that can be used for filtering an entity
// list or for searching across entity types.
//
// Keys must be of length 1-63 characters, and cannot start with "kong",
// "konnect", "mesh", "kic", or "_".
type Labels map[string]string
type Labels map[string]LabelsValue

// LabelsUpdateValue is the value type for LabelsUpdate.
//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=63
// +kubebuilder:validation:Pattern=`^[a-z0-9A-Z]{1}([a-z0-9A-Z-._]*[a-z0-9A-Z]+)?$`
type LabelsUpdateValue string

// LabelsUpdate Labels store metadata of an entity that can be used for
// filtering an entity list or for searching across entity types.
Expand All @@ -473,7 +492,7 @@ type Labels map[string]string
//
// Keys must be of length 1-63 characters, and cannot start with "kong",
// "konnect", "mesh", "kic", or "_".
type LabelsUpdate map[string]string
type LabelsUpdate map[string]LabelsUpdateValue

// MinRuntimeVersion The minimum runtime version supported by the API.
// This is the lowest version of the data plane
Expand Down
20 changes: 20 additions & 0 deletions config/crd/kong-operator/x-konnect.konghq.com_dcrproviders.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ spec:
type: string
labels:
additionalProperties:
description: LabelsValue is the value type for Labels.
maxLength: 63
minLength: 1
pattern: ^[a-z0-9A-Z]{1}([a-z0-9A-Z-._]*[a-z0-9A-Z]+)?$
type: string
description: |-
Labels store metadata of an entity that can be used for filtering an entity
Expand Down Expand Up @@ -178,6 +182,10 @@ spec:
type: string
labels:
additionalProperties:
description: LabelsValue is the value type for Labels.
maxLength: 63
minLength: 1
pattern: ^[a-z0-9A-Z]{1}([a-z0-9A-Z-._]*[a-z0-9A-Z]+)?$
type: string
description: |-
Labels store metadata of an entity that can be used for filtering an entity
Expand Down Expand Up @@ -244,6 +252,10 @@ spec:
type: string
labels:
additionalProperties:
description: LabelsValue is the value type for Labels.
maxLength: 63
minLength: 1
pattern: ^[a-z0-9A-Z]{1}([a-z0-9A-Z-._]*[a-z0-9A-Z]+)?$
type: string
description: |-
Labels store metadata of an entity that can be used for filtering an entity
Expand Down Expand Up @@ -339,6 +351,10 @@ spec:
type: string
labels:
additionalProperties:
description: LabelsValue is the value type for Labels.
maxLength: 63
minLength: 1
pattern: ^[a-z0-9A-Z]{1}([a-z0-9A-Z-._]*[a-z0-9A-Z]+)?$
type: string
description: |-
Labels store metadata of an entity that can be used for filtering an entity
Expand Down Expand Up @@ -396,6 +412,10 @@ spec:
type: string
labels:
additionalProperties:
description: LabelsValue is the value type for Labels.
maxLength: 63
minLength: 1
pattern: ^[a-z0-9A-Z]{1}([a-z0-9A-Z-._]*[a-z0-9A-Z]+)?$
type: string
description: |-
Labels store metadata of an entity that can be used for filtering an entity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ spec:
type: string
labels:
additionalProperties:
description: LabelsValue is the value type for Labels.
maxLength: 63
minLength: 1
pattern: ^[a-z0-9A-Z]{1}([a-z0-9A-Z-._]*[a-z0-9A-Z]+)?$
type: string
description: |-
Labels store metadata of an entity that can be used for filtering an entity
Expand Down
4 changes: 4 additions & 0 deletions config/crd/kong-operator/x-konnect.konghq.com_portals.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ spec:
type: string
labels:
additionalProperties:
description: LabelsUpdateValue is the value type for LabelsUpdate.
maxLength: 63
minLength: 1
pattern: ^[a-z0-9A-Z]{1}([a-z0-9A-Z-._]*[a-z0-9A-Z]+)?$
type: string
description: |-
Labels store metadata of an entity that can be used for filtering an entity
Expand Down
19 changes: 19 additions & 0 deletions crd-from-oas/pkg/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,25 @@ func (g *Generator) generateSchemaTypes(refs map[string]bool, parsed *parser.Par
fmt.Fprintf(&buf, "\t// %sDisabled sets %s as disabled.\n", goName, goName)
fmt.Fprintf(&buf, "\t%sDisabled %s = \"Disabled\"\n", goName, goName)
fmt.Fprintf(&buf, ")\n\n")

case schema.AdditionalProperties != nil:
// Map type with value constraints: generate a dedicated value type
// with native kubebuilder markers, then define the map using it.
valueTypeName := refName + "Value"
valueBaseType := propertyToGoBaseType(schema.AdditionalProperties)

fmt.Fprintf(&buf, "// %s is the value type for %s.\n", valueTypeName, refName)
if markers := valueTypeMarkers(schema.AdditionalProperties); len(markers) > 0 {
buf.WriteString("//\n")
for _, marker := range markers {
fmt.Fprintf(&buf, "// %s\n", marker)
}
}
fmt.Fprintf(&buf, "type %s %s\n\n", valueTypeName, valueBaseType)

buf.WriteString(comment)
fmt.Fprintf(&buf, "type %s map[string]%s\n\n", refName, valueTypeName)

default:
// Generate based on the schema's actual type
buf.WriteString(comment)
Expand Down
167 changes: 83 additions & 84 deletions crd-from-oas/pkg/generator/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,61 +11,6 @@ import (
"github.com/kong/kong-operator/v2/crd-from-oas/pkg/parser"
)

func TestObjectRefTypeName(t *testing.T) {
tests := []struct {
name string
commonTypes *config.CommonTypesConfig
want string
}{
{
name: "nil commonTypes returns ObjectRef",
commonTypes: nil,
want: "ObjectRef",
},
{
name: "generate true returns ObjectRef",
commonTypes: &config.CommonTypesConfig{
ObjectRef: &config.ObjectRefConfig{
Generate: new(true),
},
},
want: "ObjectRef",
},
{
name: "import with alias returns qualified name",
commonTypes: &config.CommonTypesConfig{
ObjectRef: &config.ObjectRefConfig{
Import: &config.ImportConfig{
Path: "github.com/kong/kong-operator/v2/api/common/v1alpha1",
Alias: "commonv1alpha1",
},
},
},
want: "commonv1alpha1.ObjectRef",
},
{
name: "import without alias uses last path segment",
commonTypes: &config.CommonTypesConfig{
ObjectRef: &config.ObjectRefConfig{
Import: &config.ImportConfig{
Path: "github.com/kong/kong-operator/v2/api/common/v1alpha1",
},
},
},
want: "v1alpha1.ObjectRef",
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
g := NewGenerator(Config{
CommonTypes: tc.commonTypes,
})
assert.Equal(t, tc.want, g.objectRefTypeName())
})
}
}

func TestGoType_ObjectRef(t *testing.T) {
t.Run("without import uses ObjectRef", func(t *testing.T) {
g := NewGenerator(Config{})
Expand Down Expand Up @@ -809,35 +754,6 @@ func TestGenerateSDKOps_NormalizesBooleanFields(t *testing.T) {
assert.NotContains(t, content, "if err := normalizeSDKOpsBoolFields(payload); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to normalize PortalAPISpec for CreatePortal: %w\", err)")
}

func TestGenerateSDKOpsTest_AssertsNormalizedPayload(t *testing.T) {
g := NewGenerator(Config{APIVersion: "v1alpha1"})
schema := &parser.Schema{
Properties: []*parser.Property{
{
Name: "name",
Type: "string",
},
{
Name: "rbac_enabled",
Type: "boolean",
},
},
}
opsConfig := &config.EntityOpsConfig{
Ops: map[string]*config.OpConfig{
"create": {
Path: "github.com/Kong/sdk-konnect-go/models/components.CreatePortal",
},
},
}

content, err := g.generateSDKOpsTest("Portal", schema, opsConfig)
require.NoError(t, err)
assert.Contains(t, content, `RBACEnabled: "Enabled"`)
assert.Contains(t, content, `require.Equal(t, true, payload["rbac_enabled"])`)
assert.Contains(t, content, `require.Equal(t, "test-value", payload["name"])`)
}

func TestParseSDKTypePath(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -893,3 +809,86 @@ func TestParseSDKTypePath(t *testing.T) {
})
}
}

func TestGenerateSchemaTypes_MapWithValueTypes(t *testing.T) {
g := NewGenerator(Config{
APIVersion: "v1alpha1",
})

parsed := &parser.ParsedSpec{
Schemas: map[string]*parser.Schema{
"Labels": {
Name: "Labels",
Description: "Labels store metadata.",
Type: "object",
MaxProperties: func() *int64 { v := int64(50); return &v }(),
AdditionalProperties: &parser.Property{
Type: "string",
MinLength: func() *int64 { v := int64(1); return &v }(),
MaxLength: func() *int64 { v := int64(63); return &v }(),
Pattern: `^[a-z0-9A-Z]+$`,
},
},
"LabelsUpdate": {
Name: "LabelsUpdate",
Description: "LabelsUpdate store metadata.",
Type: "object",
AdditionalProperties: &parser.Property{
Type: "string",
MinLength: func() *int64 { v := int64(1); return &v }(),
MaxLength: func() *int64 { v := int64(63); return &v }(),
Pattern: `^[a-z0-9A-Z]+$`,
},
},
},
}

refs := map[string]bool{
"Labels": true,
"LabelsUpdate": true,
}

content := g.generateSchemaTypes(refs, parsed)

// Labels should generate a value type with native markers, then a map type using it
assert.Contains(t, content, "type LabelsValue string")
assert.Contains(t, content, "type Labels map[string]LabelsValue")
assert.Contains(t, content, "+kubebuilder:validation:MinLength=1")
assert.Contains(t, content, "+kubebuilder:validation:MaxLength=63")
assert.Contains(t, content, "+kubebuilder:validation:Pattern=`^[a-z0-9A-Z]+$`")

// LabelsUpdate should also generate a value type
assert.Contains(t, content, "type LabelsUpdateValue string")
assert.Contains(t, content, "type LabelsUpdate map[string]LabelsUpdateValue")

// No CEL XValidation rules or MaxProperties on the type (goes on the field)
assert.NotContains(t, content, "XValidation")
assert.NotContains(t, content, "MaxProperties")
}

func TestGenerateSchemaTypes_NoValueTypeForNonMapTypes(t *testing.T) {
g := NewGenerator(Config{
APIVersion: "v1alpha1",
})

parsed := &parser.ParsedSpec{
Schemas: map[string]*parser.Schema{
"GatewayName": {
Name: "GatewayName",
Description: "The name of the Gateway.",
Type: "string",
},
},
}

refs := map[string]bool{
"GatewayName": true,
}

content := g.generateSchemaTypes(refs, parsed)

assert.Contains(t, content, "type GatewayName string")
assert.NotContains(t, content, "Value")
assert.NotContains(t, content, "XValidation")
assert.NotContains(t, content, "MaxProperties")
}
Loading
Loading