Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trigger Schema Generated Tool #6489

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ To learn more about active deprecations, we recommend checking [GitHub Discussio
- **General**: Enable OpenSSF Scorecard to enhance security practices across the project ([#5913](https://github.com/kedacore/keda/issues/5913))
- **General**: Introduce new NSQ scaler ([#3281](https://github.com/kedacore/keda/issues/3281))
- **General**: Operator flag to control patching of webhook resources certificates ([#6184](https://github.com/kedacore/keda/issues/6184))
- **General**: Trigger Schema Generated Tool ([#6345](https://github.com/kedacore/keda/issues/6345))
- **Azure Pipelines Scaler**: Introduce requireAllDemandsAndIgnoreOthers to match job demands while ignoring extras ([#5579](https://github.com/kedacore/keda/issues/5579))

#### Experimental
Expand Down
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ GOLANGCI_VERSION:=1.63.4
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec

# Scaler schema generation parameters
SCALERS_BUILDER_FILE ?= "pkg/scaling/scalers_builder.go"
SCALERS_FILES_DIR ?= "pkg/scalers"
OUTPUT_FILE_PATH ?= "schema/generated/"
OUTPUT_FILE_NAME ?= "scalers-metadata-schema"

ifneq '${VERSION}' 'main'
OUTPUT_FILE_NAME :="${OUTPUT_FILE_NAME}_v${VERSION}"
endif


##################################################
# All #
##################################################
Expand Down Expand Up @@ -266,6 +277,14 @@ set-version:
@sed -i".out" -e 's@Version[ ]*=.*@Version = "$(VERSION)"@g' ./version/version.go;
rm -rf ./version/version.go.out

.PHONY: generate-scalers-schema
generate-scalers-schema: ## Generate scalers shcema
GOBIN=$(LOCALBIN) go run ./schema/generate_scaler_schema.go --keda-version $(VERSION) --scalers-builder-file $(SCALERS_BUILDER_FILE) --scalers-files-dir $(SCALERS_FILES_DIR) --output-file-path $(OUTPUT_FILE_PATH) --output-file-name $(OUTPUT_FILE_NAME) --output-file-format both

.PHONY: verify-scalers-schema
verify-scalers-schema: ## Verify scalers shcema
./hack/verify-schema.sh

##################################################
# Deployment #
##################################################
Expand Down
77 changes: 77 additions & 0 deletions hack/verify-schema.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash

# Copyright 2025 The KEDA Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# set -o errexit
set -o nounset
set -o pipefail

SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..

SCHEMAROOT="${SCRIPT_ROOT}/schema/generated"
TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/schema"
_tmp="${SCRIPT_ROOT}/_tmp"

cleanup() {
rm -rf "${_tmp}"
}
trap "cleanup" EXIT SIGINT

# Make sure schema json file has correct format
find $SCHEMAROOT -name "*.json" | while read file; do
if jq -e . $file >/dev/null 2>&1; then
echo "Parsed JSON successfully and got something other than false/null"
else
echo "Failed to parse JSON, or got false/null from $file"
break
fi

err_line_content=$(grep -vE '"kedaVersion":.+|"schemaVersion":.+|"scalers": \[|"metadata": \[|"optional":.+|"default":.+|"canReadFromEnv":.+|"canReadFromAuth":.+|"type":.+|"name":.+|"rangeSeparator":.+|"separator":.+|"allowedValue": \[|"deprecatedAnnounce":.+|^[^:]*$' "$file")

if [ ! -z "$err_line_content" ]; then
echo "ERROR: error schema format founded: $err_line_content in $file"
exit 1
fi
done
echo "Schema json files are in correct format"

# Make sure schema yaml file has correct format
find $SCHEMAROOT -name "*.yaml" | while read file; do
err_line_content=$(grep -vE "kedaVersion:.+|schemaVersion:.+|scalers:|metadata:|optional:.+|default:.+|canReadFromEnv:.+|canReadFromAuth:.+|type:.+|name:.+|rangeSeparator:.+|separator:.+|allowedValue:|deprecatedAnnounce:.+|^[^:]*$" "$file")
if [ ! -z "$err_line_content" ]; then
echo "ERROR: error schema format founded: $err_line_content in $file"
exit 1
fi
done
echo "Schema yaml files are in correct format"

cleanup

mkdir -p "${TMP_DIFFROOT}"
cp -a "${SCHEMAROOT}"/* "${TMP_DIFFROOT}"

make generate-scalers-schema
echo "diffing ${SCHEMAROOT} against freshly generated scalers schema"
ret=0
diff -Naup "${SCHEMAROOT}" "${TMP_DIFFROOT}" || ret=$?
cp -a "${TMP_DIFFROOT}"/* "${SCHEMAROOT}"
if [[ $ret -eq 0 ]]
then
echo "${SCHEMAROOT} up to date."
else
echo "${SCHEMAROOT} is out of date. Please run 'make generate-scalers-schema'"
exit 1
fi

80 changes: 40 additions & 40 deletions pkg/scalers/scalersconfig/typed_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ const (
AuthParams ParsingOrder = "authParams"
)

// allowedParsingOrderMap is a map with set of valid parsing orders
var allowedParsingOrderMap = map[ParsingOrder]bool{
// AllowedParsingOrderMap is a map with set of valid parsing orders
var AllowedParsingOrderMap = map[ParsingOrder]bool{
TriggerMetadata: true,
ResolvedEnv: true,
AuthParams: true,
Expand All @@ -58,28 +58,28 @@ var allowedParsingOrderMap = map[ParsingOrder]bool{
// separators for field tag structure
// e.g. name=stringVal,order=triggerMetadata;resolvedEnv;authParams,optional
const (
tagSeparator = ","
tagKeySeparator = "="
tagValueSeparator = ";"
TagSeparator = ","
TagKeySeparator = "="
TagValueSeparator = ";"
)

// separators for map and slice elements
const (
elemKeyValSeparator = "="
ElemKeyValSeparator = "="
)

// field tag parameters
const (
optionalTag = "optional"
deprecatedTag = "deprecated"
deprecatedAnnounceTag = "deprecatedAnnounce"
defaultTag = "default"
orderTag = "order"
nameTag = "name"
enumTag = "enum"
exclusiveSetTag = "exclusiveSet"
rangeTag = "range"
separatorTag = "separator"
OptionalTag = "optional"
DeprecatedTag = "deprecated"
DeprecatedAnnounceTag = "deprecatedAnnounce"
DefaultTag = "default"
OrderTag = "order"
NameTag = "name"
EnumTag = "enum"
ExclusiveSetTag = "exclusiveSet"
RangeTag = "range"
SeparatorTag = "separator"
)

// Params is a struct that represents the parameter list that can be used in the keda tag
Expand Down Expand Up @@ -139,7 +139,7 @@ func (p Params) IsDeprecated() bool {

// DeprecatedMessage is a function that returns the optional deprecated message if the parameter is deprecated
func (p Params) DeprecatedMessage() string {
if p.Deprecated == deprecatedTag {
if p.Deprecated == DeprecatedTag {
return ""
}
return fmt.Sprintf(": %s", p.Deprecated)
Expand Down Expand Up @@ -219,7 +219,7 @@ func (sc *ScalerConfig) setValue(field reflect.Value, params Params) error {
}
if !exists && !(params.Optional || params.IsDeprecated()) {
if len(params.Order) == 0 {
apo := slices.Sorted(maps.Keys(allowedParsingOrderMap))
apo := slices.Sorted(maps.Keys(AllowedParsingOrderMap))
return fmt.Errorf("missing required parameter %q, no 'order' tag, provide any from %v", params.Name(), apo)
}
return fmt.Errorf("missing required parameter %q in %v", params.Name(), params.Order)
Expand Down Expand Up @@ -301,9 +301,9 @@ func setConfigValueMap(params Params, valFromConfig string, field reflect.Value)
split := splitWithSeparator(valFromConfig, params.Separator)
for _, s := range split {
s := strings.TrimSpace(s)
kv := strings.Split(s, elemKeyValSeparator)
kv := strings.Split(s, ElemKeyValSeparator)
if len(kv) != 2 {
return fmt.Errorf("expected format key%vvalue, got %q", elemKeyValSeparator, s)
return fmt.Errorf("expected format key%vvalue, got %q", ElemKeyValSeparator, s)
}
key := strings.TrimSpace(kv[0])
val := strings.TrimSpace(kv[1])
Expand Down Expand Up @@ -459,66 +459,66 @@ func (sc *ScalerConfig) configParamValue(params Params) (string, bool) {
// paramsFromTag is a function that returns the Params struct based on the field tag
func paramsFromTag(tag string, field reflect.StructField) (Params, error) {
params := Params{FieldName: field.Name}
tagSplit := strings.Split(tag, tagSeparator)
tagSplit := strings.Split(tag, TagSeparator)
for _, ts := range tagSplit {
tsplit := strings.Split(ts, tagKeySeparator)
tsplit := strings.Split(ts, TagKeySeparator)
tsplit[0] = strings.TrimSpace(tsplit[0])
switch tsplit[0] {
case optionalTag:
case OptionalTag:
if len(tsplit) == 1 {
params.Optional = true
}
if len(tsplit) > 1 {
params.Optional, _ = strconv.ParseBool(strings.TrimSpace(tsplit[1]))
}
case orderTag:
case OrderTag:
if len(tsplit) > 1 {
order := strings.Split(tsplit[1], tagValueSeparator)
order := strings.Split(tsplit[1], TagValueSeparator)
for _, po := range order {
poTyped := ParsingOrder(strings.TrimSpace(po))
if !allowedParsingOrderMap[poTyped] {
apo := slices.Sorted(maps.Keys(allowedParsingOrderMap))
if !AllowedParsingOrderMap[poTyped] {
apo := slices.Sorted(maps.Keys(AllowedParsingOrderMap))
return params, fmt.Errorf("unknown parsing order value %s, has to be one of %s", po, apo)
}
params.Order = append(params.Order, poTyped)
}
}
case nameTag:
case NameTag:
if len(tsplit) > 1 {
params.Names = strings.Split(strings.TrimSpace(tsplit[1]), tagValueSeparator)
params.Names = strings.Split(strings.TrimSpace(tsplit[1]), TagValueSeparator)
}
case deprecatedTag:
case DeprecatedTag:
if len(tsplit) == 1 {
params.Deprecated = deprecatedTag
params.Deprecated = DeprecatedTag
} else {
params.Deprecated = strings.TrimSpace(tsplit[1])
}
case deprecatedAnnounceTag:
case DeprecatedAnnounceTag:
if len(tsplit) == 1 {
params.DeprecatedAnnounce = deprecatedAnnounceTag
params.DeprecatedAnnounce = DeprecatedAnnounceTag
} else {
params.DeprecatedAnnounce = strings.TrimSpace(tsplit[1])
}
case defaultTag:
case DefaultTag:
if len(tsplit) > 1 {
params.Default = strings.TrimSpace(tsplit[1])
}
case enumTag:
case EnumTag:
if len(tsplit) > 1 {
params.Enum = strings.Split(tsplit[1], tagValueSeparator)
params.Enum = strings.Split(tsplit[1], TagValueSeparator)
}
case exclusiveSetTag:
case ExclusiveSetTag:
if len(tsplit) > 1 {
params.ExclusiveSet = strings.Split(tsplit[1], tagValueSeparator)
params.ExclusiveSet = strings.Split(tsplit[1], TagValueSeparator)
}
case rangeTag:
case RangeTag:
if len(tsplit) == 1 {
params.RangeSeparator = "-"
}
if len(tsplit) == 2 {
params.RangeSeparator = strings.TrimSpace(tsplit[1])
}
case separatorTag:
case SeparatorTag:
if len(tsplit) > 1 {
params.Separator = strings.TrimSpace(tsplit[1])
}
Expand Down
Loading
Loading