Skip to content
Open
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
44 changes: 40 additions & 4 deletions bddframework/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package config
import (
"fmt"
"path/filepath"
"strings"

flag "github.com/spf13/pflag"
)
Expand All @@ -45,6 +46,7 @@ type TestConfig struct {
operatorInstallationSource string
operatorCatalogImage string
useProductOperator bool
relatedImages map[string]string

// profiling
operatorProfiling bool
Expand Down Expand Up @@ -102,26 +104,29 @@ type TestConfig struct {
}

const (
defaultOperatorYamlURI = "../operator.yaml"
defaultOperatorYamlURI = "http://raw.githubusercontent.com/kubesmarts/kie-tools/refs/heads/9.105.x-prod/packages/sonataflow-operator/operator.yaml"
defaultRhpamOperatorYamlURI = "../rhpam-operator.yaml"
defaultCliPath = "../build/_output/bin/kogito"

defaultOperatorProfilingDataAccessYamlURI = "../profiling/kogito-operator-profiling-data-access.yaml"
defaultOperatorProfilingOutputFileURI = "./bdd-cover.out"

defaultKogitoExamplesURI = "https://github.com/apache/incubator-kie-kogito-examples"
defaultKogitoExamplesURI = "https://github.com/kiegroup/kogito-examples"

defaultLoadFactor = 1
defaultHTTPRetryNumber = 3

defaultContainerEngine = "podman"
defaultContainerEngine = "docker"

installationSourceOlm = "olm"
installationSourceYaml = "yaml"
)

var (
env = TestConfig{}

// Map at the package level to hold the flag pointers
relatedImagePointers = make(map[string]*string)
)

// BindFlags binds BDD tests env flags to given flag set
Expand All @@ -147,14 +152,24 @@ func BindFlags(set *flag.FlagSet) {
set.StringVar(&env.operatorInstallationSource, prefix+"operator_installation_source", installationSourceYaml, "Operator installation source")
set.StringVar(&env.operatorCatalogImage, prefix+"operator_catalog_image", "", "Operator catalog image")
set.BoolVar(&env.useProductOperator, prefix+"use_product_operator", false, "Set to true to deploy RHPAM Kogito operator, false for using Kogito operator. Default is false.")
// operator related images
for _, v := range GetRelatedImageVars() {
flagName := strings.ToLower(strings.TrimPrefix(v, "RELATED_IMAGE_"))
// set.String automatically returns a *string linked to the CLI flag!
relatedImagePointers[v] = set.String(
"operator.related_image."+flagName,
"",
fmt.Sprintf("Override for %s", v),
)
}

// operator profiling
set.BoolVar(&env.operatorProfiling, prefix+"operator_profiling_enabled", false, "Enable the profiling of the operator. If enabled, operator will be automatically deployed with yaml files.")
set.StringVar(&env.operatorProfilingDataAccessYamlURI, prefix+"operator_profiling_data_access_yaml_uri", defaultOperatorProfilingDataAccessYamlURI, "Url or Path to kogito-operator-profiling-data-access.yaml file.")
set.StringVar(&env.operatorProfilingOutputFileURI, prefix+"operator_profiling_output_file_uri", defaultOperatorProfilingOutputFileURI, "Url or Path where to store the profiling outputs.")

// files/binaries
set.StringVar(&env.operatorYamlURI, prefix+"operator_yaml_uri", defaultOperatorYamlURI, "Url or Path to kogito-operator.yaml file")
set.StringVar(&env.operatorYamlURI, prefix+"operator_yaml_uri", defaultOperatorYamlURI, "Url or Path to operator.yaml file")
set.StringVar(&env.rhpamOperatorYamlURI, prefix+"rhpam_operator_yaml_uri", defaultRhpamOperatorYamlURI, "Url or Path to kogito-operator.yaml file")
set.StringVar(&env.cliPath, prefix+"cli_path", defaultCliPath, "Path to built CLI to test")

Expand Down Expand Up @@ -222,6 +237,27 @@ func addPersistenceTypeImageTagFlags(set *flag.FlagSet, imageTags *imageTags, im
set.StringVar(imageTags.GetImageTagPointerFromPersistenceType(imageType, persistenceType), key, "", description)
}

// Get Array of string identifiers for all operator RELATED_IMAGES keys
func GetRelatedImageVars() []string {
return []string{
"RELATED_IMAGE_JOBS_SERVICE_POSTGRESQL",
"RELATED_IMAGE_JOBS_SERVICE_EPHEMERAL",
"RELATED_IMAGE_DATA_INDEX_POSTGRESQL",
"RELATED_IMAGE_DATA_INDEX_EPHEMERAL",
"RELATED_IMAGE_DB_MIGRATOR_TOOL",
"RELATED_IMAGE_BASE_BUILDER",
"RELATED_IMAGE_DEVMODE",
}
}

// GetRelatedImage returns the override for a specific related image variable
func GetRelatedImage(varName string) string {
if ptr, ok := relatedImagePointers[varName]; ok && ptr != nil {
return *ptr
}
return ""
}

// tests configuration

// IsSmokeTests return whether smoke tests should be executed
Expand Down
37 changes: 37 additions & 0 deletions run_custom_operator_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash

export DEBUG=true

rm -rf testbdd/logs/

go run testbdd/scripts/prune_namespaces.go

./hack/clean-cluster-operators.sh
./hack/clean-crds.sh

# Configuration variables (change these to match your actual test registry/tags)
REGISTRY="registry-proxy.engineering.redhat.com/rh-osbs"
OPERATOR_TAG="openshift-serverless-nightly-rhel-9-containers-candidate-69005-20260325162017"
JOBSSERVICE_POSTGESQL_TAG="openshift-serverless-nightly-rhel-9-containers-candidate-18542-20260325161846"
JOBSSERVICE_EPHEMERAL_TAG="openshift-serverless-nightly-rhel-9-containers-candidate-45012-20260325161758"
DATAINDEX_POSTGESQL_TAG="openshift-serverless-nightly-rhel-9-containers-candidate-71465-20260325161709"
DATAINDEX_EPHEMERAL_TAG="openshift-serverless-nightly-rhel-9-containers-candidate-58554-20260325161807"
DBMIGRATOR_TAG="openshift-serverless-nightly-rhel-9-containers-candidate-65986-20260325161934"
BUIDLER_TAG="openshift-serverless-nightly-rhel-9-containers-candidate-71914-20260325161933"
DEVMODE_TAG="openshift-serverless-nightly-rhel-9-containers-candidate-95367-20260325162027"

echo "🚀 Running Logic Operator BDD tests with custom related NIGHTLY images..."

# Execute the Godog test suite and pass the CLI arguments
go test ./testbdd/... -v --godog.tags="installation" --godog.format=junit --godog.format=pretty -args \
--tests.operator_image_tag="${REGISTRY}/openshift-serverless-1-logic-rhel9-operator:${OPERATOR_TAG}" \
--operator.related_image.jobs_service_postgresql="${REGISTRY}/openshift-serverless-1-logic-jobs-service-postgresql-rhel9:${JOBSSERVICE_POSTGESQL_TAG}" \
--operator.related_image.jobs_service_ephemeral="${REGISTRY}/openshift-serverless-1-logic-jobs-service-ephemeral-rhel9:${JOBSSERVICE_EPHEMERAL_TAG}" \
--operator.related_image.data_index_postgresql="${REGISTRY}/openshift-serverless-1-logic-data-index-postgresql-rhel9:${DATAINDEX_POSTGESQL_TAG}" \
--operator.related_image.data_index_ephemeral="${REGISTRY}/openshift-serverless-1-logic-data-index-ephemeral-rhel9:${DATAINDEX_EPHEMERAL_TAG}" \
--operator.related_image.db_migrator_tool="${REGISTRY}/openshift-serverless-1-logic-db-migrator-tool-rhel9:${DBMIGRATOR_TAG}" \
--operator.related_image.base_builder="${REGISTRY}/openshift-serverless-1-logic-swf-builder-rhel9:${BUIDLER_TAG}" \
--operator.related_image.devmode="${REGISTRY}/openshift-serverless-1-logic-swf-devmode-rhel9:${DEVMODE_TAG}" \
--tests.cr_deployment_only


1 change: 1 addition & 0 deletions testbdd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ You can set these optional keys:
*Default is false.*
- `local_execution` to be set to true if running tests in local using either a local or remote cluster.
*Default is false.*
- `operator.related_image.jobs_service_postgresql` etc.

Logs will be shown on the Terminal.

Expand Down
25 changes: 25 additions & 0 deletions testbdd/features/installation/install_logic_operator_yaml.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Feature: Deploy Logic Operator with using YAML
As a developer
I want to install the Logic Operator using YAML
So that I can verify the installation behaves expectedly

@installation
Scenario: Deploy the operator and verify it is running
Given Namespace is created
When SonataFlow Operator is deployed
Then SonataFlow Operator has 1 pod running
And Service "logic-operator-controller-manager-metrics-service" exists
And ConfigMap "logic-operator-builder-config" exists
And ConfigMap "logic-operator-controllers-config" exists
And ConfigMap "logic-operator-builder-config" contains following strings:
| FROM ${RELATED_IMAGE_BASE_BUILDER} AS builder |
| FROM registry.access.redhat.com/ubi9/openjdk-17-runtime:1.23 |
When Postgres is deployed
When SonataFlowPlatform with DataIndexAndJobsService using Postgres is deployed
When SonataFlow callbackstatetimeouts example is deployed
Then SonataFlow "callbackstatetimeouts" has the condition "Running" set to "True" within 5 minutes
Then HTTP POST request on non-dev SonataFlow "callbackstatetimeouts" is successful within 1 minute with path "callbackstatetimeouts", expectedResponseContains '"workflowdata":{"message":"Hello"}"' and body:
"""json
{"message": "Hello"
}
"""
3 changes: 2 additions & 1 deletion testbdd/framework/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import (
const (
sonataFlowOperatorTimeoutInMin = 5

sonataFlowOperatorName = "logic-operator-rhel8"
// TODO - this needs to use the correct value based on the STREAM ( CR/Nightly )
sonataFlowOperatorName = "logic-operator"
sonataFlowOperatorDeploymentName = sonataFlowOperatorName + "-controller-manager"
sonataFlowOperatorPullImageSecretPrefix = sonataFlowOperatorName + "-dockercfg"
)
Expand Down
105 changes: 92 additions & 13 deletions testbdd/installers/sonataflow_installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"errors"
"fmt"
"regexp"
"strings"

"sigs.k8s.io/controller-runtime/pkg/client"

Expand All @@ -32,13 +33,13 @@ import (
srvframework "github.com/kubesmarts/operator-bdd-test/testbdd/framework"
)

const defaultOperatorImage = "quay.io/kubesmarts/incubator-kie-sonataflow-operator"
const defaultOperatorImage = "registry-proxy.engineering.redhat.com/rh-osbs/openshift-serverless-1-logic-rhel9-operator:latest"

var (
// sonataFlowYamlClusterInstaller installs SonataFlow operator cluster wide using YAMLs
sonataFlowYamlClusterInstaller = installers.YamlClusterWideServiceInstaller{
InstallClusterYaml: installSonataFlowUsingYaml,
InstallationNamespace: SonataFlowNamespace,
InstallationNamespace: LogicOperatorNamespace,
WaitForClusterYamlServiceRunning: waitForSonataFlowOperatorUsingYamlRunning,
GetAllClusterYamlCrsInNamespace: getSonataFlowCrsInNamespace,
UninstallClusterYaml: uninstallSonataFlowUsingYaml,
Expand Down Expand Up @@ -71,7 +72,25 @@ var (
sonataFlowServiceName = "SonataFlow operator"

sonataFlowOperatorSubscriptionName = "sonataflow-operator"
sonataFlowOperatorSubscriptionChannel = "alpha"
sonataFlowOperatorSubscriptionChannel = "stable"

sonataFlowOperatorControllerConfigName = sonataFlowOperatorSubscriptionName + "-controllers-config"
sonataFlowOperatorBuilderConfigName = sonataFlowOperatorSubscriptionName + "-builder-config"
sonataFlowOperatorControllerManagerServiceAccountName = sonataFlowOperatorSubscriptionName + "-controller-manager"
sonataFlowOperatorMetricsReaderName = sonataFlowOperatorSubscriptionName + "-metrics-reader"
sonataFlowOperatorLeaderElectionRoleName = sonataFlowOperatorSubscriptionName + "-leader-election-role"
sonataFlowOperatorBuilderManagerRoleName = sonataFlowOperatorSubscriptionName + "-builder-manager-role"

// Openshift Serverless Logic naming constants
LogicOperatorNamespace = "openshift-serverless-logic"
logicOperatorSubscriptionName = "logic-operator"

logicOperatorControllerConfigName = logicOperatorSubscriptionName + "-controllers-config"
logicOperatorBuilderConfigName = logicOperatorSubscriptionName + "-builder-config"
logicOperatorControllerManagerServiceAccountName = logicOperatorSubscriptionName + "-controller-manager"
logicOperatorMetricsReaderName = logicOperatorSubscriptionName + "-metrics-reader"
logicOperatorLeaderElectionRoleName = logicOperatorSubscriptionName + "-leader-election-role"
logicOperatorBuilderManagerRoleName = logicOperatorSubscriptionName + "-builder-manager-role"
)

// GetSonataFlowInstaller returns SonataFlow installer
Expand All @@ -96,24 +115,84 @@ func GetSonataFlowInstaller() (installers.ServiceInstaller, error) {
func installSonataFlowUsingYaml() error {
framework.GetMainLogger().Info("Installing SonataFlow operator")

yamlContent, err := framework.ReadFromURI(config.GetOperatorYamlURI())
operatorImage := config.GetOperatorImageTag()
manifestURI := config.GetOperatorYamlURI()

// Read the content of operator.yaml from configured URL
yamlContent, err := framework.ReadFromURI(manifestURI)
if err != nil {
framework.GetMainLogger().Error(err, "Error while reading the operator YAML file")
framework.GetMainLogger().Error(err, "Error while reading the operator YAML file at %s", manifestURI)
return err
}

regexp, err := regexp.Compile("main")
if err != nil {
return err
// Patch the image reference of operator
if len(operatorImage) > 0 {
imageRegex := regexp.MustCompile(`image:\s*["']?.*?incubator-kie-sonataflow-operator[^"'\s]*["']?`)
if !imageRegex.MatchString(yamlContent) {
// Fallback: search for a generic placeholder if the specific one isn't found
imageRegex = regexp.MustCompile(`image:\s*["']?placeholder["']?|image:\s*["']?main["']?`)
}
yamlContent = imageRegex.ReplaceAllString(yamlContent, fmt.Sprintf("image: %s", operatorImage))
}

// 2. Patch Related Images Environment Variables
relatedImageVars := []string{
"RELATED_IMAGE_JOBS_SERVICE_POSTGRESQL",
"RELATED_IMAGE_JOBS_SERVICE_EPHEMERAL",
"RELATED_IMAGE_DATA_INDEX_POSTGRESQL",
"RELATED_IMAGE_DATA_INDEX_EPHEMERAL",
"RELATED_IMAGE_DB_MIGRATOR_TOOL",
"RELATED_IMAGE_BASE_BUILDER",
"RELATED_IMAGE_DEVMODE",
}

for _, envVar := range relatedImageVars {
overrideValue := config.GetRelatedImage(envVar)
if len(overrideValue) > 0 {
// (?s) allows the regex to read across newlines.
// It finds "name: <VAR>" and the subsequent "value: " string, keeping them intact (${1})
// and replaces the actual image tag.
re := regexp.MustCompile(`(?s)(name:\s*` + envVar + `\s+value:\s*)([^\s"']+)`)
yamlContent = re.ReplaceAllString(yamlContent, "${1}"+overrideValue)

framework.GetMainLogger().Info(fmt.Sprintf("Patched %s with %s", envVar, overrideValue))
}
}
yamlContent = regexp.ReplaceAllString(yamlContent, config.GetOperatorImageTag())

tempFilePath, err := framework.CreateTemporaryFile("kogito-serverless-operator*.yaml", yamlContent)
// Replace builder image reference in sonataflow-operator-builder-config
builderImageUrl := config.GetRelatedImage("RELATED_IMAGE_BASE_BUILDER")
builderRegex := regexp.MustCompile(`docker\.io/apache/incubator-kie-sonataflow-builder[^\s"']*`)
yamlContent = builderRegex.ReplaceAllString(yamlContent, builderImageUrl)

// Replace sonataflow-operator-system with openshift-serverless-logic
yamlContent = strings.ReplaceAll(yamlContent, SonataFlowNamespace, LogicOperatorNamespace)
// Replace remaining community prefixes
yamlContent = strings.ReplaceAll(yamlContent, "sonataflow-operator-", "logic-operator-")

/**
// Replace sonataflow-operator-controllers-config with logic-operator-controllers-config
yamlContent = strings.ReplaceAll(yamlContent, sonataFlowOperatorControllerConfigName, logicOperatorControllerConfigName)
// Replace sonataflow-operator-builder-config with logic-operator-builder-config
yamlContent = strings.ReplaceAll(yamlContent, sonataFlowOperatorBuilderConfigName, logicOperatorBuilderConfigName)
// Replace sonataflow-operator-controller-manager with logic-operator-controller-manager
yamlContent = strings.ReplaceAll(yamlContent, sonataFlowOperatorControllerManagerServiceAccountName,
logicOperatorControllerManagerServiceAccountName)
// Replace sonataflow-operator-metrics-reader with logic-operator-metrics-reader
yamlContent = strings.ReplaceAll(yamlContent, sonataFlowOperatorMetricsReaderName, logicOperatorMetricsReaderName)
// Replace sonataflow-operator-leader-election-role with logic-operator-leader-election-role
yamlContent = strings.ReplaceAll(yamlContent, sonataFlowOperatorLeaderElectionRoleName, logicOperatorLeaderElectionRoleName)
// Replace sonataflow-operator-builder-manager-role with logic-operator-builder-manager-role
yamlContent = strings.ReplaceAll(yamlContent, sonataFlowOperatorBuilderManagerRoleName, logicOperatorBuilderManagerRoleName)
*/
// Create also one file to be able to inspect the YAML if needed
framework.CreateFile("./", "operator.yaml", yamlContent)
tempFilePath, err := framework.CreateTemporaryFile("logic-operator*.yaml", yamlContent)
if err != nil {
framework.GetMainLogger().Error(err, "Error while storing adjusted YAML content to temporary file")
return err
}

// TODO: Make this differentiate between different CLIs
_, err = framework.CreateCommand("oc", "create", "-f", tempFilePath).Execute()
if err != nil {
framework.GetMainLogger().Error(err, "Error while installing SonataFlow operator from YAML file")
Expand All @@ -124,15 +203,15 @@ func installSonataFlowUsingYaml() error {
}

func waitForSonataFlowOperatorUsingYamlRunning() error {
return srvframework.WaitForSonataFlowOperatorRunning(SonataFlowNamespace)
return srvframework.WaitForSonataFlowOperatorRunning(LogicOperatorNamespace)
}

func uninstallSonataFlowUsingYaml() error {
framework.GetMainLogger().Info("Uninstalling SonataFlow operator")

output, err := framework.CreateCommand("oc", "delete", "-f", config.GetOperatorYamlURI(), "--timeout=30s", "--ignore-not-found=true").Execute()
output, err := framework.CreateCommand("oc", "delete", "-f", "./operator.yaml", "--timeout=60s", "--ignore-not-found=true").Execute()
if err != nil {
framework.GetMainLogger().Error(err, fmt.Sprintf("Deleting SonataFlow operator failed, output: %s", output))
framework.GetMainLogger().Error(err, fmt.Sprintf("Deleting SonataFlow operator failed, output:\n %s", output))
return err
}

Expand Down
1 change: 1 addition & 0 deletions testbdd/steps/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
// Data contains all data needed by Gherkin steps to run
type Data struct {
*sonataFlowSteps.Data
OperatorNamespace string
}

// RegisterAllSteps register all steps available to the test suite
Expand Down
Loading