From bd2adf7b67472395bb72e04bd2ec3740fb16d6d2 Mon Sep 17 00:00:00 2001 From: Artem Kuleshov Date: Fri, 14 Aug 2026 13:31:30 +0300 Subject: [PATCH 1/2] [addon-operator] inject registry as plain values to fix CEL validation Signed-off-by: Artem Kuleshov --- .../models/modules/values_storage.go | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pkg/module_manager/models/modules/values_storage.go b/pkg/module_manager/models/modules/values_storage.go index eceaf4127..06cec118a 100644 --- a/pkg/module_manager/models/modules/values_storage.go +++ b/pkg/module_manager/models/modules/values_storage.go @@ -52,6 +52,26 @@ type Registry struct { CA string `json:"ca,omitempty" yaml:"ca,omitempty"` } +// toValues renders the registry as plain JSON types, keeping the struct's own JSON shape. +// Values are walked natively by CEL (structpb) and OpenAPI validation, both fail on a Go struct. +func (r *Registry) toValues() map[string]any { + if r == nil { + return nil + } + + values := map[string]any{ + "base": r.Base, + "dockercfg": r.DockerCfg, + "scheme": r.Scheme, + } + + if r.CA != "" { + values["ca"] = r.CA + } + + return values +} + // NewValuesStorage build a new storage for module values // // staticValues - values from /modules//values.yaml, which couldn't be reloaded during the runtime @@ -212,7 +232,7 @@ func (vs *ValuesStorage) InjectRegistryValue(registry *Registry) { vs.staticValues = utils.Values{} } - vs.staticValues["registry"] = registry + vs.staticValues["registry"] = registry.toValues() _ = vs.calculateResultValues() } From f1a273e8704677d5a23b4341fae59d74ca8cf896 Mon Sep 17 00:00:00 2001 From: Artem Kuleshov Date: Fri, 14 Aug 2026 14:07:45 +0300 Subject: [PATCH 2/2] [addon-operator] add regression test for injected registry values Signed-off-by: Artem Kuleshov --- .../models/modules/values_storage_test.go | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/pkg/module_manager/models/modules/values_storage_test.go b/pkg/module_manager/models/modules/values_storage_test.go index b2757467e..21d89bd01 100644 --- a/pkg/module_manager/models/modules/values_storage_test.go +++ b/pkg/module_manager/models/modules/values_storage_test.go @@ -203,3 +203,77 @@ modulesImages: tags: {} `, v.AsString("yaml")) } + +func TestInjectRegistryValue(t *testing.T) { + // the rule sits at the root of the config schema: ExtendTransformer copies root + // extensions into the values schema, so it is evaluated against module values too + cfg := ` +type: object +default: {} +additionalProperties: false +properties: + xxx: + type: string +x-deckhouse-validations: + - expression: 'self.registry.base != ""' + message: registry.base must be set +` + + vcfg := ` +x-extend: + schema: config-values.yaml +type: object +default: {} +properties: + internal: + type: object + default: {} +` + + tests := []struct { + name string + give *Registry + want string + }{ + { + name: "empty CA is omitted", + give: &Registry{Base: "registry.example.com/d8", DockerCfg: "e30=", Scheme: "HTTPS"}, + want: ` +xxx: yyy +internal: {} +registry: + base: registry.example.com/d8 + dockercfg: e30= + scheme: HTTPS +`, + }, + { + name: "CA is kept", + give: &Registry{Base: "registry.example.com/d8", DockerCfg: "e30=", Scheme: "HTTPS", CA: "ca-content"}, + want: ` +xxx: yyy +internal: {} +registry: + base: registry.example.com/d8 + ca: ca-content + dockercfg: e30= + scheme: HTTPS +`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + valuesStorage, err := NewValuesStorage("test-module", utils.Values{"xxx": "yyy"}, []byte(cfg), []byte(vcfg)) + require.NoError(t, err) + + valuesStorage.InjectRegistryValue(tt.give) + + values := valuesStorage.GetValues(false) + // values must hold plain JSON types: CEL converts them with structpb and + // fails on a Go struct, both as the current and as the previous value + require.NoError(t, valuesStorage.validateValues(values)) + assert.YAMLEq(t, tt.want, values.AsString("yaml")) + }) + } +}