Skip to content

Commit 8b9c661

Browse files
committed
remove update command -- add new makefile for patterns
1 parent 9549207 commit 8b9c661

9 files changed

Lines changed: 265 additions & 270 deletions

File tree

Containerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ WORKDIR ${PATTERNIZER_RESOURCES_DIR}
2424

2525
COPY pattern.sh .
2626
COPY values-secret.yaml.template .
27+
COPY Makefile-pattern .
2728

2829
ARG PATTERN_REPO_ROOT=/repo
2930
WORKDIR ${PATTERN_REPO_ROOT}

Makefile-pattern

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
NAME ?= $(shell basename "`pwd`")
2+
3+
ifneq ($(origin TARGET_SITE), undefined)
4+
TARGET_SITE_OPT=--set main.clusterGroupName=$(TARGET_SITE)
5+
endif
6+
7+
# Set this to true if you want to skip any origin validation
8+
DISABLE_VALIDATE_ORIGIN ?= false
9+
ifeq ($(DISABLE_VALIDATE_ORIGIN),true)
10+
VALIDATE_ORIGIN :=
11+
else
12+
VALIDATE_ORIGIN := validate-origin
13+
endif
14+
15+
# This variable can be set in order to pass additional helm arguments from the
16+
# the command line. I.e. we can set things without having to tweak values files
17+
EXTRA_HELM_OPTS ?=
18+
19+
# This variable can be set in order to pass additional ansible-playbook arguments from the
20+
# the command line. I.e. we can set -vvv for more verbose logging
21+
EXTRA_PLAYBOOK_OPTS ?=
22+
23+
# INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:394248
24+
# or
25+
# INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:394248,registry-proxy.engineering.redhat.com/rh-osbs/iib:394249
26+
INDEX_IMAGES ?=
27+
28+
# git branch --show-current is also available as of git 2.22, but we will use this for compatibility
29+
TARGET_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
30+
31+
# Default to the branch remote
32+
TARGET_ORIGIN ?= $(shell git config branch.$(TARGET_BRANCH).remote)
33+
34+
# This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL
35+
# This is because we expect to use tokens for repo authentication as opposed to SSH keys
36+
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://%')
37+
38+
UUID_FILE ?= ~/.config/validated-patterns/pattern-uuid
39+
UUID_HELM_OPTS ?=
40+
41+
# --set values always take precedence over the contents of -f
42+
ifneq ("$(wildcard $(UUID_FILE))","")
43+
UUID := $(shell cat $(UUID_FILE))
44+
UUID_HELM_OPTS := --set main.analyticsUUID=$(UUID)
45+
endif
46+
47+
# Set the secret name *and* its namespace when deploying from private repositories
48+
# The format of said secret is documented here: https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#repositories
49+
TOKEN_SECRET ?=
50+
TOKEN_NAMESPACE ?=
51+
52+
ifeq ($(TOKEN_SECRET),)
53+
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)
54+
else
55+
# 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://
56+
TARGET_CLEAN_REPO=$(shell git ls-remote --get-url --symref $(TARGET_ORIGIN))
57+
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)
58+
endif
59+
60+
# Helm does the right thing and fetches all the tags and detects the newest one
61+
PATTERN_INSTALL_CHART ?= oci://quay.io/hybridcloudpatterns/pattern-install
62+
63+
.PHONY: default
64+
default: help
65+
66+
.PHONY: help
67+
help: ## This help message
68+
@echo "Pattern: $(NAME)"
69+
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\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)
70+
71+
.PHONY: install
72+
USE_SECRETS ?= false
73+
install: operator-deploy ## Install the pattern (USE_SECRETS=true to load secrets)
74+
@if [ "$(USE_SECRETS)" != "false" ]; then \
75+
$(MAKE) post-install; \
76+
fi
77+
@echo "Installed"
78+
79+
.PHONY: operator-deploy
80+
operator-deploy operator-upgrade: validate-prereq $(VALIDATE_ORIGIN) validate-cluster
81+
@echo -n "Installing pattern: "
82+
@RUNS=10; \
83+
WAIT=15; \
84+
for i in $$(seq 1 $$RUNS); do \
85+
exec 3>&1 4>&2; \
86+
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); \
87+
ret=$$?; \
88+
exec 3>&- 4>&-; \
89+
if [ $$ret -eq 0 ]; then \
90+
break; \
91+
else \
92+
echo -n "."; \
93+
sleep "$$WAIT"; \
94+
fi; \
95+
done; \
96+
if [ $$i -eq $$RUNS ]; then \
97+
echo "Installation failed [$$i/$$RUNS]. Error:"; \
98+
echo "$$OUT"; \
99+
exit 1; \
100+
fi
101+
@echo "Done"
102+
103+
.PHONY: validate-prereq
104+
validate-prereq:
105+
$(eval GLOBAL_PATTERN := $(shell yq -r .global.pattern values-global.yaml))
106+
@if [ $(NAME) != $(GLOBAL_PATTERN) ]; then\
107+
echo "";\
108+
echo "WARNING: folder directory is \"$(NAME)\" and global.pattern is set to \"$(GLOBAL_PATTERN)\"";\
109+
echo "this can create problems. Please make sure they are the same!";\
110+
echo "";\
111+
fi
112+
@if [ ! -f /run/.containerenv ]; then\
113+
echo "Checking prerequisites:";\
114+
echo -n " Check for python-kubernetes: ";\
115+
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;\
116+
echo "OK";\
117+
echo -n " Check for kubernetes.core collection: ";\
118+
if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi;\
119+
echo "OK";\
120+
else\
121+
if [ -f values-global.yaml ]; then\
122+
OUT=`yq -r '.main.multiSourceConfig.enabled // (.main.multiSourceConfig.enabled = "false")' values-global.yaml`;\
123+
if [ "$${OUT,,}" = "false" ]; then\
124+
echo "You must set \".main.multiSourceConfig.enabled: true\" in your 'values-global.yaml' file";\
125+
echo "because your common subfolder is the slimmed down version with no helm charts in it";\
126+
exit 1;\
127+
fi;\
128+
fi;\
129+
fi
130+
131+
.PHONY: validate-origin
132+
validate-origin:
133+
@echo "Checking repository:"
134+
$(eval UPSTREAMURL := $(shell yq -r '.main.git.repoUpstreamURL // (.main.git.repoUpstreamURL = "")' values-global.yaml))
135+
@if [ -z "$(UPSTREAMURL)" ]; then\
136+
echo -n " $(TARGET_REPO) - branch '$(TARGET_BRANCH)': ";\
137+
git ls-remote --exit-code --heads $(TARGET_REPO) $(TARGET_BRANCH) >/dev/null &&\
138+
echo "OK" || (echo "NOT FOUND"; exit 1);\
139+
else\
140+
echo "Upstream URL set to: $(UPSTREAMURL)";\
141+
echo -n " $(UPSTREAMURL) - branch '$(TARGET_BRANCH)': ";\
142+
git ls-remote --exit-code --heads $(UPSTREAMURL) $(TARGET_BRANCH) >/dev/null &&\
143+
echo "OK" || (echo "NOT FOUND"; exit 1);\
144+
fi
145+
146+
.PHONY: validate-cluster
147+
validate-cluster:
148+
@echo "Checking cluster:"
149+
@echo -n " cluster-info: "
150+
@oc cluster-info >/dev/null && echo "OK" || (echo "Error"; exit 1)
151+
@echo -n " storageclass: "
152+
@if [ `oc get storageclass -o go-template='{{printf "%d\n" (len .items)}}'` -eq 0 ]; then\
153+
echo "WARNING: No storageclass found";\
154+
else\
155+
echo "OK";\
156+
fi
157+
158+
.PHONY: post-install
159+
post-install:
160+
make load-secrets
161+
@echo "Done"
162+
163+
.PHONY: load-secrets
164+
load-secrets: ## Load secrets into the configured backend
165+
@PATTERN_NAME=$(NAME); \
166+
PATTERN_DIR=$$(pwd); \
167+
BACKEND=$$(yq '.global.secretStore.backend' "$$PATTERN_DIR/values-global.yaml" 2>/dev/null); \
168+
if [ -z "$$BACKEND" -o "$$BACKEND" = "null" ]; then \
169+
BACKEND="vault"; \
170+
fi; \
171+
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"

README.md

Lines changed: 8 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,8 @@ patternizer init
5353
# Initialize pattern files with secrets support
5454
patternizer init --with-secrets
5555

56-
# Update existing pattern to use patternizer workflow (with secrets by default)
57-
patternizer update
58-
59-
# Update existing pattern without secrets
60-
patternizer update --no-secrets
61-
6256
# Show help for specific commands
6357
patternizer init help
64-
patternizer update help
6558
```
6659

6760
### Output Files
@@ -71,17 +64,14 @@ The `patternizer init` command generates:
7164
- `values-global.yaml` - Global pattern configuration
7265
- `values-<cluster_group>.yaml` - Cluster group-specific configuration
7366
- `pattern.sh` - Utility script for pattern operations
67+
- `Makefile` - Self-contained Makefile with inlined scripts for pattern management
7468

7569
When using `--with-secrets`:
7670
- `values-secret.yaml.template` - Template for secrets configuration
7771
- Modified `pattern.sh` with `USE_SECRETS=true` as default
72+
- Modified `Makefile` with `USE_SECRETS=true` as default
7873

79-
The `patternizer update` command modifies existing patterns:
80-
81-
- **Removes** `common/` directory (now handled by utility container)
82-
- **Removes** existing `Makefile` (utility container provides its own)
83-
- **Replaces** `pattern.sh` symlink/file with patternizer version
84-
- **Sets** `USE_SECRETS=true` by default (use `--no-secrets` for `USE_SECRETS=false`)
74+
Both `pattern.sh` and `Makefile` provide equivalent functionality for pattern installation and management, with the Makefile offering a more traditional build tool approach.
8575

8676
---
8777

@@ -129,75 +119,6 @@ podman run --rm -it -v .:/repo:z quay.io/dminnear/patternizer init --with-secret
129119

130120
---
131121

132-
## Updating Existing Patterns
133-
134-
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:
135-
136-
### Update Workflow
137-
138-
1. **Navigate to your existing pattern repository:**
139-
```bash
140-
cd /path/to/your/existing-pattern
141-
```
142-
143-
2. **Update the pattern (with secrets by default):**
144-
```bash
145-
podman run --rm -it -v .:/repo:z quay.io/dminnear/patternizer update
146-
```
147-
148-
3. **Or update without secrets:**
149-
```bash
150-
podman run --rm -it -v .:/repo:z quay.io/dminnear/patternizer update --no-secrets
151-
```
152-
153-
4. **Verify the changes:**
154-
```bash
155-
# Check that old files are removed
156-
ls -la common/ # Should not exist
157-
ls -la Makefile # Should not exist
158-
159-
# Check that pattern.sh is now a real file
160-
ls -la pattern.sh # Should be a regular file, not a symlink
161-
162-
# Verify USE_SECRETS setting
163-
grep "USE_SECRETS" pattern.sh
164-
```
165-
166-
### What Gets Updated
167-
168-
The `update` command modernizes your pattern by:
169-
-**Removing** the `common/` directory (functionality moved to utility container)
170-
-**Removing** the top-level `Makefile` (utility container provides its own)
171-
-**Replacing** the symlinked `pattern.sh` with the patternizer version
172-
-**Configuring** secrets support (`USE_SECRETS=true` by default)
173-
-**Preserving** all your existing values files and custom configurations
174-
175-
### Example: Updating multicloud-gitops
176-
177-
```bash
178-
# Clone an existing pattern
179-
git clone https://github.com/validatedpatterns/multicloud-gitops.git
180-
cd multicloud-gitops
181-
182-
# Before: traditional structure
183-
ls -la common/ # Directory exists
184-
ls -la Makefile # File exists
185-
ls -la pattern.sh # Symlink to ./common/scripts/pattern-util.sh
186-
187-
# Update to patternizer workflow
188-
podman run --rm -it -v .:/repo:z quay.io/dminnear/patternizer update
189-
190-
# After: modernized structure
191-
ls -la common/ # Directory removed
192-
ls -la Makefile # File removed
193-
ls -la pattern.sh # Now a regular file with USE_SECRETS=true
194-
195-
# Use the updated pattern
196-
./pattern.sh make install
197-
```
198-
199-
---
200-
201122
## Development
202123

203124
### Prerequisites
@@ -299,37 +220,34 @@ The project includes comprehensive unit tests across multiple packages:
299220

300221
### Integration Tests
301222

302-
The integration test suite (`test/integration_test.sh`) validates the complete CLI workflow with five comprehensive test scenarios:
223+
The integration test suite (`test/integration_test.sh`) validates the complete CLI workflow with four comprehensive test scenarios:
303224

304225
**Test 1: Basic Initialization (Without Secrets)**
305226
- Clones the [trivial-pattern](https://github.com/dminnear-rh/trivial-pattern) repository
306227
- Runs `patternizer init` and validates generated files
307228
- Verifies `values-global.yaml` and `values-prod.yaml` content using YAML normalization
308229
- Ensures `pattern.sh` is created with `USE_SECRETS=false` and executable permissions
230+
- Validates `Makefile` is created with `USE_SECRETS=false` and contains proper functionality
309231

310232
**Test 2: Initialization with Secrets**
311233
- Tests `patternizer init --with-secrets` functionality
312234
- Validates secrets-specific applications (vault, golang-external-secrets) are added
313235
- Verifies additional namespaces and `values-secret.yaml.template` are created
314236
- Ensures `pattern.sh` is configured with `USE_SECRETS=true`
237+
- Validates `Makefile` is configured with `USE_SECRETS=true` and contains secrets support
315238

316239
**Test 3: Custom Pattern and Cluster Group Names**
317240
- Tests field preservation and intelligent merging of existing configurations
318241
- Pre-populates custom `values-global.yaml` with renamed pattern and cluster group
319242
- Verifies custom names are preserved while adding missing default configurations
320243
- Validates custom cluster group file generation (e.g., `values-renamed-cluster-group.yaml`)
244+
- Ensures both `pattern.sh` and `Makefile` are configured correctly for the custom setup
321245

322246
**Test 4: Sequential Execution**
323247
- Tests running `patternizer init` followed by `patternizer init --with-secrets`
324248
- Validates that the second command properly upgrades the configuration
325249
- Ensures final state matches direct `--with-secrets` execution
326-
327-
**Test 5: Update Existing Pattern**
328-
- Clones the [multicloud-gitops](https://github.com/validatedpatterns/multicloud-gitops) repository (real-world existing pattern)
329-
- Verifies initial traditional pattern structure (`common/` directory, `Makefile`, symlinked `pattern.sh`)
330-
- Runs `patternizer update` to modernize the pattern
331-
- Validates complete cleanup: `common/` and `Makefile` removal, `pattern.sh` symlink replacement
332-
- Ensures new `pattern.sh` has `USE_SECRETS=true` and executable permissions
250+
- Verifies both `pattern.sh` and `Makefile` are updated correctly in the sequential workflow
333251

334252
Run integration tests locally:
335253
```bash

pattern.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/env bash
1+
#!/bin/bash
22

33
function is_available {
44
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 {
99
}
1010

1111
if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then
12-
PATTERN_UTILITY_CONTAINER="quay.io/dminnear/common-utility-container"
12+
PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/utility-container"
1313
fi
1414
# If PATTERN_DISCONNECTED_HOME is set it will be used to populate both PATTERN_UTILITY_CONTAINER
1515
# and PATTERN_INSTALL_CHART automatically
@@ -107,10 +107,10 @@ podman run -it --rm --pull=newer \
107107
-e K8S_AUTH_TOKEN \
108108
-e USE_SECRETS="$USE_SECRETS" \
109109
${PKI_HOST_MOUNT_ARGS} \
110-
-v "$(pwd)":/pattern-repo \
111110
-v "${HOME}":"${HOME}" \
112111
-v "${HOME}":/pattern-home \
113112
${PODMAN_ARGS} \
114113
${EXTRA_ARGS} \
114+
-w "$(pwd)" \
115115
"$PATTERN_UTILITY_CONTAINER" \
116-
bash -c 'cp -r /pattern-repo/. /pattern && exec "$@"' _ "$@"
116+
$@

src/cmd/init.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func runInit(withSecrets bool) error {
3434
return fmt.Errorf("error processing cluster group values: %w", err)
3535
}
3636

37-
// Copy pattern.sh from resources
37+
// Copy pattern.sh and Makefile from resources
3838
resourcesDir, err := fileutils.GetResourcePath()
3939
if err != nil {
4040
return fmt.Errorf("error getting resource path: %w", err)
@@ -51,6 +51,18 @@ func runInit(withSecrets bool) error {
5151
return fmt.Errorf("error modifying pattern.sh: %w", err)
5252
}
5353

54+
// Copy and modify Makefile
55+
makefileSrc := filepath.Join(resourcesDir, "Makefile-pattern")
56+
makefileDst := filepath.Join(repoRoot, "Makefile")
57+
if err := fileutils.CopyFile(makefileSrc, makefileDst); err != nil {
58+
return fmt.Errorf("error copying Makefile: %w", err)
59+
}
60+
61+
// Set USE_SECRETS in Makefile based on the flag
62+
if err := fileutils.ModifyMakefileScript(makefileDst, withSecrets); err != nil {
63+
return fmt.Errorf("error modifying Makefile: %w", err)
64+
}
65+
5466
// Handle secrets setup if requested
5567
if withSecrets {
5668
if err := fileutils.HandleSecretsSetup(resourcesDir, repoRoot); err != nil {

0 commit comments

Comments
 (0)