Skip to content

Commit a8df829

Browse files
authored
FEATURE/crossplane (#13)
* Feature: Crossplane Support - use crossplane to deploy, track and manage the state of cloud resources like s3, vpc peering, fsx (netapp ontap) and route53. - dynamic automatic route53 failover during DR activities - argocd resource actions to provide better UX during failover activities. * sanitizing values files * update README.md * update values-hub for linter * update README for super-linter * linter * natural language linter * removing unnecessary values file * updated readme and added example aws iam policy * fixed readme and iam policy for linter
1 parent f556e88 commit a8df829

115 files changed

Lines changed: 8208 additions & 1690 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.ansible-lint

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ exclude_paths:
99
- common/
1010
- tests/
1111

12+
# Role `route53_failover_terraform` intentionally exposes `route53_failover_*` variables
13+
# to playbooks; renaming to `route53_failover_terraform_*` would be a breaking API change.
14+
skip_list:
15+
- var-naming[no-role-prefix]
16+
1217
# warn_list:
1318
# - yaml
1419
# - schema

.github/linters/trivy.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ scan:
88
- MEDIUM
99
- CRITICAL
1010
- HIGH
11+
# Legacy Terraform modules: starter defaults trip many AWS hardening rules; Crossplane is the primary path.
12+
skip-dirs:
13+
- terraform/fsx-ontap
14+
- terraform/terraform-state

.github/workflows/jsonschema.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
# disable shellcheck of single quotes in yq
5454
# shellcheck disable=2016
5555
yq eval-all '. as $item ireduce ({}; . * $item )' values-global.yaml "$i" > tmp.yaml
56-
check-jsonschema --fill-defaults --schemafile https://raw.githubusercontent.com/validatedpatterns/clustergroup-chart/refs/heads/main/values.schema.json tmp.yaml
56+
check-jsonschema --fill-defaults --schemafile https://raw.githubusercontent.com/day0hero/clustergroup-chart/refs/heads/resource_actions/values.schema.json tmp.yaml
5757
rm -f tmp.yaml
5858
done
5959

.github/workflows/superlinter.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ jobs:
2121
fetch-depth: 0
2222
persist-credentials: false
2323

24+
# When VALIDATE_TRIVY is true, super-linter runs `helm template` on charts; Crossplane is a wrapper chart and needs dependencies present.
25+
- name: Helm dependency build (crossplane)
26+
run: |
27+
helm repo add crossplane-stable https://charts.crossplane.io/stable --force-update
28+
helm dependency build charts/all/crossplane
29+
2430
################################
2531
# Run Linter against code base #
2632
################################
@@ -35,6 +41,7 @@ jobs:
3541
VALIDATE_BASH: false
3642
VALIDATE_CHECKOV: false
3743
VALIDATE_JSCPD: false
44+
# JSON is formatted with Biome in super-linter v8. Local parity: npm ci && npm run lint:biome (see package.json / biome.json).
3845
VALIDATE_JSON_PRETTIER: false
3946
VALIDATE_MARKDOWN_PRETTIER: false
4047
VALIDATE_PYTHON_PYLINT: false

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
node_modules/
2+
3+
# Helm dependency archives (built in CI before super-linter; keep Chart.lock)
4+
charts/all/crossplane/charts/
5+
16
*~
27
*.swp
38
*.swo

.trivyignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,3 @@ AVD-KSV-0020 # Container 'apache' of Deployment 'hello-world' should set 'securi
33
AVD-KSV-0021 # Container 'apache' of Deployment 'hello-world' should set 'securityContext.runAsGroup' > 10000. Not needed on OCP
44
AVD-KSV-0014 # Readonly root filesystem does not work with httpd ubi images
55
AVD-KSV-0125 # Container apache in deployment hello-world (namespace: default) uses an image from an untrusted registry. registry.access.redhat.com is trusted
6-
7-

Makefile

Lines changed: 49 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,14 @@ include Makefile-common
88
# NetApp DR Starter Kit - Infrastructure Targets
99
# =============================================================================
1010
#
11-
# These targets manage the DR infrastructure:
12-
# - Terraform state backend (S3 bucket + DynamoDB from values-trident.yaml)
11+
# These targets manage the DR infrastructure via Crossplane (GitOps):
1312
# - Cluster discovery (auto-detect VPC, CIDR, region from kubeconfigs)
14-
# - VPC peering between prod and DR clusters
15-
# - FSx for NetApp ONTAP filesystems in each region
13+
# - Crossplane values written to Helm values files
14+
# - ArgoCD syncs Crossplane managed resources (FSx, S3, VPC Peering, Route53)
1615
#
1716
# Required:
1817
# PROD_KUBECONFIG - path to production cluster kubeconfig
1918
# DR_KUBECONFIG - path to DR cluster kubeconfig
20-
#
21-
# The terraform state bucket name is read from values-trident.yaml
22-
# (.terraform.state.bucket) automatically by the playbook.
2319
# =============================================================================
2420

2521
# Expand ~ in kubeconfig paths
@@ -82,130 +78,64 @@ endef
8278

8379
##@ NetApp DR Infrastructure
8480

85-
.PHONY: build-dr
86-
build-dr: ## Build complete DR infrastructure (state backend, VPC peering, FSx filesystems)
87-
$(call _validate_kubeconfigs,build-dr)
88-
@echo "=========================================="
89-
@echo "Building DR Infrastructure"
90-
@echo " Prod kubeconfig: $(_PROD_KUBECONFIG)"
91-
@echo " DR kubeconfig: $(_DR_KUBECONFIG)"
92-
@echo " State bucket: from values-trident.yaml"
93-
@echo "=========================================="
94-
ansible-playbook $(EXTRA_PLAYBOOK_OPTS) ansible/dr-setup.yaml \
95-
-e @ansible/dr-vars.yml \
96-
$(_DR_EXTRA_VARS)
81+
##@ Crossplane Infrastructure
9782

98-
.PHONY: destroy-dr
99-
destroy-dr: ## Destroy DR infrastructure (FSx filesystems and VPC peering; preserves state bucket)
100-
$(call _validate_kubeconfigs,destroy-dr)
83+
.PHONY: crossplane-setup
84+
crossplane-setup: ## Discover clusters and write Crossplane values (then commit+push for ArgoCD)
85+
$(call _validate_kubeconfigs,crossplane-setup)
10186
@echo "=========================================="
102-
@echo "Destroying DR Infrastructure"
87+
@echo "Crossplane Infrastructure Setup"
10388
@echo " Prod kubeconfig: $(_PROD_KUBECONFIG)"
10489
@echo " DR kubeconfig: $(_DR_KUBECONFIG)"
105-
@echo " Note: State bucket is preserved"
90+
@echo " (Route53: configure AWS credentials for hosted-zone discovery — no zone ID in Git)"
10691
@echo "=========================================="
107-
ansible-playbook $(EXTRA_PLAYBOOK_OPTS) ansible/dr-setup.yaml \
108-
-e @ansible/dr-vars.yml \
109-
$(_DR_EXTRA_VARS) \
110-
-e destroy_resources=true
111-
112-
# Trident Protect AppVault S3 bucket from values-global.yaml
113-
_APPVAULT_BUCKET := $(shell yq '.global.tridentProtect.appVault.s3.bucketName // ""' values-global.yaml 2>/dev/null)
114-
_APPVAULT_REGION := $(shell yq '.global.tridentProtect.appVault.s3.region // ""' values-global.yaml 2>/dev/null)
115-
116-
.PHONY: create-appvault-bucket
117-
create-appvault-bucket: ## Create the S3 bucket for Trident Protect AppVault
118-
@if [ -z "$(_APPVAULT_BUCKET)" ]; then \
119-
echo "Error: Could not read .global.tridentProtect.appVault.s3.bucketName from values-global.yaml"; \
120-
exit 1; \
121-
fi
122-
@if [ -z "$(_APPVAULT_REGION)" ]; then \
123-
echo "Error: Could not read .global.tridentProtect.appVault.s3.region from values-global.yaml"; \
124-
exit 1; \
125-
fi
126-
@echo "=========================================="
127-
@echo "Trident Protect AppVault S3 Bucket"
92+
ansible-playbook $(EXTRA_PLAYBOOK_OPTS) ansible/crossplane-setup.yaml \
93+
-e @ansible/crossplane-vars.yml \
94+
$(_DR_EXTRA_VARS)
95+
@echo ""
12896
@echo "=========================================="
129-
@echo " Bucket: $(_APPVAULT_BUCKET)"
130-
@echo " Region: $(_APPVAULT_REGION)"
97+
@echo " Values files updated. Next steps:"
98+
@echo " 1. git diff (review changes)"
99+
@echo " 2. git add -A && git commit"
100+
@echo " 3. git push (triggers ArgoCD sync)"
101+
@echo " 4. kubectl get managed (monitor)"
131102
@echo "=========================================="
132-
@if aws s3api head-bucket --bucket $(_APPVAULT_BUCKET) --region $(_APPVAULT_REGION) 2>/dev/null; then \
133-
echo "Bucket '$(_APPVAULT_BUCKET)' already exists - nothing to do."; \
134-
else \
135-
echo "Creating S3 bucket '$(_APPVAULT_BUCKET)' in $(_APPVAULT_REGION)..."; \
136-
if [ "$(_APPVAULT_REGION)" = "us-east-1" ]; then \
137-
aws s3api create-bucket \
138-
--bucket $(_APPVAULT_BUCKET) \
139-
--region $(_APPVAULT_REGION); \
140-
else \
141-
aws s3api create-bucket \
142-
--bucket $(_APPVAULT_BUCKET) \
143-
--region $(_APPVAULT_REGION) \
144-
--create-bucket-configuration LocationConstraint=$(_APPVAULT_REGION); \
145-
fi; \
146-
echo "Bucket '$(_APPVAULT_BUCKET)' created."; \
147-
fi
148103

149-
# State bucket name, DynamoDB table, and region from values-trident.yaml
150-
_STATE_BUCKET := $(shell yq '.terraform.state.bucket // ""' values-trident.yaml 2>/dev/null)
151-
_DYNAMODB_TABLE := $(shell yq '.terraform.state.dynamodb_table // "terraform-state-lock"' values-trident.yaml 2>/dev/null)
152-
_STATE_REGION := $(shell yq '.terraform.state.region // "us-east-1"' values-trident.yaml 2>/dev/null)
104+
##@ Crossplane DR Teardown
153105

154-
.PHONY: destroy-terraform-state
155-
destroy-terraform-state: ## Destroy the Terraform state backend (S3 bucket + DynamoDB table) - DESTRUCTIVE
156-
@if [ -z "$(_STATE_BUCKET)" ]; then \
157-
echo "Error: Could not read .terraform.state.bucket from values-trident.yaml"; \
158-
exit 1; \
159-
fi
106+
.PHONY: destroy-dr dr-destroy
107+
# dr-destroy is an alias (same recipe as destroy-dr)
108+
destroy-dr dr-destroy: ## Destroy DR infrastructure (pauses ArgoCD, cleans ONTAP, deletes Crossplane resources)
109+
$(call _validate_kubeconfigs,destroy-dr)
160110
@echo "=========================================="
161-
@echo "WARNING: Destroying Terraform State Backend"
111+
@echo "WARNING: Destroying DR Infrastructure"
162112
@echo "=========================================="
163-
@echo " S3 Bucket: $(_STATE_BUCKET)"
164-
@echo " DynamoDB Table: $(_DYNAMODB_TABLE)"
165-
@echo " Region: $(_STATE_REGION)"
113+
@echo " Prod kubeconfig: $(_PROD_KUBECONFIG)"
114+
@echo " DR kubeconfig: $(_DR_KUBECONFIG)"
166115
@echo ""
167-
@echo " This will permanently delete all Terraform state!"
168-
@echo " Make sure you have destroyed all DR resources first"
169-
@echo " (make destroy-dr) or they will become orphaned."
116+
@echo " This will:"
117+
@echo " 1. Pause ArgoCD auto-sync on both clusters"
118+
@echo " 2. Clean up ONTAP (SnapMirror, volumes, peers)"
119+
@echo " 3. Scrub AppVault S3 bucket"
120+
@echo " 4. Delete Crossplane-managed AWS resources (hub oc delete)"
121+
@echo " 5. AWS CLI fallback: remove any remaining FSx + VPC peering by tag/VPC pair"
122+
@echo " (playbook fails if they still exist — needs aws configure / env creds)"
170123
@echo "=========================================="
171124
@echo ""
172125
@read -p "Type 'yes' to confirm: " confirm && [ "$$confirm" = "yes" ] || (echo "Aborted."; exit 1)
173-
@echo ""
174-
@echo "--- Emptying S3 bucket (all object versions) ---"
175-
@aws s3api list-object-versions \
176-
--bucket $(_STATE_BUCKET) \
177-
--region $(_STATE_REGION) \
178-
--query '{Objects: Versions[].{Key:Key,VersionId:VersionId}}' \
179-
--output json 2>/dev/null | \
180-
python3 -c "\
181-
import sys, json, subprocess; \
182-
data = json.load(sys.stdin); \
183-
objects = data.get('Objects') or []; \
184-
[subprocess.run(['aws', 's3api', 'delete-objects', '--bucket', '$(_STATE_BUCKET)', '--region', '$(_STATE_REGION)', '--delete', json.dumps({'Objects': objects[i:i+1000], 'Quiet': True})], check=True) for i in range(0, len(objects), 1000)] if objects else None; \
185-
print(f'Deleted {len(objects)} object versions') if objects else print('No object versions to delete')" || true
186-
@echo "--- Removing delete markers ---"
187-
@aws s3api list-object-versions \
188-
--bucket $(_STATE_BUCKET) \
189-
--region $(_STATE_REGION) \
190-
--query '{Objects: DeleteMarkers[].{Key:Key,VersionId:VersionId}}' \
191-
--output json 2>/dev/null | \
192-
python3 -c "\
193-
import sys, json, subprocess; \
194-
data = json.load(sys.stdin); \
195-
objects = data.get('Objects') or []; \
196-
[subprocess.run(['aws', 's3api', 'delete-objects', '--bucket', '$(_STATE_BUCKET)', '--region', '$(_STATE_REGION)', '--delete', json.dumps({'Objects': objects[i:i+1000], 'Quiet': True})], check=True) for i in range(0, len(objects), 1000)] if objects else None; \
197-
print(f'Removed {len(objects)} delete markers') if objects else print('No delete markers to remove')" || true
198-
@echo "--- Deleting S3 bucket ---"
199-
@aws s3api delete-bucket --bucket $(_STATE_BUCKET) --region $(_STATE_REGION) \
200-
&& echo "S3 bucket '$(_STATE_BUCKET)' deleted" \
201-
|| echo "S3 bucket '$(_STATE_BUCKET)' not found (already deleted?)"
202-
@echo "--- Deleting DynamoDB table ---"
203-
@aws dynamodb delete-table --table-name $(_DYNAMODB_TABLE) --region $(_STATE_REGION) > /dev/null 2>&1 \
204-
&& echo "DynamoDB table '$(_DYNAMODB_TABLE)' deleted" \
205-
|| echo "DynamoDB table '$(_DYNAMODB_TABLE)' not found (already deleted?)"
206-
@echo "--- Cleaning local working directory ---"
207-
@rm -rf /tmp/netapp-dr-terraform/terraform-state
208-
@echo ""
209-
@echo "=========================================="
210-
@echo " Terraform State Backend Destroyed"
211-
@echo "=========================================="
126+
ansible-playbook $(EXTRA_PLAYBOOK_OPTS) ansible/crossplane-destroy.yaml \
127+
-e @ansible/crossplane-vars.yml \
128+
$(_DR_EXTRA_VARS)
129+
130+
##@ Code quality (Biome — JSON formatter parity with GitHub super-linter v8)
131+
132+
.PHONY: deps-js lint-biome format-biome
133+
134+
deps-js: ## Install Node devDependencies (Biome); uses package-lock.json
135+
npm ci
136+
137+
lint-biome: deps-js ## Run Biome check (format + lint) on the repo
138+
npm run lint:biome
139+
140+
format-biome: deps-js ## Write Biome formatting (e.g. Grafana dashboard JSON)
141+
npm run format:biome

0 commit comments

Comments
 (0)