-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMakefile
412 lines (364 loc) · 14.1 KB
/
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# special makefile variables
.DEFAULT_GOAL := help
.RECIPEPREFIX := >
# recursively expanded variables
SHELL = /usr/bin/sh
TRUTHY_VALUES = \
true\
1
ANSIBLE_SECRETS_DIR_PATH = ./playbooks/vars
ANSIBLE_SECRETS_FILE = ansible_secrets.yml
ANSIBLE_SECRETS_FILE_PATH = ${ANSIBLE_SECRETS_DIR_PATH}/${ANSIBLE_SECRETS_FILE}
BITWARDEN_ANSIBLE_SECRETS_ITEMID = a50012a3-3685-454c-b480-adf300ec834c
BITWARDEN_SSH_KEYS_DIR_PATH = ./playbooks/ssh_keys
BITWARDEN_SSH_KEYS_ITEMID = 9493f9e9-82e0-458f-b609-ae20004f8227
BITWARDEN_SSH_KEYS = \
id_rsa_ron.pub\
id_rsa_roxanne.pub
BITWARDEN_RSA_KEYS_DIR_PATH = ./playbooks/rsa_keys
BITWARDEN_RSA_KEYS_ITEMID = 0a2e75a3-7f1d-4720-ad05-aec2016c4ba9
BITWARDEN_RSA_KEYS = \
poseidon_k8s_ca.key
BITWARDEN_TLS_CERTS_DIR_PATH = ./playbooks/certs
BITWARDEN_TLS_CERTS_ITEMID = 0857a42d-0d60-4ecc-8c43-ae200066a2b3
BITWARDEN_TLS_CERTS = \
liberachat.pem\
oftc.pem\
poseidon_k8s_ca.crt
export PROJECT_VAGRANT_CONFIGURATION_FILE = vagrant_ansible_vars.json
export ANSIBLE_CONFIG = ./ansible.cfg
# targets
HELP = help
SETUP = setup
INVENTORY = inventory
PRESEED_CFG = preseed.cfg
PRODUCTION = production
STAGING = staging
PRODUCTION_MAINTENANCE = production-maintenance
STAGING_MAINTENANCE = staging-maintenance
ANSIBLE_SECRETS = ansible-secrets
K8S_NODE_IMAGES = k8s-node-images
CONTAINERD_DEB = containerd-deb
EXAMPLES_TEST = examples-test
LINT = lint
FORMAT = format
DEVELOPMENT_SHELL = development-shell
CLEAN = clean
# ansible-secrets actions
PUT = put
# libvirt provider configurations
LIBVIRT = libvirt
export LIBVIRT_PREFIX = $(shell basename ${CURDIR})_
# to be (or can be) passed in at make runtime
VAGRANT_PROVIDER = ${LIBVIRT}
export USE_MAINTENANCE_PLAYBOOK =
export ANSIBLE_EXTRA_VARS =
export ANSIBLE_TAGS = all
# executables
ANSIBLE = ansible
ANSIBLE_GALAXY = ansible-galaxy
ANSIBLE_LINT = ansible-lint
ANSIBLE_PLAYBOOK = ansible-playbook
ANSIBLE_VAULT = ansible-vault
BASH = bash
VIRSH = virsh
VAGRANT = vagrant
PACKER = packer
BUNDLE = bundle
GEM = gem
PKILL = pkill
JQ = jq
BW = bw
PYTHON = python
PIP = pip
NPM = npm
NPX = npx
PRE_COMMIT = pre-commit
CURL = curl
MOLECULE = molecule
MARKDOWNLINT_CLI2 = markdownlint-cli2
PRETTIER = prettier
# simply expanded variables
executables := \
${VIRSH}\
${VAGRANT}\
${PKILL}\
${JQ}\
${ANSIBLE_PLAYBOOK}\
${ANSIBLE_GALAXY}\
${ANSIBLE_LINT}\
${ANSIBLE_VAULT}\
${BUNDLE}\
${GEM}\
${BASH}\
${PYTHON}\
${NPM}\
${PACKER}\
${CURL}
_check_executables := $(foreach exec,${executables},$(if $(shell command -v ${exec}),pass,$(error "No ${exec} in PATH")))
# provider VM identifiers
VM_NAMES := $(shell ${JQ} < ${PROJECT_VAGRANT_CONFIGURATION_FILE} --raw-output '.ansible_host_vars | keys[]')
# include all VMs by default
VMS_INCLUDE := $(shell \
${JQ} \
--raw-output \
< ${PROJECT_VAGRANT_CONFIGURATION_FILE} \
'.vms_include[]? // (.ansible_host_vars | keys[])' \
)
LIBVIRT_DOMAINS := $(shell \
for vm_name in ${VM_NAMES}; do \
if echo "${VMS_INCLUDE}" | grep --quiet "$${vm_name}"; then \
echo "${LIBVIRT_PREFIX}$${vm_name}"; \
fi; \
done \
)
ifeq (${VAGRANT_PROVIDER},${LIBVIRT})
ifneq ($(shell for domain in ${LIBVIRT_DOMAINS}; do ${VIRSH} list --all --name | head --lines -1 | grep "$${domain}"; done),)
VMS_EXISTS := true
endif
# else (${VAGRANT_PROVIDER},${VBOX})
# ifneq ($(code that determines if virtualbox VMs exist),)
# VMS_EXISTS=true
# endif
endif
.PHONY: ${HELP}
${HELP}:
# inspired by the makefiles of the Linux kernel and Mercurial
> @echo 'Common make targets:'
> @echo ' ${SETUP} - installs the distro-independent dependencies for this'
> @echo ' project'
> @echo ' ${INVENTORY} - creates the production inventory file'
> @echo ' ${PRESEED_CFG} - creates the production preseed.cfg file'
> @echo ' ${PRODUCTION} - runs the main playbook for my homelab'
> @echo ' ${STAGING} - runs the main playbook for my homelab, but in a'
> @echo ' virtual environment setup by Vagrant'
> @echo ' ${LINT} - lints the yaml configuration code, json configurations'
> @echo ' and markdown documentation'
> @echo ' ${FORMAT} - formats the markdown documentation'
> @echo ' ${EXAMPLES_TEST} - tests the example'\''s yaml configuration code'
> @echo ' ${ANSIBLE_SECRETS} - manage secrets used by this project, by default'
> @echo ' secrets are pulled into the project'
> @echo ' ${DEVELOPMENT_SHELL} - runs a bash shell with make variables injected into'
> @echo ' it to work with the project'\''s Vagrantfile'
> @echo ' ${CLEAN} - removes files generated from targets'
> @echo 'Common make configurations (e.g. make [config]=1 [targets]):'
> @echo ' ANSIBLE_TAGS - set the tags denoting the tasks/roles/plays for'
> @echo ' Ansible to run (default: all)'
> @echo ' VAGRANT_PROVIDER - set the provider used for the virtual environment'
> @echo ' created by Vagrant (default: libvirt)'
> @echo ' ANSIBLE_VERBOSITY - set the verbosity level when running ansible commands,'
> @echo ' equivalent to the number of -v passed in the command line'
> @echo ' ANSIBLE_SECRETS_ACTION - determines the action to take concerning project'
> @echo ' secrets (options: put)'
> @echo ' ANSIBLE_EXTRA_VARS - pass variables into the ansible-playbook runtime, see'
> @echo ' --extra-vars documentation on argument format'
> @echo ' ANSIBLE_LIMIT - set a pattern to further limit selected hosts, see'
> @echo ' --limit documentation on argument format'
.PHONY: ${SETUP}
${SETUP}:
> ./scripts/chk-vagrant-pkg
> ${BUNDLE} install
> ${VAGRANT} plugin install "$$(find ./vendor -name 'vagrant-libvirt-*.gem')"
> ${NPM} install
> ${PYTHON} -m ${PIP} install --upgrade "${PIP}"
> ${PYTHON} -m ${PIP} install \
--requirement "./requirements.txt" \
--requirement "./requirements-dev.txt"
> ${ANSIBLE_GALAXY} collection install \
--requirements-file "./meta/requirements.yml" \
--collections-path "./ansible/collections"
> ${PRE_COMMIT} install
.PHONY: ${INVENTORY}
${INVENTORY}:
> ${ANSIBLE} \
--module-name "ansible.builtin.template" \
--args 'src=./production.j2 dest=./production mode="644"' \
--extra-vars "@./playbooks/vars/network_configs.yml" \
"localhost"
${PRESEED_CFG}: ./preseed.cfg.j2
> @[ -n "${DISK_ID}" ] \
|| { echo "make: DISK_ID was not passed into make"; exit 1; }
> @[ -n "${ENCRYPTED_ANSIBLE_USER_PASSWORD}" ] \
|| { echo "make: ENCRYPTED_ANSIBLE_USER_PASSWORD was not passed into make"; exit 1; }
> @[ -n "${ENCRYPTION_PASSPHRASE}" ] \
|| { echo "make: ENCRYPTION_PASSPHRASE was not passed into make"; exit 1; }
> ${ANSIBLE} \
--module-name "ansible.builtin.template" \
--args 'src=$< dest=./$@ mode="644"' \
--extra-vars '{"disk_id": "${DISK_ID}","encrypted_password":"$(value ENCRYPTED_ANSIBLE_USER_PASSWORD)","encryption_passphrase":"$(value ENCRYPTION_PASSPHRASE)","encrypt_disks":true,"for_vms":false}' \
"localhost"
.PHONY: ${PRODUCTION}
${PRODUCTION}: export ANSIBLE_LOG_PATH = \
./logs/ansible.log.prod-$(shell date "+%Y-%m-%dT%H:%M:%S-$$(uuidgen | head --bytes 5)")
${PRODUCTION}: ANSIBLE_PLAYBOOK_OPTIONS := --ask-become-pass\
--inventory "production"\
--tags "${ANSIBLE_TAGS}"\
--extra-vars "network_configs_path=./vars/network_configs.yml"
${PRODUCTION}: ANSIBLE_PLAYBOOK_OPTIONS += $(if ${ANSIBLE_LIMIT},--limit ${ANSIBLE_LIMIT},)
${PRODUCTION}: ANSIBLE_PLAYBOOK_OPTIONS += $(if ${ANSIBLE_EXTRA_VARS},--extra-vars ${ANSIBLE_EXTRA_VARS},)
${PRODUCTION}:
> ${ANSIBLE_PLAYBOOK} ${ANSIBLE_PLAYBOOK_OPTIONS} "./playbooks/site.yml"
.PHONY: ${STAGING}
${STAGING}: export ANSIBLE_LOG_PATH = \
./logs/ansible.log.staging-$(shell date "+%Y-%m-%dT%H:%M:%S-$$(uuidgen | head --bytes 5)")
${STAGING}:
ifneq ($(findstring ${VMS_EXISTS},${TRUTHY_VALUES}),)
> ${VAGRANT} up \
--provision \
--no-destroy-on-error \
--provider "${VAGRANT_PROVIDER}"
else
> ${VAGRANT} up --no-destroy-on-error --provider "${VAGRANT_PROVIDER}"
endif
.PHONY: ${PRODUCTION_MAINTENANCE}
${PRODUCTION_MAINTENANCE}: export ANSIBLE_LOG_PATH = \
./logs/ansible.log.prod-$(shell date "+%Y-%m-%dT%H:%M:%S-$$(uuidgen | head --bytes 5)")
${PRODUCTION_MAINTENANCE}: ANSIBLE_PLAYBOOK_OPTIONS := --ask-become-pass\
--inventory "production"\
--tags "${ANSIBLE_TAGS}"
${PRODUCTION_MAINTENANCE}: ANSIBLE_PLAYBOOK_OPTIONS += $(if ${ANSIBLE_LIMIT},--limit ${ANSIBLE_LIMIT},)
${PRODUCTION_MAINTENANCE}:
> ${ANSIBLE_PLAYBOOK} ${ANSIBLE_PLAYBOOK_OPTIONS} "./playbooks/maintenance.yml"
.PHONY: ${STAGING_MAINTENANCE}
${STAGING_MAINTENANCE}:
> ${MAKE} USE_MAINTENANCE_PLAYBOOK="true" "${STAGING}"
.PHONY: ${LINT}
${LINT}:
> @for fil in ${PROJECT_VAGRANT_CONFIGURATION_FILE} "./examples/${PROJECT_VAGRANT_CONFIGURATION_FILE}"; do \
if echo "$${fil}" | grep --quiet "-"; then \
echo "make: $${fil} should not contain a dash in the filename"; \
fi \
done
> ${ANSIBLE_LINT}
> ${NPX} ${MARKDOWNLINT_CLI2} \
'**/*.md' \
'!./node_modules' \
'!./vendor' \
'!./ansible'
.PHONY: ${FORMAT}
${FORMAT}:
> ${NPX} ${PRETTIER} --write './docs/*.md'
.PHONY: ${DEVELOPMENT_SHELL}
${DEVELOPMENT_SHELL}:
> ${BASH} -i
.PHONY: ${ANSIBLE_SECRETS}
${ANSIBLE_SECRETS}:
> @${NPX} ${BW} login --check > /dev/null 2>&1 \
|| { \
echo "make: login to bitwarden and export BW_SESSION before running this target"; \
exit 1; \
}
> @${NPX} ${BW} unlock --check > /dev/null 2>&1 \
|| { \
echo "make: unlock bitwarden vault and export BW_SESSION before running this target"; \
exit 1; \
}
ifeq (${ANSIBLE_SECRETS_ACTION},${PUT})
> ${ANSIBLE_VAULT} encrypt "${ANSIBLE_SECRETS_FILE_PATH}"
> ${NPX} ${BW} delete attachment \
"$$(${NPX} ${BW} list items \
| ${JQ} --raw-output '.[] | select(.attachments?).attachments[] | select(.fileName=="${ANSIBLE_SECRETS_FILE}").id' \
)" \
--itemid "${BITWARDEN_ANSIBLE_SECRETS_ITEMID}"
> ${NPX} ${BW} create attachment \
--file "${ANSIBLE_SECRETS_FILE_PATH}" \
--itemid "${BITWARDEN_ANSIBLE_SECRETS_ITEMID}"
else
> ${NPX} ${BW} get attachment \
"${ANSIBLE_SECRETS_FILE}" \
--itemid "${BITWARDEN_ANSIBLE_SECRETS_ITEMID}" \
--output "${ANSIBLE_SECRETS_DIR_PATH}/"
> ${ANSIBLE_VAULT} decrypt "${ANSIBLE_SECRETS_FILE_PATH}"
> @for ssh_key in ${BITWARDEN_SSH_KEYS}; do \
echo ${NPX} ${BW} get attachment \
"$${ssh_key}" \
--itemid \"${BITWARDEN_SSH_KEYS_ITEMID}\" \
--output \"${BITWARDEN_SSH_KEYS_DIR_PATH}/$${ssh_key}\"; \
\
${NPX} ${BW} get attachment \
"$${ssh_key}" \
--itemid "${BITWARDEN_SSH_KEYS_ITEMID}" \
--output "${BITWARDEN_SSH_KEYS_DIR_PATH}/$${ssh_key}"; \
done
> @for rsa_key in ${BITWARDEN_RSA_KEYS}; do \
echo ${NPX} ${BW} get attachment \
"$${rsa_key}" \
--itemid \"${BITWARDEN_RSA_KEYS_ITEMID}\" \
--output \"${BITWARDEN_RSA_KEYS_DIR_PATH}/$${rsa_key}\"; \
\
${NPX} ${BW} get attachment \
"$${rsa_key}" \
--itemid "${BITWARDEN_RSA_KEYS_ITEMID}" \
--output "${BITWARDEN_RSA_KEYS_DIR_PATH}/$${rsa_key}"; \
done
> @for tls_cert in ${BITWARDEN_TLS_CERTS}; do \
echo ${NPX} ${BW} get attachment \
"$${tls_cert}" \
--itemid \"${BITWARDEN_TLS_CERTS_ITEMID}\" \
--output \"${BITWARDEN_TLS_CERTS_DIR_PATH}/$${tls_cert}\"; \
\
${NPX} ${BW} get attachment \
"$${tls_cert}" \
--itemid "${BITWARDEN_TLS_CERTS_ITEMID}" \
--output "${BITWARDEN_TLS_CERTS_DIR_PATH}/$${tls_cert}"; \
done
endif
.PHONY: ${K8S_NODE_IMAGES}
${K8S_NODE_IMAGES}:
> @[ -n "${PREFERRED_NAMESERVER}" ] \
|| { echo "make: PREFERRED_NAMESERVER was not passed into make"; exit 1; }
> @[ -n "${ANSIBLE_USER_PASSWORD}" ] \
|| { echo "make: ANSIBLE_USER_PASSWORD was not passed into make"; exit 1; }
> @[ -n "${ENCRYPTED_ANSIBLE_USER_PASSWORD}" ] \
|| { echo "make: ENCRYPTED_ANSIBLE_USER_PASSWORD was not passed into make"; exit 1; }
# Password and password hashes could contain the '$' char which make will try
# to perform variable expansion on, hence the value func is used to prevent said
# expansion.
> ANSIBLE_VERBOSITY=0 ${ANSIBLE} \
--module-name "ansible.builtin.template" \
--args 'src=./preseed.cfg.j2 dest=./playbooks/packer/preseed.cfg mode="644"' \
--extra-vars '{"encrypted_password":"$(value ENCRYPTED_ANSIBLE_USER_PASSWORD)","encryption_passphrase":"$(value ENCRYPTION_PASSPHRASE)","encrypt_disks":false,"for_vms":true}' \
"localhost"
> ${PACKER} build \
-var ansible_user_password='$(value ANSIBLE_USER_PASSWORD)' \
-var preferred_nameserver="${PREFERRED_NAMESERVER}" \
-var timezone_offset="$(shell date '+%-:z' | awk -F ":" '{ print $$1"h" }')" \
"./k8s-nodes.pkr.hcl"
.PHONY: ${CONTAINERD_DEB}
${CONTAINERD_DEB}:
> ${CURL} \
--output "./playbooks/files/containerd_1.6.20~ds1-1+b1_amd64.deb" \
"https://snapshot.debian.org/archive/debian/20230409T151531Z/pool/main/c/containerd/containerd_1.6.20~ds1-1%2Bb1_amd64.deb"
.PHONY: ${EXAMPLES_TEST}
${EXAMPLES_TEST}:
> env --chdir "./extensions" ${MOLECULE} converge --scenario-name "examples_docker"
> env --chdir "./extensions" ${MOLECULE} destroy --scenario-name "examples_docker"
.PHONY: ${CLEAN}
${CLEAN}:
> rm --force ./logs/ansible.log.*
> rm --force "./production"
> rm \
--recursive \
--force \
"./playbooks/packer/qemu-poseidon_k8s_controller" \
"./playbooks/packer/qemu-poseidon_k8s_worker"
> rm --force "./playbooks/files/containerd_1.6.20~ds1-1+b1_amd64.deb"
ifeq (${VAGRANT_PROVIDER}, ${LIBVIRT})
# There are times where vagrant may get into defunct state and will be unable to
# remove a domain known to libvirt (through 'vagrant destroy'). Hence the calls
# to virsh destroy and undefine.
> -@for domain in ${LIBVIRT_DOMAINS}; do \
echo ${VIRSH} destroy --domain "$${domain}"; \
${VIRSH} destroy --domain "$${domain}"; \
done
> -@for domain in ${LIBVIRT_DOMAINS}; do \
echo ${VIRSH} undefine --domain "$${domain}"; \
${VIRSH} undefine --domain "$${domain}"; \
done
> ${VAGRANT} destroy --force
# done in recommendation by vagrant when a domain fails to connect via ssh
> rm --recursive --force "./.vagrant"
> ${PKILL} ssh-agent
else
> @echo make: unknown VAGRANT_PROVIDER \'${VAGRANT_PROVIDER}\' passed in
endif