IMAGE ?= sightingdb
TAG ?= dev
IMAGE_REF := $(IMAGE):$(TAG)
FEATURES ?=
RELEASE ?= sightingdb
NAMESPACE ?= sightingdb
KIND_CLUSTER ?= sightingdb
CHART := helm/sightingdb
VALUES ?= $(CHART)/values-local.yaml
HELM_ARGS ?=
PORT ?= 9999
CARGO ?= cargo
DOCKER ?= docker
HELM ?= helm
KUBECTL ?= kubectl
KIND ?= kind
CONTEXT ?= $(shell $(KUBECTL) config current-context 2>/dev/null)
LOCAL_CONTEXTS ?= docker-desktop rancher-desktop minikube colima kind-% k3d-% k3s-%
KUBE := $(KUBECTL) --context $(CONTEXT) --namespace $(NAMESPACE)
IMAGE_ID = $(shell $(DOCKER) image inspect $(IMAGE_REF) --format '{{.Id}}' 2>/dev/null)
HELM_INSTALL = $(HELM) upgrade --install $(RELEASE) $(CHART) \
--kube-context $(CONTEXT) \
--namespace $(NAMESPACE) --create-namespace \
--values $(VALUES) \
--set image.repository=$(IMAGE) --set image.tag=$(TAG) \
$(if $(IMAGE_ID),--set podAnnotations.sightingdb-image-id=$(IMAGE_ID),) \
$(HELM_ARGS)
.DEFAULT_GOAL := help
.PHONY: help all sightingdb build release test fmt lint check clean \
image image-push image-exists load deploy kind-up kind-down kind-load \
install upgrade uninstall local-only \
status logs port-forward admin-key acl rotate-key smoke chart-test wait \
refresh restart dev teardown \
helm-lint helm-template helm-package sync-version
all: build
build:
$(CARGO) build
sightingdb: build
release:
$(CARGO) build --release
test:
$(CARGO) test
fmt:
$(CARGO) fmt
lint:
$(CARGO) fmt --all -- --check
$(CARGO) clippy --all-targets -- -D warnings
$(CARGO) test --locked
check: lint helm-lint
clean:
$(CARGO) clean
image:
$(DOCKER) build -f docker/Dockerfile -t $(IMAGE_REF) \
$(if $(FEATURES),--build-arg FEATURES=$(FEATURES),) .
@echo "built $(IMAGE_REF)"
image-push: image
$(DOCKER) push $(IMAGE_REF)
local-only:
@if [ -z "$(CONTEXT)" ]; then \
echo "kubectl has no current context, and CONTEXT was not set."; exit 1; \
fi; \
if [ "$(ALLOW_ANY_CONTEXT)" = "1" ]; then \
echo "WARNING: acting on '$(CONTEXT)' because ALLOW_ANY_CONTEXT=1"; \
exit 0; \
fi; \
for pattern in $(LOCAL_CONTEXTS); do \
case "$(CONTEXT)" in $${pattern//\%/*}) exit 0 ;; esac; \
done; \
echo "Refusing to act on kube context '$(CONTEXT)': it is not a local cluster."; \
echo; \
echo "This Makefile installs and deletes things, and runs a test that writes"; \
echo "data. It only does that to a cluster on this machine — one of:"; \
echo " $(LOCAL_CONTEXTS)"; \
echo; \
echo "Switch context, or pass CONTEXT=<local cluster>."; \
echo "If you genuinely mean this one: make <target> ALLOW_ANY_CONTEXT=1"; \
exit 1
image-exists:
@$(DOCKER) image inspect $(IMAGE_REF) >/dev/null 2>&1 || { \
echo "$(IMAGE_REF) has not been built. Run 'make image' first, or 'make dev' which does."; \
exit 1; \
}
load: local-only image-exists
@context=$$($(KUBECTL) config current-context 2>/dev/null); \
if [ -z "$$context" ]; then echo "kubectl has no current context"; exit 1; fi; \
case "$$context" in \
kind-*) $(KIND) load docker-image $(IMAGE_REF) --name "$${context#kind-}" ;; \
k3d-*) k3d image import $(IMAGE_REF) --cluster "$${context#k3d-}" ;; \
minikube) minikube image load $(IMAGE_REF) ;; \
docker-desktop|rancher-desktop) \
node=$$($(KUBECTL) --context "$$context" get nodes -o jsonpath='{.items[0].metadata.name}' 2>/dev/null); \
if [ -n "$$node" ] && $(DOCKER) exec "$$node" true >/dev/null 2>&1; then \
echo "importing $(IMAGE_REF) into $$node"; \
$(DOCKER) save $(IMAGE_REF) | $(DOCKER) exec -i "$$node" ctr -n k8s.io images import - >/dev/null; \
echo "imported"; \
else \
echo "$$context shares this Docker daemon; $(IMAGE_REF) is already visible to it"; \
fi ;; \
*) \
echo "Context '$$context' is not a local cluster this can hand an image to."; \
echo "Push it somewhere the cluster can pull from:"; \
echo " make image-push IMAGE=registry.example.com/sightingdb TAG=$(TAG)"; \
exit 1 ;; \
esac
kind-up:
@if $(KIND) get clusters 2>/dev/null | grep -qx $(KIND_CLUSTER); then \
echo "kind cluster $(KIND_CLUSTER) is already up"; \
else \
$(KIND) create cluster --name $(KIND_CLUSTER); \
fi
kind-load: local-only image-exists
$(KIND) load docker-image $(IMAGE_REF) --name $(KIND_CLUSTER)
kind-down:
$(KIND) delete cluster --name $(KIND_CLUSTER)
helm-lint:
$(HELM) lint $(CHART)
$(HELM) lint $(CHART) --values $(VALUES)
helm-template:
@$(HELM) template $(RELEASE) $(CHART) --namespace $(NAMESPACE) \
--values $(VALUES) --set image.repository=$(IMAGE) --set image.tag=$(TAG) \
$(HELM_ARGS)
helm-package:
$(HELM) package $(CHART)
sync-version:
@version=$$(grep -m1 '^version = ' Cargo.toml | cut -d'"' -f2); \
sed -i.bak "s/^appVersion: .*/appVersion: \"$$version\"/" $(CHART)/Chart.yaml; \
rm -f $(CHART)/Chart.yaml.bak; \
sed -i.bak "s/^ version: \".*\"/ version: \"$$version\"/" doc/openapi.yaml; \
rm -f doc/openapi.yaml.bak; \
echo "chart appVersion and the OpenAPI version are now $$version"
install: local-only
$(HELM_INSTALL)
upgrade: install
uninstall: local-only
$(HELM) uninstall $(RELEASE) --kube-context $(CONTEXT) --namespace $(NAMESPACE)
status:
$(KUBE) get statefulset,pod,svc,pvc,job -l app.kubernetes.io/instance=$(RELEASE)
logs:
$(KUBE) logs -f statefulset/$(RELEASE) --all-containers
port-forward:
@echo "http://localhost:$(PORT)/_management/ (key: make admin-key)"
$(KUBE) port-forward svc/$(RELEASE) $(PORT):$(PORT)
admin-key:
@$(KUBE) get secret $(RELEASE)-acl -o jsonpath='{.data.admin-key}' | base64 -d; echo
acl:
@$(KUBE) get secret $(RELEASE)-acl -o jsonpath='{.data.acl\.toml}' | base64 -d
@echo
@echo "# Keys created in the management interface live on the volume, not here."
rotate-key: local-only
@echo "This forgets every key the release knows, including any made in the"
@echo "management interface, and generates one new admin key."
@printf "Continue? [y/N] "; read answer; [ "$$answer" = y ] || [ "$$answer" = Y ] || exit 1
@echo "old key: $$($(KUBE) get secret $(RELEASE)-acl -o jsonpath='{.data.admin-key}' | base64 -d)"
$(KUBE) delete secret $(RELEASE)-acl
$(HELM_INSTALL) --set acl.overwriteOnStart=true
@$(KUBE) rollout status statefulset/$(RELEASE) --timeout=300s
@echo "new key: $$($(KUBE) get secret $(RELEASE)-acl -o jsonpath='{.data.admin-key}' | base64 -d)"
@echo
@echo "Put acl.overwriteOnStart back to its default with 'make install', or"
@echo "keys made in the interface will be replaced at every restart."
chart-test: local-only
$(HELM) test $(RELEASE) --kube-context $(CONTEXT) --namespace $(NAMESPACE) --logs
wait:
$(KUBE) rollout status statefulset/$(RELEASE) --timeout=300s
smoke: local-only
@set -e; \
$(KUBE) port-forward svc/$(RELEASE) $(PORT):$(PORT) >/dev/null 2>&1 & \
forward=$$!; \
trap "kill $$forward 2>/dev/null || true" EXIT; \
key=$$($(KUBE) get secret $(RELEASE)-acl -o jsonpath='{.data.admin-key}' | base64 -d); \
for i in $$(seq 1 30); do \
curl -fsSk "http://localhost:$(PORT)/health" >/dev/null 2>&1 && break; \
sleep 1; \
done; \
echo "health: $$(curl -fsSk http://localhost:$(PORT)/health)"; \
curl -fsSk -H "Authorization: $$key" "http://localhost:$(PORT)/w/smoke/test?val=127.0.0.1" >/dev/null; \
echo "sighting: $$(curl -fsSk -H "Authorization: $$key" 'http://localhost:$(PORT)/r/smoke/test?val=127.0.0.1&noshadow')"; \
echo "stix: $$(curl -fsSk -X POST -H "Authorization: $$key" -H 'Content-Type: application/json' \
-d '{"namespace":"smoke/test"}' http://localhost:$(PORT)/_api/stix | head -c 120)..."
refresh: image load install wait
@echo "$(RELEASE) is running the image you just built"
restart: local-only
$(KUBE) rollout restart statefulset/$(RELEASE)
deploy: image load install wait smoke
@echo
@echo "SightingDB is running in $$($(KUBECTL) config current-context)."
@echo " make port-forward reach it at http://localhost:$(PORT)/_management/"
@echo " make admin-key the API key to sign in with"
dev: image kind-up kind-load install wait smoke
@echo
@echo "SightingDB is running in kind cluster '$(KIND_CLUSTER)'."
@echo " make port-forward reach it at http://localhost:$(PORT)/_management/"
@echo " make admin-key the API key to sign in with"
@echo " make chart-test run the chart's own test"
@echo " make teardown delete the cluster and everything in it"
teardown: kind-down
help:
@printf "SightingDB — build it, and run it locally on Kubernetes.\n\n"
@printf " \033[1mmake dev\033[0m image → a kind cluster of its own → chart → smoke test\n"
@printf " \033[1mmake deploy\033[0m image → the cluster kubectl already points at → chart → smoke test\n"
@awk 'BEGIN {FS = ":.*##"} \
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5); next } \
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 }' \
$(MAKEFILE_LIST)
@printf "\n\033[1mVariables\033[0m (override on the command line: make dev TAG=wip)\n"
@printf " %-15s %s\n" IMAGE "$(IMAGE)"
@printf " %-15s %s\n" TAG "$(TAG)"
@printf " %-15s %s\n" CONTEXT "$(CONTEXT)"
@printf " %-15s %s\n" NAMESPACE "$(NAMESPACE)"
@printf " %-15s %s\n" RELEASE "$(RELEASE)"
@printf " %-15s %s\n" KIND_CLUSTER "$(KIND_CLUSTER)"
@printf " %-15s %s\n" VALUES "$(VALUES)"
@printf " %-15s %s\n" PORT "$(PORT)"
@printf " %-15s %s\n" FEATURES "$(FEATURES)$(if $(FEATURES),, (empty: link OpenSSL dynamically))"
@printf " %-15s %s\n" HELM_ARGS "$(HELM_ARGS)$(if $(HELM_ARGS),, (empty: passed to helm upgrade --install))"