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
5 changes: 4 additions & 1 deletion .github/workflows/nightly.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ jobs:
run: make lint

- name: Test
run: make test
run: |
make local-dynamic-plugins
make test

- name: Upload coverage to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7
Expand All @@ -92,6 +94,7 @@ jobs:
# Otherwise, we might end up with a race condition where the tool binary is not yet downloaded,
# but the `make test` command tries to use it.
make manifests generate fmt vet install
make local-dynamic-plugins
make run &
MAKE_RUN_BG_PID=$!
echo "MAKE_RUN_BG_PID=${MAKE_RUN_BG_PID}" | tee -a $GITHUB_ENV
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ jobs:

- name: Test
if: steps.changed-files.outputs.any_changed == 'true'
run: make test
run: |
make local-dynamic-plugins
make test

- name: Upload coverage to Codecov
if: steps.changed-files.outputs.any_changed == 'true'
Expand All @@ -83,6 +85,7 @@ jobs:
# Otherwise, we might end up with a race condition where the tool binary is not yet downloaded,
# but the `make test` command tries to use it.
make manifests generate fmt vet install
make local-dynamic-plugins
make run &

- name: Generic Integration test
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ bundle_tmp*
testbin/*
Dockerfile.cross
__debug_bin*
tmp/*

# Test binary, built with `go test -c`
*.test
Expand Down Expand Up @@ -47,4 +48,7 @@ rhdh-disconnected-install.Dockerfile
.oc-mirror.log

# from mirror-plugins.sh script
rhdh-plugin-mirroring-summary.txt
rhdh-plugin-mirroring-summary.txt

# local-test directories are user-specific local configs for testing
**/local-test/
14 changes: 8 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,12 @@ fmt: goimports ## Format the code using goimports

.PHONY: test
test: manifests generate fmt vet setup-envtest $(LOCALBIN) ## Run tests. We need LOCALBIN=$(LOCALBIN) to get correct default-config path
mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config
mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/plugin-deps/* $(LOCALBIN)/plugin-deps 2>/dev/null || :
@./hack/copy-local-dynamic-plugins.sh $(PROFILE) $(LOCALBIN)
LOCALBIN=$(LOCALBIN) KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(PKGS) -coverprofile cover.out

.PHONY: integration-test
integration-test: ginkgo manifests generate fmt vet envtest $(LOCALBIN) ## Run integration_tests. We need LOCALBIN=$(LOCALBIN) to get correct default-config path
mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config
mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/plugin-deps/* $(LOCALBIN)/plugin-deps 2>/dev/null || :
@./hack/copy-local-dynamic-plugins.sh $(PROFILE) $(LOCALBIN)
LOCALBIN=$(LOCALBIN) KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" $(GINKGO) -v -r $(ARGS) integration_tests

# After this time, Ginkgo will emit progress reports, so we can get visibility into long-running tests.
Expand Down Expand Up @@ -232,10 +230,14 @@ build: manifests generate fmt vet ## Build manager binary.

.PHONY: run
run: manifests generate fmt vet $(LOCALBIN) ## Run a controller from your host.
mkdir -p $(LOCALBIN)/default-config/ && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config/
mkdir -p $(LOCALBIN)/plugin-deps/ && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/plugin-deps/* $(LOCALBIN)/plugin-deps/ 2>/dev/null || :
@./hack/copy-local-dynamic-plugins.sh $(PROFILE) $(LOCALBIN)
go run -C $(LOCALBIN) ../cmd/main.go $(ARGS)

.PHONY: local-dynamic-plugins
local-dynamic-plugins: ## Generate local-test dynamic-plugins.yaml from catalog-index image for local testing
@echo "Generating local-test dynamic-plugins.yaml from catalog-index image..."
@./hack/create-local-dynamic-plugins.sh

# by default images expire from quay registry after 14 days
# set a longer timeout (or set no label to keep images forever)
LABEL ?= quay.expires-after=14d
Expand Down
2 changes: 1 addition & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/v1alpha3/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/v1alpha4/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/v1alpha5/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions docs/developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,36 @@ make uninstall
You can run your controller standalone (this will run in the foreground, so switch to a new terminal if you want to leave it running)
This way you can see controllers log just in your terminal window which is quite convenient for debugging.

#### Local Dynamic Plugins Setup (rhdh profile)

For the `rhdh` profile, default dynamic plugins configuration is provided by the catalog-index image (not stored in the repository). In production, this configuration is extracted at runtime.

For local development and testing, we download the configuration once and reuse it to avoid repeated image pulls on every `make run` or `make test` invocation.

```sh
make local-dynamic-plugins
```

This command extracts `dynamic-plugins.default.yaml` from the catalog-index image and creates `config/profile/rhdh/local-test/dynamic-plugins.yaml`. The `local-test` directory is git-ignored, allowing you to customize dynamic-plugins for local testing.

**This only needs to be run once.** Re-run it when you want to refresh with newer catalog-index data.

**Requirements:**
- `skopeo` must be installed (`brew install skopeo` on macOS, `dnf install skopeo` on Fedora/RHEL)

**Optional:** Specify a different catalog-index image:
```sh
IMAGE=quay.io/rhdh/plugin-catalog-index:1.10 make local-dynamic-plugins
```

#### Running the controller

```sh
make [PROFILE=<configuration-profile>] [install] run
```

**Note:** For the `rhdh` profile, `make run`, `make test`, and `make integration-test` require the `local-test` directory to exist. If it doesn't, the command will fail with instructions to run `make local-dynamic-plugins` first.

You can use it for manual and automated ([such as](../integration_tests/README.md) `USE_EXISTING_CLUSTER=true make integration-test`) tests efficiently, but, note, RBAC is not working with this kind of deployment.

### Deploy operator to the real cluster
Expand Down
14 changes: 14 additions & 0 deletions docs/dynamic-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,20 @@ The extraction directory can be configured via the `CATALOG_ENTITIES_EXTRACT_DIR

More details in [Catalog Entities Extraction](https://github.com/redhat-developer/rhdh/blob/main/docs/dynamic-plugins/installing-plugins.md#catalog-entities-extraction).

## Plugin URL References

The operator supports special URL reference syntax in plugin package URLs, allowing users to reference versions or plugins from the default configuration.

**Operator behavior:**
- The operator resolves all references during ConfigMap merge (before passing to the init container)
- If a reference cannot be resolved, the operator returns an error and the Backstage CR will not reconcile

### Inherit Reference (`:{{inherit}}`)

Allows inheriting version (tag or digest) from default plugins. Useful when overriding plugin settings without hardcoding versions.

For syntax details and examples, see [OCI Package Version Inheritance](https://github.com/redhat-developer/rhdh/blob/main/docs/dynamic-plugins/installing-plugins.md#oci-package-version-inheritance).

## Dynamic plugins dependency management

### Overview
Expand Down
56 changes: 56 additions & 0 deletions hack/copy-local-dynamic-plugins.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/bin/bash
# Copy default-config and plugin-deps to LOCALBIN for 'make run'
#
# For all profiles: copies default-config and plugin-deps
# For 'rhdh' profile only: overlays dynamic-plugins.yaml from local-test
#
# Usage: ./hack/copy-local-dynamic-plugins.sh <PROFILE> <LOCALBIN>

set -euo pipefail

PROFILE="${1:-rhdh}"
LOCALBIN="${2:-./bin}"

DEFAULT_CONFIG_SRC="config/profile/${PROFILE}/default-config"
PLUGIN_DEPS_SRC="config/profile/${PROFILE}/plugin-deps"
DEFAULT_CONFIG_TARGET="${LOCALBIN}/default-config"
PLUGIN_DEPS_TARGET="${LOCALBIN}/plugin-deps"

# Step 1: Copy default-config for all profiles
# This includes dynamic-plugins.yaml from default-config
mkdir -p "${DEFAULT_CONFIG_TARGET}"
rm -fr "${DEFAULT_CONFIG_TARGET:?}"/*
cp -r "${DEFAULT_CONFIG_SRC}"/* "${DEFAULT_CONFIG_TARGET}/"

# Step 2: Copy plugin-deps if it exists
mkdir -p "${PLUGIN_DEPS_TARGET}"
rm -fr "${PLUGIN_DEPS_TARGET:?}"/*
if [[ -d "${PLUGIN_DEPS_SRC}" ]]; then
cp -r "${PLUGIN_DEPS_SRC}"/* "${PLUGIN_DEPS_TARGET}/" 2>/dev/null || :
fi

# Step 3: For rhdh profile only, overlay dynamic-plugins.yaml from local-test
# (replaces the one copied from default-config)
if [[ "${PROFILE}" == "rhdh" ]]; then
LOCAL_TEST_DIR="config/profile/${PROFILE}/local-test"
DYNAMIC_PLUGINS_FILE="${LOCAL_TEST_DIR}/dynamic-plugins.yaml"

if [[ ! -d "${LOCAL_TEST_DIR}" ]]; then
echo "Error: local-test directory not found at ${LOCAL_TEST_DIR}" >&2
echo "" >&2
echo "Run 'make local-dynamic-plugins' to generate it first." >&2
echo "" >&2
echo "This extracts dynamic-plugins.default.yaml from the catalog-index image" >&2
echo "and creates a local configuration for testing." >&2
exit 1
fi

if [[ ! -f "${DYNAMIC_PLUGINS_FILE}" ]]; then
echo "Error: dynamic-plugins.yaml not found at ${DYNAMIC_PLUGINS_FILE}" >&2
echo "" >&2
echo "Run 'make local-dynamic-plugins' to regenerate the local-test directory." >&2
exit 1
fi

cp "${DYNAMIC_PLUGINS_FILE}" "${DEFAULT_CONFIG_TARGET}/"
fi
119 changes: 119 additions & 0 deletions hack/create-local-dynamic-plugins.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/bin/bash
# Extract dynamic-plugins.default.yaml from catalog-index image
# and create local-test/default-dynamic-plugins.yaml for local testing
#
# The local-test directory is git-ignored, allowing developers to customize
# dynamic-plugins for local testing. Other config files come from default-config.
#
# Usage: Run from project root:
# ./hack/create-local-dynamic-plugins.sh
# IMAGE=quay.io/rhdh/plugin-catalog-index:1.10 ./hack/create-local-dynamic-plugins.sh

set -euo pipefail

# Save current directory (project root)
PROJECT_ROOT="$(pwd)"

IMAGE="${IMAGE:-quay.io/rhdh/plugin-catalog-index:latest}"
OUTPUT_DIR="config/profile/rhdh/local-test"
OUTPUT_FILE="${OUTPUT_DIR}/dynamic-plugins.yaml"

# Verify we're in project root
if [[ ! -f "go.mod" ]] || [[ ! -d "config/profile/rhdh" ]]; then
echo "Error: This script must be run from the project root directory" >&2
echo "Usage: ./hack/create-local-dynamic-plugins.sh" >&2
exit 1
fi

# Verify skopeo is available
if ! command -v skopeo &> /dev/null; then
echo "Error: skopeo command not found" >&2
echo "Please install skopeo to use this script" >&2
echo " macOS: brew install skopeo" >&2
echo " Fedora/RHEL: dnf install skopeo" >&2
exit 1
fi

# Convert paths to absolute
OUTPUT_DIR="${PROJECT_ROOT}/${OUTPUT_DIR}"
OUTPUT_FILE="${PROJECT_ROOT}/${OUTPUT_FILE}"

echo "=== Creating local-test dynamic-plugins from catalog-index ==="
echo "Source image: ${IMAGE}"
echo "Output file: ${OUTPUT_FILE}"
echo ""

# Create local-test directory
mkdir -p "${OUTPUT_DIR}"

# Create temporary directory
TEMP_DIR=$(mktemp -d)
trap 'rm -rf ${TEMP_DIR}' EXIT

echo "Extracting dynamic-plugins.default.yaml from image using skopeo..."
cd "${TEMP_DIR}"

# Use skopeo to copy image to a local directory (linux/amd64)
skopeo copy --override-arch amd64 --override-os linux "docker://${IMAGE}" "dir:./image"

# Find and extract the layer containing dynamic-plugins.default.yaml
for layer in image/*; do
[[ "$layer" == */manifest.json || "$layer" == */version ]] && continue
if tar -tf "$layer" 2>/dev/null | grep -q "dynamic-plugins.default.yaml"; then
tar -xf "$layer" dynamic-plugins.default.yaml 2>/dev/null || true
break
fi
done

# Check if dynamic-plugins.default.yaml was extracted
if [[ ! -f "dynamic-plugins.default.yaml" ]]; then
echo "Error: dynamic-plugins.default.yaml not found in image" >&2
echo "Trying alternative extraction..." >&2

# Alternative: extract all layers and search
mkdir -p rootfs
for layer in image/*; do
[[ "$layer" == */manifest.json || "$layer" == */version ]] && continue
tar -xf "$layer" -C rootfs 2>/dev/null || true
done

if [[ -f "rootfs/dynamic-plugins.default.yaml" ]]; then
cp rootfs/dynamic-plugins.default.yaml ./
else
echo "Error: Could not find dynamic-plugins.default.yaml in any layer" >&2
exit 1
fi
fi

# Create ConfigMap with the extracted content
echo "Creating ConfigMap at ${OUTPUT_FILE}..."

# Create ConfigMap header
cat > "${OUTPUT_FILE}" <<'EOF'
# WARNING: This file is auto-generated!
#
# This file is automatically extracted from the catalog-index image
# by ./hack/create-local-dynamic-plugins.sh
# This is for LOCAL TESTING ONLY and is git-ignored.
#
# You can edit this file for local testing purposes.
# To regenerate: make local-dynamic-plugins
#
apiVersion: v1
kind: ConfigMap
metadata:
name: default-dynamic-plugins
data:
dynamic-plugins.yaml: |
EOF

# Append the extracted content with proper indentation (4 spaces)
sed 's/^/ /' dynamic-plugins.default.yaml >> "${OUTPUT_FILE}"

echo ""
echo "=== Local test config created ==="
echo "File: ${OUTPUT_FILE}"
echo "Size: $(du -h "${OUTPUT_FILE}" | cut -f1)"
echo ""
echo "This file is git-ignored. Edit as needed for local testing."
echo "Use 'make run' to test (default-config + local-test overlay)."
Loading
Loading