Koncur - A test harness for Konveyor tools that "concurs with your expected results!"
Koncur is a declarative test harness for running and validating end-to-end tests for Konveyor tools (Kantra, Tackle, Kai). Define what you want to analyze, not how to run commands.
- Declarative test definitions - Specify application, label selector, and analysis mode
- Multiple execution targets - Kantra CLI, Tackle Hub API, Tackle UI, Kai RPC, VSCode extension
- Flexible target configuration - Separate target config from test definitions
- Exact match validation - Compare actual output against expected RuleSets
- Clear diff output - See exactly what differs when tests fail
- Multiple input formats - Support for inline expected results or file references
- Built on Konveyor types - Uses analyzer-lsp RuleSet structures directly
- Automatic output filtering - Filters empty rulesets for cleaner expected outputs
- Test output management - Clean up old test runs with the
cleancommand
go build -o koncur ./cmd/koncur# my-test.yaml
name: "Sample Kantra Test"
description: "Test cloud-readiness analysis"
analysis:
application: /path/to/application/source
labelSelector: "konveyor.io/target=quarkus"
analysisMode: source-only
expect:
exitCode: 0
output:
result:
- name: cloud-readiness
violations:
session-00001:
description: "Avoid use of HttpSession"
category: mandatory
incidents:
- uri: "file:///src/main/java/MyServlet.java"
lineNumber: 42# Use default target (kantra)
koncur run my-test.yaml
# Specify target type
koncur run my-test.yaml --target kantra
# Use a target configuration file
koncur run my-test.yaml --target-config target-tackle-hub.yamlname: "Test Name"
description: "Optional description"
analysis:
# Application to analyze (file path or git URL)
application: /path/to/source
# Optional: Label selector expression
labelSelector: "konveyor.io/target=quarkus"
# Analysis mode: source-only | full
analysisMode: source-only
# Optional: Execution timeout (default: 5m)
timeout: 10m
# Optional: Work directory (default: .koncur/output)
workDir: /tmp/my-tests
expect:
exitCode: 0
output:
# Option 1: Inline expected RuleSets
result:
- name: ruleset-name
violations: {...}
# Option 2: Reference to external file
file: /absolute/path/to/expected.yamlTarget configuration is separate from test definitions, allowing the same test to run against different targets/environments.
type: kantra
kantra:
binaryPath: /usr/local/bin/kantra # Optional
# forceLocal: true # optional: pass --run-local=true for local/containerless mode (default is --run-local=false)
# Container image overrides (optional, for container mode)
# javaProviderImage: my-java-provider:dev
# genericProviderImage: my-generic-provider:dev
# runnerImage: my-kantra:devtype: tackle-hub
tackleHub:
url: https://tackle-hub.example.com
username: admin
password: secret
# Or use token:
# token: your-api-token
# Override component images (koncur patches the Tackle CR automatically)
images:
analyzer: my-analyzer:dev
javaProvider: my-java-provider:dev
# hub: my-hub:dev
# genericProvider: my-generic-provider:dev
# csharpProvider: my-csharp-provider:dev
# runner: my-kantra:dev
# discoveryAddon: my-discovery:dev
# platformAddon: my-platform:devWhen images is specified, koncur patches the Tackle Custom Resource on the cluster via kubectl and waits for the Hub to become ready before running tests. See Local Testing with Custom Images for detailed workflows.
Not Implemented
type: tackle-ui
tackleUI:
url: https://tackle.example.com
username: admin
password: secret
browser: chrome # chrome or firefox
headless: trueNot Implemented
type: kai-rpc
kaiRPC:
host: localhost
port: 8080** Not Implemented **
type: vscode
vscode:
binaryPath: /usr/local/bin/code # Optional
extensionId: konveyor.konveyor-analyzer
workspaceDir: /path/to/workspace # OptionalExecute a test and validate output against expected results.
koncur run testdata/examples/sample_test.yamlValidate a test definition without running it.
koncur validate testdata/examples/sample_test.yamlGenerate expected outputs by running tests and capturing their results. This command:
- Finds all
test.yamlfiles in the specified directory - Executes each test using the specified target
- Filters out empty rulesets (no violations, insights, or tags)
- Saves the filtered output as
expected-output.yamlin each test directory - Updates test definitions to use file-based expectations
# Generate expected outputs for all tests
koncur generate -d ./tests
# Generate for a specific test
koncur generate -d ./tests/my-test
# Filter by test name pattern
koncur generate -d ./tests --filter "tackle"
# Dry run (show what would be done)
koncur generate -d ./tests --dry-run
# Use a specific target
koncur generate -d ./tests --target kantraFlags:
-d, --test-dir- Directory containing test definitions (default:./tests)-f, --filter- Filter tests by name pattern--dry-run- Show what would be done without executing-t, --target- Target type to use (default:kantra)
Clean up old test run outputs from the .koncur/output directory.
By default, keeps the most recent run for each test and deletes older ones.
# Clean old test runs (keeps latest for each test)
koncur clean
# Preview what would be deleted
koncur clean --dry-run
# Remove all output directories
koncur clean --all
# Preview removing everything
koncur clean --all --dry-runFlags:
--all- Remove all output directories (not just old ones)--dry-run- Show what would be deleted without actually deleting
-v, --verbose- Enable verbose logging
See testdata/examples/ for sample test definitions.
pkg/config/- Test definition types and loadingpkg/targets/- Target executors (Kantra, Tackle, Kai)pkg/parser/- Output parsing (RuleSets)pkg/validator/- Exact match validation with diffpkg/cli/- CLI commands
# Build
go build -o koncur ./cmd/koncur
# Run tests
go test ./...
# Validate a test definition
./koncur validate testdata/examples/sample_test.yamlKoncur includes a Makefile for quickly setting up and testing against a local Tackle Hub instance running in Kind (Kubernetes in Docker).
# Complete setup: create cluster, install hub, build binary
make setup
# This runs:
# 1. make kind-create - Creates Kind cluster with ingress
# 2. make hub-install - Installs Tackle Hub with OLM (auth disabled)
# 3. make build - Builds the koncur binaryTo install Tackle Hub with Keycloak authentication enabled:
# Complete setup with auth: create cluster, install hub with Keycloak, build binary
make setup-auth
# This runs:
# 1. make kind-create - Creates Kind cluster with ingress
# 2. make hub-install-auth - Installs Tackle Hub with Keycloak (auth enabled)
# 3. make build - Builds the koncur binaryWhen auth is enabled, the setup:
- Deploys Keycloak SSO alongside Tackle Hub
- Configures Keycloak hostname for
https://localhost:8443/auth - Creates a NetworkPolicy to allow ingress traffic to Keycloak
- Provisions an admin user with password from the
tackle-keycloak-ssosecret - Serves Tackle Hub over HTTPS with a self-signed certificate
Default credentials:
- Username:
admin - Password:
Passw0rd!
The Tackle application user password (Passw0rd!) is set by the hub when it
seeds the admin user into Keycloak. This is separate from the Keycloak server
admin password stored in the tackle-keycloak-sso secret.
Once setup is complete, Tackle Hub is accessible via:
Without auth (HTTP):
- Hub API:
http://localhost:8080/hub - Hub UI:
http://localhost:8080/hub
With auth (HTTPS):
- Hub:
https://localhost:8443 - Accept the self-signed certificate warning in your browser
Port-forward (alternative, works for both):
make hub-forward
# Hub will be available at http://localhost:8081# Run a test against Tackle Hub
make test-hub
# Or run manually with koncur
./koncur run tests/tackle-testapp-with-deps/test.yaml \
--target-config .koncur/config/target-tackle-hub.yamlYou can package the test suite into a portable archive for running tests without the full repo:
# Build the archive (~200KB)
make test-archive
# Run tests from the archive against any target
./koncur run --test-archive koncur-tests.tar.gz \
-t tackle-hub --target-config .koncur/config/target-tackle-hub.yamlSee Local Testing with Custom Images for the full portable testing workflow.
Setup & Teardown:
make setup- Complete setup (cluster + hub + build, no auth)make setup-auth- Complete setup with Keycloak authenticationmake teardown- Complete teardown (uninstall hub + delete cluster)
Cluster Management:
make kind-create- Create Kind cluster with ingress-nginxmake kind-delete- Delete the Kind cluster
Tackle Hub:
make hub-install- Install Tackle Hub with auth disabledmake hub-install-auth- Install Tackle Hub with Keycloak auth enabledmake hub-uninstall- Uninstall Tackle Hubmake hub-status- Check Tackle Hub statusmake hub-forward- Port-forward to access Hub at :8081
Build & Test:
make build- Build koncur binarymake test-hub- Run tackle-testapp test against Hubmake clean- Clean build artifacts and test outputs
The Makefile uses these configurable variables:
# Cluster Configuration
KIND_CLUSTER_NAME ?= koncur-test
KONVEYOR_NAMESPACE ?= konveyor-tackle
KUBECTL ?= kubectl
# Image Overrides (optional)
# Override any image by setting environment variables:
HUB ?= quay.io/konveyor/tackle2-hub:latest
ANALYZER_ADDON ?= quay.io/konveyor/tackle2-addon-analyzer:latest
CSHARP_PROVIDER_IMG ?= quay.io/konveyor/c-sharp-provider:latest
GENERIC_PROVIDER_IMG ?= quay.io/konveyor/generic-external-provider:latest
JAVA_PROVIDER_IMG ?= quay.io/konveyor/java-external-provider:latest
RUNNER_IMG ?= quay.io/konveyor/kantra:latest
DISCOVERY_ADDON ?= quay.io/konveyor/tackle2-addon-discovery:latest
PLATFORM_ADDON ?= quay.io/konveyor/tackle2-addon-platform:latestRecommended: target config (patches the Tackle CR automatically)
Put image overrides in your target config and koncur will patch the Tackle CR and wait for readiness before running tests:
# target-tackle-hub.yaml
type: tackle-hub
tackleHub:
url: http://localhost:8080/hub
images:
analyzer: my-analyzer:dev
javaProvider: my-java-provider:dev./koncur run tests -t tackle-hub --target-config target-tackle-hub.yamlAlternative: Makefile env vars (at install time)
Override images when first installing Hub:
ANALYZER_ADDON=my-analyzer:dev make hub-installFor a complete guide on building, loading, and testing with locally-built images (including Kind image loading and iteration workflows), see Local Testing with Custom Images.
Without auth - the default target config (.koncur/config/target-tackle-hub.yaml):
type: tackle-hub
tackleHub:
url: http://localhost:8080/hub
token: ""
mavenSettings: settings.xmlWith auth - when using make setup-auth, the target config must include
username, password, and insecure: true (for the self-signed certificate):
type: tackle-hub
tackleHub:
url: https://localhost:8443/hub
username: admin
password: "Passw0rd!"
insecure: true
mavenSettings: settings.xmlThe insecure: true field tells koncur to skip TLS certificate verification,
which is required because the Kind cluster uses a self-signed certificate.
The make hub-install target automatically:
- Installs OLM (Operator Lifecycle Manager)
- Installs Tackle Operator from main branch
- Creates Tackle CR with:
- Authentication disabled (
feature_auth_required: "false") - Cache storage configured (10Gi RWX PV)
- Resource limits optimized for testing (100m CPU for providers)
- All component images configurable via environment variables
- Authentication disabled (
- Patches ingress to disable SSL redirect (allows HTTP access at
http://localhost:8080) - Waits for readiness with automatic health checks
The make hub-install-auth target does all of the above, plus:
- Enables authentication (
feature_auth_required: "true") - Deploys Keycloak SSO and waits for it to become ready
- Creates a NetworkPolicy allowing ingress-nginx to reach Keycloak
- Configures Keycloak hostname for
https://localhost:8443/authwith backchannel dynamic routing - Disables forced password update so the admin user can log in immediately
- Provisions the admin user in the
tacklerealm and clears any required actions
Ingress redirecting to HTTPS:
- The Makefile automatically patches the ingress to disable SSL redirect
- If you see 308 redirects, run:
kubectl annotate ingress tackle -n konveyor-tackle nginx.ingress.kubernetes.io/ssl-redirect="false" --overwrite
Ingress not working:
- Ensure Kind cluster was created with ingress support:
kubectl get pods -n ingress-nginx - Verify ingress controller is running and ready
- Check ingress resource:
kubectl get ingress -n konveyor-tackle
Operator not ready:
- Check operator logs:
kubectl logs -n konveyor-tackle -l name=tackle-operator - Verify CRD installed:
kubectl get crd tackles.tackle.konveyor.io
Hub pods not starting:
- Check pod status:
make hub-status - View pod logs:
kubectl logs -n konveyor-tackle -l app.kubernetes.io/name=tackle-hub
Auth setup: Keycloak or Hub unreachable via ingress:
- The ingress-nginx controller may need to be restarted after Tackle and Keycloak
ingress resources are created. Delete the controller pod and let Kubernetes
recreate it:
kubectl delete pod -n ingress-nginx -l app.kubernetes.io/component=controller
- Wait for the new pod to become ready:
kubectl wait --namespace ingress-nginx \ --for=condition=ready pod \ --selector=app.kubernetes.io/component=controller \ --timeout=120s - Then verify you can reach the Hub at
https://localhost:8443
Apache 2.0