diff --git a/Containerfile b/Containerfile index 5d3568f..bd9d31b 100644 --- a/Containerfile +++ b/Containerfile @@ -24,6 +24,7 @@ WORKDIR ${PATTERNIZER_RESOURCES_DIR} COPY pattern.sh . COPY values-secret.yaml.template . +COPY Makefile-pattern . ARG PATTERN_REPO_ROOT=/repo WORKDIR ${PATTERN_REPO_ROOT} diff --git a/Makefile-pattern b/Makefile-pattern new file mode 100644 index 0000000..f9483bf --- /dev/null +++ b/Makefile-pattern @@ -0,0 +1,171 @@ +NAME ?= $(shell basename "`pwd`") + +ifneq ($(origin TARGET_SITE), undefined) + TARGET_SITE_OPT=--set main.clusterGroupName=$(TARGET_SITE) +endif + +# Set this to true if you want to skip any origin validation +DISABLE_VALIDATE_ORIGIN ?= false +ifeq ($(DISABLE_VALIDATE_ORIGIN),true) + VALIDATE_ORIGIN := +else + VALIDATE_ORIGIN := validate-origin +endif + +# This variable can be set in order to pass additional helm arguments from the +# the command line. I.e. we can set things without having to tweak values files +EXTRA_HELM_OPTS ?= + +# This variable can be set in order to pass additional ansible-playbook arguments from the +# the command line. I.e. we can set -vvv for more verbose logging +EXTRA_PLAYBOOK_OPTS ?= + +# INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:394248 +# or +# INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:394248,registry-proxy.engineering.redhat.com/rh-osbs/iib:394249 +INDEX_IMAGES ?= + +# git branch --show-current is also available as of git 2.22, but we will use this for compatibility +TARGET_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD) + +# Default to the branch remote +TARGET_ORIGIN ?= $(shell git config branch.$(TARGET_BRANCH).remote) + +# This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL +# This is because we expect to use tokens for repo authentication as opposed to SSH keys +TARGET_REPO=$(shell git ls-remote --get-url --symref $(TARGET_ORIGIN) | sed -e 's/.*URL:[[:space:]]*//' -e 's%^git@%%' -e 's%^https://%%' -e 's%:%/%' -e 's%^%https://%') + +UUID_FILE ?= ~/.config/validated-patterns/pattern-uuid +UUID_HELM_OPTS ?= + +# --set values always take precedence over the contents of -f +ifneq ("$(wildcard $(UUID_FILE))","") + UUID := $(shell cat $(UUID_FILE)) + UUID_HELM_OPTS := --set main.analyticsUUID=$(UUID) +endif + +# Set the secret name *and* its namespace when deploying from private repositories +# The format of said secret is documented here: https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#repositories +TOKEN_SECRET ?= +TOKEN_NAMESPACE ?= + +ifeq ($(TOKEN_SECRET),) + HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(UUID_HELM_OPTS) $(EXTRA_HELM_OPTS) +else + # When we are working with a private repository we do not escape the git URL as it might be using an ssh secret which does not use https:// + TARGET_CLEAN_REPO=$(shell git ls-remote --get-url --symref $(TARGET_ORIGIN)) + HELM_OPTS=-f values-global.yaml --set main.tokenSecret=$(TOKEN_SECRET) --set main.tokenSecretNamespace=$(TOKEN_NAMESPACE) --set main.git.repoURL="$(TARGET_CLEAN_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(UUID_HELM_OPTS) $(EXTRA_HELM_OPTS) +endif + +# Helm does the right thing and fetches all the tags and detects the newest one +PATTERN_INSTALL_CHART ?= oci://quay.io/hybridcloudpatterns/pattern-install + +.PHONY: default +default: help + +.PHONY: help +help: ## This help message + @echo "Pattern: $(NAME)" + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^(\s|[a-zA-Z_0-9-])+:.*?##/ { printf " \033[36m%-35s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +.PHONY: install +USE_SECRETS ?= false +install: operator-deploy ## Install the pattern (USE_SECRETS=true to load secrets) + @if [ "$(USE_SECRETS)" != "false" ]; then \ + $(MAKE) post-install; \ + fi + @echo "Installed" + +.PHONY: operator-deploy +operator-deploy operator-upgrade: validate-prereq $(VALIDATE_ORIGIN) validate-cluster + @echo -n "Installing pattern: " + @RUNS=10; \ + WAIT=15; \ + for i in $$(seq 1 $$RUNS); do \ + exec 3>&1 4>&2; \ + OUT=$$( { helm template --include-crds --name-template $(NAME) $(PATTERN_INSTALL_CHART) $(HELM_OPTS) 2>&4 | oc apply -f- 2>&4 1>&3; } 4>&1 3>&1); \ + ret=$$?; \ + exec 3>&- 4>&-; \ + if [ $$ret -eq 0 ]; then \ + break; \ + else \ + echo -n "."; \ + sleep "$$WAIT"; \ + fi; \ + done; \ + if [ $$i -eq $$RUNS ]; then \ + echo "Installation failed [$$i/$$RUNS]. Error:"; \ + echo "$$OUT"; \ + exit 1; \ + fi + @echo "Done" + +.PHONY: validate-prereq +validate-prereq: + $(eval GLOBAL_PATTERN := $(shell yq -r .global.pattern values-global.yaml)) + @if [ $(NAME) != $(GLOBAL_PATTERN) ]; then\ + echo "";\ + echo "WARNING: folder directory is \"$(NAME)\" and global.pattern is set to \"$(GLOBAL_PATTERN)\"";\ + echo "this can create problems. Please make sure they are the same!";\ + echo "";\ + fi + @if [ ! -f /run/.containerenv ]; then\ + echo "Checking prerequisites:";\ + echo -n " Check for python-kubernetes: ";\ + if ! ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1; then echo "Not found"; exit 1; fi;\ + echo "OK";\ + echo -n " Check for kubernetes.core collection: ";\ + if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi;\ + echo "OK";\ + else\ + if [ -f values-global.yaml ]; then\ + OUT=`yq -r '.main.multiSourceConfig.enabled // (.main.multiSourceConfig.enabled = "false")' values-global.yaml`;\ + if [ "$${OUT,,}" = "false" ]; then\ + echo "You must set \".main.multiSourceConfig.enabled: true\" in your 'values-global.yaml' file";\ + echo "because your common subfolder is the slimmed down version with no helm charts in it";\ + exit 1;\ + fi;\ + fi;\ + fi + +.PHONY: validate-origin +validate-origin: + @echo "Checking repository:" + $(eval UPSTREAMURL := $(shell yq -r '.main.git.repoUpstreamURL // (.main.git.repoUpstreamURL = "")' values-global.yaml)) + @if [ -z "$(UPSTREAMURL)" ]; then\ + echo -n " $(TARGET_REPO) - branch '$(TARGET_BRANCH)': ";\ + git ls-remote --exit-code --heads $(TARGET_REPO) $(TARGET_BRANCH) >/dev/null &&\ + echo "OK" || (echo "NOT FOUND"; exit 1);\ + else\ + echo "Upstream URL set to: $(UPSTREAMURL)";\ + echo -n " $(UPSTREAMURL) - branch '$(TARGET_BRANCH)': ";\ + git ls-remote --exit-code --heads $(UPSTREAMURL) $(TARGET_BRANCH) >/dev/null &&\ + echo "OK" || (echo "NOT FOUND"; exit 1);\ + fi + +.PHONY: validate-cluster +validate-cluster: + @echo "Checking cluster:" + @echo -n " cluster-info: " + @oc cluster-info >/dev/null && echo "OK" || (echo "Error"; exit 1) + @echo -n " storageclass: " + @if [ `oc get storageclass -o go-template='{{printf "%d\n" (len .items)}}'` -eq 0 ]; then\ + echo "WARNING: No storageclass found";\ + else\ + echo "OK";\ + fi + +.PHONY: post-install +post-install: + make load-secrets + @echo "Done" + +.PHONY: load-secrets +load-secrets: ## Load secrets into the configured backend + @PATTERN_NAME=$(NAME); \ + PATTERN_DIR=$$(pwd); \ + BACKEND=$$(yq '.global.secretStore.backend' "$$PATTERN_DIR/values-global.yaml" 2>/dev/null); \ + if [ -z "$$BACKEND" -o "$$BACKEND" = "null" ]; then \ + BACKEND="vault"; \ + fi; \ + ansible-playbook -e pattern_name="$$PATTERN_NAME" -e pattern_dir="$$PATTERN_DIR" -e secrets_backing_store="$$BACKEND" $(EXTRA_PLAYBOOK_OPTS) "rhvp.cluster_utils.process_secrets" diff --git a/README.md b/README.md index c465eb7..8877215 100644 --- a/README.md +++ b/README.md @@ -53,15 +53,8 @@ patternizer init # Initialize pattern files with secrets support patternizer init --with-secrets -# Update existing pattern to use patternizer workflow (with secrets by default) -patternizer update - -# Update existing pattern without secrets -patternizer update --no-secrets - # Show help for specific commands patternizer init help -patternizer update help ``` ### Output Files @@ -71,17 +64,14 @@ The `patternizer init` command generates: - `values-global.yaml` - Global pattern configuration - `values-.yaml` - Cluster group-specific configuration - `pattern.sh` - Utility script for pattern operations +- `Makefile` - Self-contained Makefile with inlined scripts for pattern management When using `--with-secrets`: - `values-secret.yaml.template` - Template for secrets configuration - Modified `pattern.sh` with `USE_SECRETS=true` as default +- Modified `Makefile` with `USE_SECRETS=true` as default -The `patternizer update` command modifies existing patterns: - -- **Removes** `common/` directory (now handled by utility container) -- **Removes** existing `Makefile` (utility container provides its own) -- **Replaces** `pattern.sh` symlink/file with patternizer version -- **Sets** `USE_SECRETS=true` by default (use `--no-secrets` for `USE_SECRETS=false`) +Both `pattern.sh` and `Makefile` provide equivalent functionality for pattern installation and management, with the Makefile offering a more traditional build tool approach. --- @@ -129,75 +119,6 @@ podman run --rm -it -v .:/repo:z quay.io/dminnear/patternizer init --with-secret --- -## Updating Existing Patterns - -If you have an existing Validated Pattern that uses the traditional structure (with `common/` directory and symlinked `pattern.sh`), you can modernize it to use the patternizer workflow: - -### Update Workflow - -1. **Navigate to your existing pattern repository:** - ```bash - cd /path/to/your/existing-pattern - ``` - -2. **Update the pattern (with secrets by default):** - ```bash - podman run --rm -it -v .:/repo:z quay.io/dminnear/patternizer update - ``` - -3. **Or update without secrets:** - ```bash - podman run --rm -it -v .:/repo:z quay.io/dminnear/patternizer update --no-secrets - ``` - -4. **Verify the changes:** - ```bash - # Check that old files are removed - ls -la common/ # Should not exist - ls -la Makefile # Should not exist - - # Check that pattern.sh is now a real file - ls -la pattern.sh # Should be a regular file, not a symlink - - # Verify USE_SECRETS setting - grep "USE_SECRETS" pattern.sh - ``` - -### What Gets Updated - -The `update` command modernizes your pattern by: -- ✅ **Removing** the `common/` directory (functionality moved to utility container) -- ✅ **Removing** the top-level `Makefile` (utility container provides its own) -- ✅ **Replacing** the symlinked `pattern.sh` with the patternizer version -- ✅ **Configuring** secrets support (`USE_SECRETS=true` by default) -- ✅ **Preserving** all your existing values files and custom configurations - -### Example: Updating multicloud-gitops - -```bash -# Clone an existing pattern -git clone https://github.com/validatedpatterns/multicloud-gitops.git -cd multicloud-gitops - -# Before: traditional structure -ls -la common/ # Directory exists -ls -la Makefile # File exists -ls -la pattern.sh # Symlink to ./common/scripts/pattern-util.sh - -# Update to patternizer workflow -podman run --rm -it -v .:/repo:z quay.io/dminnear/patternizer update - -# After: modernized structure -ls -la common/ # Directory removed -ls -la Makefile # File removed -ls -la pattern.sh # Now a regular file with USE_SECRETS=true - -# Use the updated pattern -./pattern.sh make install -``` - ---- - ## Development ### Prerequisites @@ -299,37 +220,34 @@ The project includes comprehensive unit tests across multiple packages: ### Integration Tests -The integration test suite (`test/integration_test.sh`) validates the complete CLI workflow with five comprehensive test scenarios: +The integration test suite (`test/integration_test.sh`) validates the complete CLI workflow with four comprehensive test scenarios: **Test 1: Basic Initialization (Without Secrets)** - Clones the [trivial-pattern](https://github.com/dminnear-rh/trivial-pattern) repository - Runs `patternizer init` and validates generated files - Verifies `values-global.yaml` and `values-prod.yaml` content using YAML normalization - Ensures `pattern.sh` is created with `USE_SECRETS=false` and executable permissions +- Validates `Makefile` is created with `USE_SECRETS=false` and contains proper functionality **Test 2: Initialization with Secrets** - Tests `patternizer init --with-secrets` functionality - Validates secrets-specific applications (vault, golang-external-secrets) are added - Verifies additional namespaces and `values-secret.yaml.template` are created - Ensures `pattern.sh` is configured with `USE_SECRETS=true` +- Validates `Makefile` is configured with `USE_SECRETS=true` and contains secrets support **Test 3: Custom Pattern and Cluster Group Names** - Tests field preservation and intelligent merging of existing configurations - Pre-populates custom `values-global.yaml` with renamed pattern and cluster group - Verifies custom names are preserved while adding missing default configurations - Validates custom cluster group file generation (e.g., `values-renamed-cluster-group.yaml`) +- Ensures both `pattern.sh` and `Makefile` are configured correctly for the custom setup **Test 4: Sequential Execution** - Tests running `patternizer init` followed by `patternizer init --with-secrets` - Validates that the second command properly upgrades the configuration - Ensures final state matches direct `--with-secrets` execution - -**Test 5: Update Existing Pattern** -- Clones the [multicloud-gitops](https://github.com/validatedpatterns/multicloud-gitops) repository (real-world existing pattern) -- Verifies initial traditional pattern structure (`common/` directory, `Makefile`, symlinked `pattern.sh`) -- Runs `patternizer update` to modernize the pattern -- Validates complete cleanup: `common/` and `Makefile` removal, `pattern.sh` symlink replacement -- Ensures new `pattern.sh` has `USE_SECRETS=true` and executable permissions +- Verifies both `pattern.sh` and `Makefile` are updated correctly in the sequential workflow Run integration tests locally: ```bash diff --git a/pattern.sh b/pattern.sh index 99b180b..be6b002 100755 --- a/pattern.sh +++ b/pattern.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/bash function is_available { command -v $1 >/dev/null 2>&1 || { echo >&2 "$1 is required but it's not installed. Aborting."; exit 1; } @@ -9,7 +9,7 @@ function version { } if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then - PATTERN_UTILITY_CONTAINER="quay.io/dminnear/common-utility-container" + PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/utility-container" fi # If PATTERN_DISCONNECTED_HOME is set it will be used to populate both PATTERN_UTILITY_CONTAINER # and PATTERN_INSTALL_CHART automatically @@ -107,10 +107,10 @@ podman run -it --rm --pull=newer \ -e K8S_AUTH_TOKEN \ -e USE_SECRETS="$USE_SECRETS" \ ${PKI_HOST_MOUNT_ARGS} \ - -v "$(pwd)":/pattern-repo \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ ${PODMAN_ARGS} \ ${EXTRA_ARGS} \ + -w "$(pwd)" \ "$PATTERN_UTILITY_CONTAINER" \ - bash -c 'cp -r /pattern-repo/. /pattern && exec "$@"' _ "$@" + $@ diff --git a/src/cmd/init.go b/src/cmd/init.go index 3028ec1..8328db9 100644 --- a/src/cmd/init.go +++ b/src/cmd/init.go @@ -34,7 +34,7 @@ func runInit(withSecrets bool) error { return fmt.Errorf("error processing cluster group values: %w", err) } - // Copy pattern.sh from resources + // Copy pattern.sh and Makefile from resources resourcesDir, err := fileutils.GetResourcePath() if err != nil { return fmt.Errorf("error getting resource path: %w", err) @@ -51,6 +51,18 @@ func runInit(withSecrets bool) error { return fmt.Errorf("error modifying pattern.sh: %w", err) } + // Copy and modify Makefile + makefileSrc := filepath.Join(resourcesDir, "Makefile-pattern") + makefileDst := filepath.Join(repoRoot, "Makefile") + if err := fileutils.CopyFile(makefileSrc, makefileDst); err != nil { + return fmt.Errorf("error copying Makefile: %w", err) + } + + // Set USE_SECRETS in Makefile based on the flag + if err := fileutils.ModifyMakefileScript(makefileDst, withSecrets); err != nil { + return fmt.Errorf("error modifying Makefile: %w", err) + } + // Handle secrets setup if requested if withSecrets { if err := fileutils.HandleSecretsSetup(resourcesDir, repoRoot); err != nil { diff --git a/src/cmd/root.go b/src/cmd/root.go index 1e72417..74dd504 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -10,7 +10,6 @@ import ( // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { var withSecrets bool - var noSecrets bool var rootCmd = &cobra.Command{ Use: "patternizer", @@ -37,27 +36,12 @@ configures the pattern.sh script for secrets usage.`, }, } - var updateCmd = &cobra.Command{ - Use: "update", - Short: "Update existing Validated Pattern to use patternizer workflow", - Long: `Update existing Validated Pattern removes the common/ directory and replaces the pattern.sh script -with the patternizer version. By default, it configures the pattern to use secrets (USE_SECRETS=true). - -Use --no-secrets flag to disable secrets usage (USE_SECRETS=false).`, - RunE: func(cmd *cobra.Command, args []string) error { - // Check if "help" is passed as an argument - if len(args) > 0 && args[0] == "help" { - return cmd.Help() - } - return runUpdate(noSecrets) - }, - } - initCmd.Flags().BoolVar(&withSecrets, "with-secrets", false, "Include secrets template and configure pattern for secrets usage") - updateCmd.Flags().BoolVar(&noSecrets, "no-secrets", false, "Disable secrets usage (USE_SECRETS=false)") rootCmd.AddCommand(initCmd) - rootCmd.AddCommand(updateCmd) + + // Hide the completion command from help since this is primarily used in containers + rootCmd.CompletionOptions.HiddenDefaultCmd = true if err := rootCmd.Execute(); err != nil { os.Exit(1) diff --git a/src/cmd/update.go b/src/cmd/update.go deleted file mode 100644 index 2baf9b3..0000000 --- a/src/cmd/update.go +++ /dev/null @@ -1,79 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/dminnear-rh/patternizer/internal/fileutils" - "github.com/dminnear-rh/patternizer/internal/pattern" -) - -// runUpdate handles the update logic for the update command. -func runUpdate(noSecrets bool) error { - // Get pattern name and repository root - patternName, repoRoot, err := pattern.GetPatternNameAndRepoRoot() - if err != nil { - return fmt.Errorf("error getting pattern information: %w", err) - } - - // Remove the existing pattern.sh file if it exists (it might be a symlink to common/) - patternShDst := filepath.Join(repoRoot, "pattern.sh") - if _, err := os.Stat(patternShDst); err == nil { - fmt.Printf("Removing existing pattern.sh: %s\n", patternShDst) - if err := os.Remove(patternShDst); err != nil { - return fmt.Errorf("error removing existing pattern.sh: %w", err) - } - } else if !os.IsNotExist(err) { - return fmt.Errorf("error checking existing pattern.sh: %w", err) - } - - // Remove the existing Makefile if it exists (utility container provides its own) - makefilePath := filepath.Join(repoRoot, "Makefile") - if _, err := os.Stat(makefilePath); err == nil { - fmt.Printf("Removing existing Makefile: %s\n", makefilePath) - if err := os.Remove(makefilePath); err != nil { - return fmt.Errorf("error removing existing Makefile: %w", err) - } - } else if !os.IsNotExist(err) { - return fmt.Errorf("error checking existing Makefile: %w", err) - } - - // Delete the common/ directory if it exists - commonDir := filepath.Join(repoRoot, "common") - if _, err := os.Stat(commonDir); err == nil { - fmt.Printf("Removing common/ directory: %s\n", commonDir) - if err := os.RemoveAll(commonDir); err != nil { - return fmt.Errorf("error removing common/ directory: %w", err) - } - } else if !os.IsNotExist(err) { - return fmt.Errorf("error checking common/ directory: %w", err) - } - - // Copy pattern.sh from resources - resourcesDir, err := fileutils.GetResourcePath() - if err != nil { - return fmt.Errorf("error getting resource path: %w", err) - } - - patternShSrc := filepath.Join(resourcesDir, "pattern.sh") - if err := fileutils.CopyFile(patternShSrc, patternShDst); err != nil { - return fmt.Errorf("error copying pattern.sh: %w", err) - } - - // Set USE_SECRETS in pattern.sh based on the flag - // By default, update uses secrets (opposite of init) - useSecrets := !noSecrets - if err := fileutils.ModifyPatternShScript(patternShDst, useSecrets); err != nil { - return fmt.Errorf("error modifying pattern.sh: %w", err) - } - - fmt.Printf("Successfully updated pattern '%s' in %s\n", patternName, repoRoot) - if useSecrets { - fmt.Println("Secrets configuration is enabled (USE_SECRETS=true).") - } else { - fmt.Println("Secrets configuration is disabled (USE_SECRETS=false).") - } - - return nil -} diff --git a/src/internal/fileutils/fileutils.go b/src/internal/fileutils/fileutils.go index 5cb733e..cdaddea 100644 --- a/src/internal/fileutils/fileutils.go +++ b/src/internal/fileutils/fileutils.go @@ -96,6 +96,54 @@ func ModifyPatternShScript(patternShPath string, useSecrets bool) error { return nil } +// ModifyMakefileScript modifies the Makefile to set USE_SECRETS to the desired value. +func ModifyMakefileScript(makefilePath string, useSecrets bool) error { + file, err := os.Open(makefilePath) + if err != nil { + return err + } + + var lines []string + scanner := bufio.NewScanner(file) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + file.Close() + + if err := scanner.Err(); err != nil { + return err + } + + // Regex to match the USE_SECRETS line in Makefile format + regex := regexp.MustCompile(`^USE_SECRETS\s*\?=\s*(.+)$`) + + for i, line := range lines { + if matches := regex.FindStringSubmatch(line); matches != nil { + if useSecrets { + lines[i] = "USE_SECRETS ?= true" + } else { + lines[i] = "USE_SECRETS ?= false" + } + break + } + } + + output, err := os.Create(makefilePath) + if err != nil { + return err + } + defer output.Close() + + for _, line := range lines { + _, err := output.WriteString(line + "\n") + if err != nil { + return err + } + } + + return nil +} + // HandleSecretsSetup handles the setup for secrets usage by copying the secrets template // and modifying the pattern.sh script. func HandleSecretsSetup(resourcesDir, repoRoot string) (err error) { diff --git a/test/integration_test.sh b/test/integration_test.sh index aa230ac..e84d285 100755 --- a/test/integration_test.sh +++ b/test/integration_test.sh @@ -11,11 +11,9 @@ NC='\033[0m' # No Color # Test configuration PATTERNIZER_BINARY="${PATTERNIZER_BINARY:-./src/patternizer}" TEST_REPO_URL="https://github.com/dminnear-rh/trivial-pattern.git" -UPDATE_TEST_REPO_URL="https://github.com/validatedpatterns/multicloud-gitops.git" TEST_DIR="/tmp/patternizer-integration-test" TEST_DIR_SECRETS="/tmp/patternizer-integration-test-secrets" TEST_DIR_CUSTOM="/tmp/patternizer-integration-test-custom" -TEST_DIR_UPDATE="/tmp/patternizer-integration-test-update" echo -e "${YELLOW}Starting patternizer integration tests...${NC}" @@ -29,9 +27,6 @@ fi if [ -d "$TEST_DIR_CUSTOM" ]; then rm -rf "$TEST_DIR_CUSTOM" fi -if [ -d "$TEST_DIR_UPDATE" ]; then - rm -rf "$TEST_DIR_UPDATE" -fi # Convert PATTERNIZER_BINARY to absolute path before changing directories PATTERNIZER_BINARY=$(realpath "$PATTERNIZER_BINARY") @@ -178,6 +173,10 @@ else exit 1 fi +# Test 1.5: Check Makefile exists and has USE_SECRETS=false +check_file_exists "Makefile" "Makefile exists (init without secrets)" +check_file_content "Makefile" 'USE_SECRETS ?= false' "Makefile contains USE_SECRETS=false" + echo -e "${GREEN}=== Test 1: Basic initialization PASSED ===${NC}" # @@ -215,6 +214,10 @@ fi # Test 2.5: Check values-secret.yaml.template exists check_file_exists "values-secret.yaml.template" "values-secret.yaml.template file exists" +# Test 2.6: Check Makefile exists and has USE_SECRETS=true +check_file_exists "Makefile" "Makefile exists (init with secrets)" +check_file_content "Makefile" 'USE_SECRETS ?= true' "Makefile contains USE_SECRETS=true" + echo -e "${GREEN}=== Test 2: Initialization with secrets PASSED ===${NC}" # @@ -255,6 +258,10 @@ fi # Test 3.5: Check values-secret.yaml.template exists check_file_exists "values-secret.yaml.template" "values-secret.yaml.template file exists (custom names)" +# Test 3.6: Check Makefile exists and has USE_SECRETS=true +check_file_exists "Makefile" "Makefile exists (custom names with secrets)" +check_file_content "Makefile" 'USE_SECRETS ?= true' "Makefile contains USE_SECRETS=true (custom names)" + echo -e "${GREEN}=== Test 3: Custom pattern and cluster group names (with secrets) PASSED ===${NC}" # @@ -302,81 +309,14 @@ fi # Test 4.5: Check values-secret.yaml.template exists check_file_exists "values-secret.yaml.template" "values-secret.yaml.template file exists (sequential)" -echo -e "${GREEN}=== Test 4: Sequential execution PASSED ===${NC}" - -# -# Test 5: Update existing pattern (multicloud-gitops) -# -echo -e "${YELLOW}=== Test 5: Update existing pattern (multicloud-gitops) ===${NC}" - -cd "$REPO_ROOT" # Go back to repo root -echo -e "${YELLOW}Cloning multicloud-gitops repository for update test...${NC}" -git clone "$UPDATE_TEST_REPO_URL" "$TEST_DIR_UPDATE" -cd "$TEST_DIR_UPDATE" - -echo -e "${YELLOW}Verifying initial state (before update)...${NC}" -# Verify the typical old pattern structure exists -if [ -d "common" ]; then - echo -e "${GREEN}INFO: common/ directory exists (as expected)${NC}" -else - echo -e "${YELLOW}WARNING: common/ directory not found - pattern may already be updated${NC}" -fi +# Test 4.6: Check Makefile exists and has USE_SECRETS=true +check_file_exists "Makefile" "Makefile exists (sequential execution)" +check_file_content "Makefile" 'USE_SECRETS ?= true' "Makefile contains USE_SECRETS=true (sequential)" -if [ -f "Makefile" ]; then - echo -e "${GREEN}INFO: Makefile exists (as expected)${NC}" -else - echo -e "${YELLOW}WARNING: Makefile not found - pattern may already be updated${NC}" -fi - -if [ -L "pattern.sh" ]; then - echo -e "${GREEN}INFO: pattern.sh is a symlink (as expected)${NC}" - echo -e "${GREEN}INFO: pattern.sh points to: $(readlink pattern.sh)${NC}" -elif [ -f "pattern.sh" ]; then - echo -e "${YELLOW}WARNING: pattern.sh exists but is not a symlink${NC}" -else - echo -e "${RED}ERROR: pattern.sh not found at all${NC}" - exit 1 -fi - -echo -e "${YELLOW}Running patternizer update...${NC}" -PATTERNIZER_RESOURCES_DIR="$REPO_ROOT" "$PATTERNIZER_BINARY" update - -echo -e "${YELLOW}Running verification tests for update...${NC}" - -# Test 5.1: Verify common/ directory was removed -check_not_exists "common" "common/ directory was removed" - -# Test 5.2: Verify Makefile was removed -check_not_exists "Makefile" "Makefile was removed" - -# Test 5.3: Verify pattern.sh exists and is a real file (not symlink) -if [ -f "pattern.sh" ] && [ ! -L "pattern.sh" ]; then - echo -e "${GREEN}PASS: pattern.sh exists and is a regular file (not symlink)${NC}" -else - echo -e "${RED}FAIL: pattern.sh is not a regular file${NC}" - if [ -L "pattern.sh" ]; then - echo "pattern.sh is still a symlink pointing to: $(readlink pattern.sh)" - elif [ ! -f "pattern.sh" ]; then - echo "pattern.sh does not exist" - fi - exit 1 -fi - -# Test 5.4: Check pattern.sh has USE_SECRETS=true (default for update) -check_file_content "pattern.sh" 'USE_SECRETS:=true' "pattern.sh contains USE_SECRETS=true (update default)" - -# Test 5.5: Verify pattern.sh is executable -if [ -x "pattern.sh" ]; then - echo -e "${GREEN}PASS: pattern.sh is executable (update)${NC}" -else - echo -e "${RED}FAIL: pattern.sh is not executable (update)${NC}" - exit 1 -fi - -echo -e "${GREEN}=== Test 5: Update existing pattern PASSED ===${NC}" +echo -e "${GREEN}=== Test 4: Sequential execution PASSED ===${NC}" echo -e "${GREEN}All integration tests passed!${NC}" # Clean up cd "$REPO_ROOT" -rm -rf "$TEST_DIR" "$TEST_DIR_SECRETS" "$TEST_DIR_CUSTOM" "$TEST_DIR_SEQUENTIAL" "$TEST_DIR_UPDATE" +rm -rf "$TEST_DIR" "$TEST_DIR_SECRETS" "$TEST_DIR_CUSTOM" "$TEST_DIR_SEQUENTIAL"