Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
1399 lines (1194 loc) · 70.6 KB
/
Copy pathMakefile
File metadata and controls
1399 lines (1194 loc) · 70.6 KB
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
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Go parameters
GOCMD=go
GOFMT=gofumpt
GOBUILD=$(GOCMD) build
GOTEST=$(GOCMD) test
GOMOD=$(GOCMD) mod
GOLINT=golangci-lint run -c .golangci.yaml
ENVTEST_K8S_VERSION ?= 1.37.0
SETUP_ENVTEST_VERSION ?= v0.25.2-0.20260923145615-d837464d41be
SETUP_ENVTEST = $(CURDIR)/bin/setup-envtest-$(SETUP_ENVTEST_VERSION)
GO_PACKAGE_PATTERNS=./api/... ./cmd/... ./deploy/... ./e2e/... ./hack/... ./internal/... ./pkg/...
# e2e packages hold nothing but files behind the e2e build tag, so `go list`
# needs the tag to see them at all. Without it they are silently skipped by
# both the formatter and the linter, which is how they accumulated whitespace
# and unchecked-error violations that CI never reported. Code generation has no
# business in e2e suites, so GO_PACKAGES stays without the tag.
#
# deploy/ holds the embed.go files and the render tests that guard the shipped
# manifests. Those load on a fresh clone by design (see deploy/machina/embed.go),
# so there is no reason for the linter to skip them, and it did.
GO_PACKAGES=$(shell $(GOCMD) list ./api/... ./cmd/... ./hack/... ./internal/... ./pkg/...)
GO_PACKAGE_DIRS=$(shell $(GOCMD) list -tags e2e -f '{{.Dir}}' $(GO_PACKAGE_PATTERNS))
CONTAINER_ENGINE ?= podman
CONTAINER_REGISTRY ?= ghcr.io/azure
# Unified install namespace for all unbounded components. Each component's
# *_NAMESPACE var derives from this by default, so overriding UNBOUNDED_NAMESPACE
# moves everything at once, while a component var can still be overridden
# individually when needed. Components resolve their runtime namespace from the
# POD_NAMESPACE Downward-API env (see internal/unbounded.SystemNamespace), so a
# non-default namespace lines up end to end; when installing to a non-default
# namespace, pass `kubectl unbounded machine register --namespace <ns>` so the
# SSH secret and its Machine ref land where machina runs.
UNBOUNDED_NAMESPACE ?= unbounded-system
FORGE_BIN=bin/forge
FORGE_CMD=./hack/cmd/forge
RELCTL_BIN=bin/relctl
RELCTL_CMD=./hack/cmd/relctl
AGENT_ARTIFACTS_BUILDER_BIN=bin/agent-artifacts-builder
AGENT_ARTIFACTS_BUILDER_CMD=./hack/cmd/agent-artifacts-builder
INVENTORY_AGENT_BIN=bin/inventory-agent
INVENTORY_AGENT_CMD=./cmd/inventory/inventory-agent
INVENTORY_NAMESPACE ?= $(UNBOUNDED_NAMESPACE)
INVENTORY_MANIFEST_TEMPLATES_DIR := deploy/inventory
INVENTORY_MANIFEST_RENDERED_DIR := deploy/inventory/rendered
INVENTORY_AGGREGATOR_BIN=bin/inventory-aggregator
INVENTORY_AGGREGATOR_CMD=./cmd/inventory/inventory-aggregator
INVENTORY_AGGREGATOR_TAG ?= $(VERSION_TAG)
INVENTORY_AGGREGATOR_IMAGE=$(CONTAINER_REGISTRY)/inventory-aggregator:$(INVENTORY_AGGREGATOR_TAG)
INVENTORY_INSPECTOR_BIN=bin/inventory-inspector
INVENTORY_INSPECTOR_CMD=./cmd/inventory/inventory-inspector
INVENTORY_INSPECTOR_TAG ?= $(VERSION_TAG)
INVENTORY_INSPECTOR_IMAGE=$(CONTAINER_REGISTRY)/inventory-inspector:$(INVENTORY_INSPECTOR_TAG)
INVENTORY_VIEWER_BIN=bin/inventory-viewer
INVENTORY_VIEWER_CMD=./cmd/inventory/inventory-viewer
INVENTORY_VIEWER_TAG ?= $(VERSION_TAG)
INVENTORY_VIEWER_IMAGE=$(CONTAINER_REGISTRY)/inventory-viewer:$(INVENTORY_VIEWER_TAG)
AGENT_BIN=bin/unbounded-agent
AGENT_CMD=./cmd/agent
MACHINA_BIN=bin/machina
MACHINA_CMD=./cmd/machina
# Fall back to the default even when the variable is set to an empty string, not
# just when unset. GNU make's `?=` treats a set-but-empty environment variable as
# already defined; a Docker `ARG MACHINA_IMAGE=` exported into the operator image
# build as "" therefore defeated `?=` and blanked the image baked into the
# operator's embedded machina manifests. `override` also neutralizes an empty
# value passed on the command line; `=` keeps CONTAINER_REGISTRY/VERSION_TAG
# expansion deferred (VERSION_TAG is defined later in this file).
ifeq ($(strip $(MACHINA_IMAGE)),)
override MACHINA_IMAGE = $(CONTAINER_REGISTRY)/machina:$(VERSION_TAG)
endif
TOKEN_REFRESHER_BIN=bin/token-refresher
TOKEN_REFRESHER_CMD=./cmd/token-refresher
TOKEN_REFRESHER_IMAGE ?= $(CONTAINER_REGISTRY)/token-refresher:$(VERSION_TAG)
MACHINE_OPS_CONTROLLER_BIN=bin/machine-ops-controller
MACHINE_OPS_CONTROLLER_CMD=./cmd/machine-ops-controller
MACHINE_OPS_CONTROLLER_IMAGE ?= $(CONTAINER_REGISTRY)/machine-ops-controller:$(VERSION_TAG)
MACHINE_OPS_CONTROLLER_NAME ?= machine-ops-controller
MACHINE_OPS_PROVIDER ?=
MACHINE_OPS_SITE ?=
METALMAN_BIN=bin/metalman
METALMAN_CMD=./cmd/metalman
NETBOOT_IMAGE ?= $(CONTAINER_REGISTRY)/netboot:$(VERSION_TAG)
PLAYPEN_TAG ?= $(VERSION_TAG)
PLAYPEN_IMAGE ?= $(CONTAINER_REGISTRY)/playpen:$(PLAYPEN_TAG)
UNBOUNDED_OPERATOR_BIN=bin/unbounded-operator
UNBOUNDED_OPERATOR_CMD=./cmd/unbounded-operator
UNBOUNDED_OPERATOR_IMAGE ?= $(CONTAINER_REGISTRY)/unbounded-operator:$(VERSION_TAG)
UNBOUNDED_OPERATOR_NAMESPACE ?= $(UNBOUNDED_NAMESPACE)
UNBOUNDED_OPERATOR_API_SERVER_ENDPOINT ?=
# Full image-repository prefix the operator resolves component images under. It
# derives from CONTAINER_REGISTRY so it cannot drift from the operator's own
# image: overriding CONTAINER_REGISTRY (as the release workflow does per fork)
# points components at the same registry/org as the operator.
UNBOUNDED_OPERATOR_IMAGE_REGISTRY ?= $(CONTAINER_REGISTRY)
UNBOUNDED_OPERATOR_REAP_LEGACY_RESOURCES ?= true
export UNBOUNDED_OPERATOR_API_SERVER_ENDPOINT
UNBOUNDED_OPERATOR_MANIFEST_TEMPLATES_DIR := deploy/unbounded-operator
UNBOUNDED_OPERATOR_MANIFEST_RENDERED_DIR := deploy/unbounded-operator/rendered
TOKEN_REFRESHER_NAMESPACE ?= $(UNBOUNDED_NAMESPACE)
TOKEN_REFRESHER_MANIFEST_TEMPLATES_DIR := deploy/token-refresher
TOKEN_REFRESHER_MANIFEST_RENDERED_DIR := deploy/token-refresher/rendered
KUBECTL_UNBOUNDED_BIN=bin/kubectl-unbounded
KUBECTL_UNBOUNDED_CMD=./cmd/kubectl-unbounded
# Net binaries
NET_CONTROLLER_BIN=bin/unbounded-net-controller
NET_CONTROLLER_CMD=./cmd/unbounded-net-controller
NET_NODE_BIN=bin/unbounded-net-node
NET_NODE_CMD=./cmd/unbounded-net-node
NET_ROUTEPLAN_DEBUG_BIN=bin/unbounded-net-routeplan-debug
NET_ROUTEPLAN_DEBUG_CMD=./cmd/unbounded-net-routeplan-debug
UNPING_BIN=bin/unping
UNPING_CMD=./cmd/unping
UNROUTE_BIN=bin/unroute
UNROUTE_CMD=./cmd/unroute
# Gantry (peer-to-peer OCI distribution)
GANTRY_BIN=bin/gantry
GANTRY_CMD=./cmd/gantry
GANTRY_IMAGE ?= $(CONTAINER_REGISTRY)/gantry:$(VERSION_TAG)
GANTRY_NAMESPACE ?= $(UNBOUNDED_NAMESPACE)
GANTRY_CHART_DIR := deploy/gantry/chart
GANTRY_MANIFEST_RENDERED_DIR := deploy/gantry/rendered
GANTRY_OPERATOR_RENDER_DIR := tmp/gantry-operator-render
GANTRY_SUPPORT_RENDER_DIR := tmp/gantry-support-render
GANTRY_CHART_VERSION ?= 0.0.0-dev
GANTRY_CHART_APP_VERSION ?= $(VERSION_TAG)
GANTRY_CHART_PACKAGE_DIR := build/charts
GANTRY_CHART_STAGE_DIR := tmp/gantry-chart-package
GANTRY_CHART_IMAGE_REPOSITORY ?= $(CONTAINER_REGISTRY)/gantry
# Version is derived from the latest git tag. Override with: make VERSION=v1.0.0
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
# VERSION_TAG is VERSION made safe for use as a Docker image tag: git describe can
# surface a nearest tag containing a slash (e.g. agent-artifacts/v20260710), which
# is invalid in an image reference. VERSION itself is kept intact for the embedded
# version string (ldflags) and release artifact paths.
VERSION_TAG ?= $(subst /,-,$(VERSION))
GIT_COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
BUILD_TIME ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
# Shared ldflags for injecting version metadata into all binaries.
STAMP_LDFLAGS=-X github.com/Azure/unbounded/internal/version.Version=$(VERSION) \
-X github.com/Azure/unbounded/internal/version.GitCommit=$(GIT_COMMIT) \
-X github.com/Azure/unbounded/internal/version.BuildTime=$(BUILD_TIME)
METALMAN_LDFLAGS=$(STAMP_LDFLAGS) -X github.com/Azure/unbounded/internal/metalman/commands.DefaultNetbootImage=$(NETBOOT_IMAGE)
METALMAN_IMAGE=$(CONTAINER_REGISTRY)/metalman:$(VERSION_TAG)
# Orca configuration
ORCA_BIN=bin/orca
ORCA_CMD=./cmd/orca
ORCA_IMAGE ?= $(CONTAINER_REGISTRY)/orca:$(VERSION_TAG)
ORCA_NAMESPACE ?= $(UNBOUNDED_NAMESPACE)
ORCA_MANIFEST_TEMPLATES_DIR := deploy/orca
ORCA_MANIFEST_RENDERED_DIR := deploy/orca/rendered
# Dev image tag used by the orca-kind-up / orca-install paths.
# Pinned to :dev so kind load and rollout-restart use a stable
# identifier (the auto-derived VERSION can include slashes from git
# tags like images/agent-ubuntu2404-nvidia/v..., which are illegal
# in OCI tags). Override with ORCA_DEV_IMAGE=... when targeting a
# remote registry.
ORCA_DEV_IMAGE ?= ghcr.io/azure/orca:dev
# Kind cluster name used by orca-kind-up / orca-kind-down. Mirrors
# the default in hack/orca/kind-up.sh.
ORCA_KIND_CLUSTER ?= orca-dev
KUBECTL_UNBOUNDED_LDFLAGS=$(STAMP_LDFLAGS)
# --- Net (unbounded-net) configuration -------------------------------------
# Container images for the net controller and node agent.
# See the MACHINA_IMAGE note above: default when empty-or-unset so an empty
# Docker ARG cannot blank the images baked into the embedded net manifests.
ifeq ($(strip $(NET_CONTROLLER_IMAGE)),)
override NET_CONTROLLER_IMAGE = $(CONTAINER_REGISTRY)/unbounded-net-controller:$(VERSION_TAG)
endif
ifeq ($(strip $(NET_NODE_IMAGE)),)
override NET_NODE_IMAGE = $(CONTAINER_REGISTRY)/unbounded-net-node:$(VERSION_TAG)
endif
# CNI plugins version baked into the net-node image. Keep in sync with the
# defaults in images/net-{node,controller}/Dockerfile and the workflow envs.
CNI_PLUGINS_VERSION ?= v1.9.1
# Host architecture for local image builds (amd64 / arm64). Used to pick the
# right CNI plugins tarball for the current machine.
HOST_GOARCH := $(shell $(GOCMD) env GOARCH)
# Kubernetes deploy knobs.
NET_NAMESPACE ?= $(UNBOUNDED_NAMESPACE)
NET_FORCE_NOT_LEADER ?= false
NET_AZURE_TENANT_ID ?=
NET_APISERVER_URL ?= $(shell kubectl config view --flatten --minify --template '{{ (index .clusters 0).cluster.server }}' 2>/dev/null)
# When set (e.g. NET_LOG_LEVEL=4), `make -C hack/net deploy-config` patches the live configmap.
NET_LOG_LEVEL ?=
# Paths.
NET_MANIFEST_TEMPLATES_DIR := deploy/net
NET_MANIFEST_RENDERED_DIR := deploy/net/rendered
NET_CRD_DIR := deploy/net/crd
NET_FRONTEND_DIR := frontend
NET_FRONTEND_DIST_DIR := internal/net/html/dist
NET_FRONTEND_CACHE_FILE := $(NET_FRONTEND_DIST_DIR)/.frontend-build-key
# Frontend build toggle (dev builds produce unminified output with sourcemaps).
REACT_DEV ?= false
.PHONY: all help fmt lint lint-actions test build vulncheck check-deps kubectl-unbounded kubectl-unbounded-build install-tools install-protoc install-helm generate kubectl-unbounded forge relctl relctl-build agent-artifacts-builder agent-artifacts-builder-build orcadev unbounded-agent machina machina-build machina-oci machina-oci-push machina-manifests machine-ops-controller machine-ops-controller-build machine-ops-controller-oci machine-ops-controller-oci-push machine-ops-manifests metalman metalman-build metalman-oci metalman-oci-push unbounded-operator unbounded-operator-build unbounded-operator-manifests playpen-manifests e2e-gantry e2e-playpen gomod docs-serve unbounded-net-controller unbounded-net-controller-build unbounded-net-node unbounded-net-node-build unbounded-net-routeplan-debug unping unping-build unroute unroute-build license-check notice notice-check gantry gantry-build gantry-manifests inventory-manifests
.PHONY: net-frontend net-frontend-clean net-ebpf-build net-ebpf-generate net-ebpf-verify net-manifests gantry-chart-lint gantry-chart-package release-bom release-manifests unbounded-operator-release-manifest
.PHONY: image-machina-local image-token-refresher-local image-machine-ops-controller-local image-metalman-local image-unbounded-operator-local image-unbounded-operator-push image-playpen-local image-net-controller-local image-net-node-local image-gantry-local image-gantry-push images-local
.PHONY: image-net-controller-push image-net-node-push images-net-all images-net-all-push
##@ General
all: kubectl-unbounded forge relctl machina machine-ops-controller token-refresher unbounded-operator unbounded-net-controller unbounded-net-node unbounded-net-routeplan-debug unping unroute gantry ## Build all binaries (default)
help: ## Show this help
@echo ""
@echo "Usage: make <target> [VAR=value ...]"
@echo ""
@echo "General:"
@echo " all Build all Go binaries (default)"
@echo " help Show this help"
@echo " install-tools Install gofumpt, golangci-lint, protoc-gen-go, protoc-gen-go-grpc, controller-gen, actionlint"
@echo " install-protoc Download pinned protoc into bin/protoc/"
@echo " install-helm Download pinned Helm into bin/"
@echo ""
@echo "Development:"
@echo " fmt Format Go source (gofumpt + wsl_v5)"
@echo " lint Run golangci-lint and actionlint"
@echo " lint-actions Run actionlint over .github/workflows"
@echo " test Run all tests"
@echo " build Compile all Go packages"
@echo " generate Run go generate (deepcopy, CRDs, protobuf)"
@echo " vulncheck Run govulncheck; fails only on vulnerabilities with final fixes"
@echo " gomod go mod tidy"
@echo " e2e-gantry Run the kind-based Gantry e2e suite"
@echo " e2e-playpen Run the kind-based playpen e2e suite"
@echo " license-check Verify project-owned license declarations"
@echo " notice Regenerate NOTICE from Go and npm dependencies"
@echo " notice-check Verify NOTICE is in sync with dependencies"
@echo " toolchain-shell Drop into the toolchain container with the repo mounted at /project (set TOOLCHAIN_FLAVOR=fedora|ubuntu to pick a flavor)"
@echo " toolchain-build Rebuild the toolchain container image (honors TOOLCHAIN_FLAVOR)"
@echo ""
@echo "Build:"
@echo " kubectl-unbounded Build kubectl-unbounded plugin"
@echo " forge Build forge dev tool"
@echo " relctl Build the relctl release tool"
@echo " agent-artifacts-builder Build offline agent artifacts builder"
@echo " agent-artifacts-builder-build Build offline agent artifacts builder without test"
@echo " orcadev Build orcadev dev/debug tool"
@echo " inventory-all Build all inventory components"
@echo " inventory-agent Build inventory-agent for amd64 and arm64"
@echo " inventory-agent-build Build inventory-agent for the host GOOS/GOARCH without test"
@echo " inventory-agent-amd64 Build inventory-agent for amd64"
@echo " inventory-agent-arm64 Build inventory-agent for arm64"
@echo " inventory-aggregator Build inventory-aggregator"
@echo " inventory-aggregator-build Build inventory-aggregator without test"
@echo " inventory-inspector Build inventory-inspector"
@echo " inventory-inspector-build Build inventory-inspector without test"
@echo " inventory-viewer Build inventory-viewer"
@echo " inventory-viewer-build Build inventory-viewer without test"
@echo " unbounded-agent Build unbounded-agent (linux)"
@echo " machina | machina-build Build machina controller (with/without lint/test)"
@echo " machine-ops-controller Build machine-ops-controller"
@echo " metalman | metalman-build Build metalman controller (with/without lint/test)"
@echo " unbounded-operator | unbounded-operator-build Build the top-level Site operator"
@echo " unbounded-net-controller Build net controller"
@echo " unbounded-net-node Build net node agent"
@echo " unbounded-net-routeplan-debug Build net routeplan debug tool"
@echo " unping Build unping health-check utility"
@echo " unroute Build unroute eBPF inspection utility"
@echo ""
@echo "Container Images (local, single-arch):"
@echo " image-inventory-all-local Build all local inventory container images"
@echo " image-inventory-all-push Build and push all inventory container images"
@echo " image-inventory-aggregator-local Build a local inventory-aggregator container image"
@echo " image-inventory-aggregator-push Build and push the inventory-aggregator container image"
@echo " image-inventory-inspector-local Build a local inventory-inspector container image"
@echo " image-inventory-inspector-push Build and push the inventory-inspector container image"
@echo " image-inventory-viewer-local Build a local inventory-viewer container image"
@echo " image-inventory-viewer-push Build and push the inventory-viewer container image"
@echo " image-machina-local Build machina image with \$$(CONTAINER_ENGINE)"
@echo " image-token-refresher-local Build token-refresher image"
@echo " image-machine-ops-controller-local Build machine-ops-controller image"
@echo " image-metalman-local Build metalman image"
@echo " image-unbounded-operator-local Build unbounded-operator image"
@echo " image-unbounded-operator-push Build and push unbounded-operator image"
@echo " image-playpen-local Build playpen image"
@echo " image-net-controller-local Build unbounded-net-controller image"
@echo " image-net-controller-push Build and push unbounded-net-controller image"
@echo " image-net-node-local Build unbounded-net-node image"
@echo " image-net-node-push Build and push unbounded-net-node image"
@echo " images-net-all Build all unbounded-net images"
@echo " images-net-all-push Build and push all unbounded-net images"
@echo " images-local Build all local images"
@echo " machina-oci-push Build machina image and push"
@echo " machine-ops-controller-oci-push Build machine-ops-controller image and push"
@echo " metalman-oci-push Build metalman image and push"
@echo " image-orca-local Build orca image"
@echo " orca-oci-push Build orca image and push"
@echo ""
@echo "Net Frontend:"
@echo " net-frontend Build frontend into \$$(NET_FRONTEND_DIST_DIR) (cached)"
@echo " net-frontend-clean Remove node_modules and dist artifacts"
@echo ""
@echo "Net eBPF:"
@echo " net-ebpf-build Compile bpf/unbounded_encap.c (requires clang-18; see bpf/clang-version)"
@echo " net-ebpf-generate Regenerate bpf/vmlinux.h from pinned Ubuntu kernel (requires bpftool, curl, dpkg-deb, python3)"
@echo " net-ebpf-verify Verify bpf/vmlinux.h matches bpf/btf-kernel-pin{,-hashes} (no extra tools)"
@echo ""
@echo "Net Manifests:"
@echo " machina-manifests Render machina manifests into deploy/machina/rendered"
@echo " machine-ops-manifests Render machine-ops manifests into deploy/machine-ops/rendered"
@echo " net-manifests Render net manifests into \$$(NET_MANIFEST_RENDERED_DIR)"
@echo " orca-manifests Render orca manifests into deploy/orca/rendered"
@echo " unbounded-operator-manifests Render unbounded-operator manifests into deploy/unbounded-operator/rendered"
@echo " gantry-chart-lint Validate the standalone Gantry Helm chart"
@echo " gantry-chart-package Package the standalone Gantry Helm chart"
@echo " unbounded-operator-release-manifest Build a versioned, directly applicable operator manifest under build/"
@echo ""
@echo "Net Kubernetes (apply to current kubectl context):"
@echo " See \`make -C hack/net help\` for cluster deploy/undeploy targets."
@echo ""
@echo "Orca Dev Install (see hack/orca/README.md for the developer quickstart):"
@echo " orca | orca-build Build orca binary (with/without lint/test)"
@echo " orcadev Build orcadev dev/debug tool"
@echo " orca-install Install Orca into the current kubectl context"
@echo " orca-kind-up | orca-up Create kind cluster + install Orca (build + side-load image)"
@echo " orca-kind-down | orca-down Delete the kind cluster"
@echo " orca-reset Rebuild image and rolling-restart Orca on kind"
@echo " orca-inttest Run orca integration tests (Docker required)"
@echo ""
@echo "Documentation:"
@echo " docs-serve Start local Hugo dev server"
@echo ""
@echo "Racer Controller:"
@echo " racer-controller Test and build the Go controller"
@echo " racer-controller-build Build the Go controller without lint/test"
@echo " racer-test Lint and race-test the controller and deployment contracts"
@echo " racer-envtest Run controller API-server tests with KUBEBUILDER_ASSETS"
@echo " racer-envtest-ci Provision pinned assets and run controller API-server tests"
@echo " racer-generate Generate Racer deepcopy and CRD artifacts"
@echo ""
@echo "Common variables (override with VAR=value):"
@echo " VERSION=$(VERSION)"
@echo " GIT_COMMIT=$(GIT_COMMIT)"
@echo " CONTAINER_REGISTRY=$(CONTAINER_REGISTRY)"
@echo " CONTAINER_ENGINE=$(CONTAINER_ENGINE)"
@echo " NET_NAMESPACE=$(NET_NAMESPACE)"
@echo " NET_CONTROLLER_IMAGE=$(NET_CONTROLLER_IMAGE)"
@echo " NET_NODE_IMAGE=$(NET_NODE_IMAGE)"
@echo " REACT_DEV=$(REACT_DEV)"
##@ Development
#
# When CI is set (GitHub Actions sets CI=true automatically), targets run
# without their usual dependency chains so each CI job stays independent.
GOFUMPT_VERSION ?= v0.11.0
GOLANGCI_LINT_VERSION ?= v2.13.1
PROTOC_GEN_GO_VERSION ?= v1.36.11
PROTOC_GEN_GO_GRPC_VERSION ?= v1.6.1
CONTROLLER_GEN_VERSION ?= v0.21.0
ACTIONLINT_VERSION ?= v1.7.12
HELM_VERSION ?= 3.21.3
HELM ?= $(CURDIR)/bin/helm
HELM_STAMP := $(CURDIR)/bin/.helm-v$(HELM_VERSION)
HELM_UNAME_S := $(shell uname -s)
HELM_UNAME_M := $(shell uname -m)
ifeq ($(HELM_UNAME_S),Darwin)
HELM_OS := darwin
else
HELM_OS := linux
endif
ifeq ($(HELM_UNAME_M),x86_64)
HELM_ARCH := amd64
else ifeq ($(HELM_UNAME_M),aarch64)
HELM_ARCH := arm64
else ifeq ($(HELM_UNAME_M),arm64)
HELM_ARCH := arm64
else
HELM_ARCH := unsupported
endif
ifeq ($(HELM_OS)-$(HELM_ARCH),linux-amd64)
HELM_SHA256 := 15e041a93a590dce8100f39385cd98c84a765c9e36aeeb9e2dc6ff9e4769e2e0
else ifeq ($(HELM_OS)-$(HELM_ARCH),linux-arm64)
HELM_SHA256 := 67f58155079ff9ffab98ba5c88daff0ed9b542f3a4732f5dd426dde7dd0f5244
else ifeq ($(HELM_OS)-$(HELM_ARCH),darwin-amd64)
HELM_SHA256 := 76d0db4730b05d3d625eee11e80f0721b32b4d8422f4e5d093de6337bf3ac9f8
else ifeq ($(HELM_OS)-$(HELM_ARCH),darwin-arm64)
HELM_SHA256 := 19879a848cad832b7a1ac24b767a481d20fb3b95ab53a220849649422ada144e
endif
# Pinned protoc for deterministic .pb.go output across environments.
# Downloaded from the upstream protobuf GitHub releases.
PROTOC_VERSION ?= 3.19.6
PROTOC_DIR ?= $(CURDIR)/bin/protoc
PROTOC := $(PROTOC_DIR)/bin/protoc
# Auto-detect OS/arch for protoc release archive naming.
# See https://github.com/protocolbuffers/protobuf/releases for valid combinations.
PROTOC_UNAME_S := $(shell uname -s)
PROTOC_UNAME_M := $(shell uname -m)
ifeq ($(PROTOC_UNAME_S),Darwin)
PROTOC_OS ?= osx
else
PROTOC_OS ?= linux
endif
ifeq ($(PROTOC_UNAME_M),x86_64)
PROTOC_ARCH ?= x86_64
else ifeq ($(PROTOC_UNAME_M),aarch64)
PROTOC_ARCH ?= aarch_64
else ifeq ($(PROTOC_UNAME_M),arm64)
PROTOC_ARCH ?= aarch_64
else
PROTOC_ARCH ?= $(PROTOC_UNAME_M)
endif
install-tools: ## Install development tools (gofumpt, golangci-lint, protoc-gen-go, protoc-gen-go-grpc, controller-gen, actionlint)
go install mvdan.cc/gofumpt@$(GOFUMPT_VERSION)
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
go install google.golang.org/protobuf/cmd/protoc-gen-go@$(PROTOC_GEN_GO_VERSION)
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@$(PROTOC_GEN_GO_GRPC_VERSION)
go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_GEN_VERSION)
go install github.com/rhysd/actionlint/cmd/actionlint@$(ACTIONLINT_VERSION)
install-protoc: $(PROTOC) ## Download pinned protoc into bin/protoc/
install-helm: $(HELM) ## Download pinned Helm into bin/
$(HELM_STAMP):
@test -n "$(HELM_SHA256)" || { echo "unsupported Helm platform $(HELM_OS)-$(HELM_ARCH)" >&2; exit 1; }
@mkdir -p $(dir $(HELM)) tmp
@archive="helm-v$(HELM_VERSION)-$(HELM_OS)-$(HELM_ARCH).tar.gz"; \
echo "Downloading Helm v$(HELM_VERSION) for $(HELM_OS)-$(HELM_ARCH)..."; \
curl -fsSL --max-time 30 -o "tmp/$$archive" "https://get.helm.sh/$$archive"; \
if command -v sha256sum >/dev/null 2>&1; then \
actual=$$(sha256sum "tmp/$$archive" | awk '{print $$1}'); \
else \
actual=$$(shasum -a 256 "tmp/$$archive" | awk '{print $$1}'); \
fi; \
test "$$actual" = "$(HELM_SHA256)" || { echo "Helm checksum mismatch: got $$actual" >&2; rm -f "tmp/$$archive"; exit 1; }; \
tar -xzf "tmp/$$archive" -C tmp; \
cp "tmp/$(HELM_OS)-$(HELM_ARCH)/helm" "$(HELM)"; \
chmod +x "$(HELM)"; \
rm -rf "tmp/$$archive" "tmp/$(HELM_OS)-$(HELM_ARCH)"; \
touch "$(HELM_STAMP)"
@$(HELM) version --short
$(HELM): $(HELM_STAMP)
@test -x $(HELM) || { rm -f $(HELM_STAMP); $(MAKE) $(HELM_STAMP); }
$(PROTOC):
@mkdir -p $(PROTOC_DIR)
@echo "Downloading protoc v$(PROTOC_VERSION) for $(PROTOC_OS)-$(PROTOC_ARCH)..."
@curl -fsSL -o $(PROTOC_DIR)/protoc.zip \
https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-$(PROTOC_OS)-$(PROTOC_ARCH).zip
@unzip -q -o $(PROTOC_DIR)/protoc.zip -d $(PROTOC_DIR)
@rm $(PROTOC_DIR)/protoc.zip
@$(PROTOC) --version
check-deps: ## Verify required tools (gofumpt, golangci-lint v2) are installed
@command -v $(GOFMT) >/dev/null 2>&1 || \
{ echo "error: $(GOFMT) not found. Install it with:"; \
echo " go install mvdan.cc/gofumpt@latest"; exit 1; }
@command -v golangci-lint >/dev/null 2>&1 || \
{ echo "error: golangci-lint not found. Install it with:"; \
echo " go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest"; exit 1; }
@golangci-lint --version 2>&1 | grep -qE 'version v?2\.' || \
{ echo "error: golangci-lint v2 is required (.golangci.yaml uses version: \"2\")."; \
echo " Your installed version: $$(golangci-lint --version 2>&1 | head -1)"; \
echo " Install v2 with:"; \
echo " go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest"; exit 1; }
# --fix applies every auto-fixable linter and formatter that .golangci.yaml
# enables, wsl_v5 among them; it does not need to be named again here. It is
# also what applies gofumpt's group-params rule, which the plain `gofumpt -w`
# recipe line does not: gofumpt's standalone -extra flag is all-or-nothing and
# would pull in rules .golangci.yaml does not ask for.
fmt: check-deps ## Format all Go source files (gofumpt + golangci-lint auto-fixes)
$(GOFMT) -w $(GO_PACKAGE_DIRS)
$(GOLINT) --fix $(GO_PACKAGE_PATTERNS)
# lint runs the same checks locally and in CI and does NOT auto-fix. Run
# `make fmt` to apply fixes. wsl_v5 is enforced via .golangci.yaml.
lint: ## Run golangci-lint and actionlint (matches CI; run `make fmt` to auto-fix)
$(GOLINT) $(GO_PACKAGE_PATTERNS)
@$(MAKE) --no-print-directory lint-actions
# lint-actions is part of `lint` because a workflow file is only ever parsed
# when it is dispatched. A duplicate `default:` key made release-prepare.yaml
# undispatchable while every check in CI stayed green, and the workflow that
# mints release tags is the worst possible place to find that out by hand.
#
# The shellcheck and pyflakes integrations are disabled explicitly rather than
# left at their defaults: GitHub-hosted runners ship shellcheck and most
# workstations do not, so leaving them on would make `make lint` mean something
# different in CI than it does locally. Enabling shellcheck over every `run:`
# block is worth doing on its own terms, with its own findings list.
lint-actions: ## Run actionlint over .github/workflows
@command -v actionlint >/dev/null 2>&1 || \
{ echo "error: actionlint not found. Install it with:"; \
echo " go install github.com/rhysd/actionlint/cmd/actionlint@$(ACTIONLINT_VERSION)"; exit 1; }
actionlint -shellcheck= -pyflakes=
ifdef CI
# In CI each job is independent; skip chained prerequisites.
test: machina-manifests token-refresher-manifests machine-ops-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Run all tests with race detector
$(GOTEST) -race ./...
else
# Locally, chain test -> lint for convenience.
test: lint machina-manifests token-refresher-manifests machine-ops-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Run all tests (implies lint)
$(GOTEST) ./...
endif
e2e-gantry: $(HELM) ## Run the kind-based Gantry e2e suite
CONTAINER_ENGINE="$(CONTAINER_ENGINE)" KIND_EXPERIMENTAL_PROVIDER="$(CONTAINER_ENGINE)" PATH="$(CURDIR)/bin:$$PATH" \
$(GOTEST) -tags=e2e -count=1 -timeout=120m -v ./e2e/gantry
e2e-playpen: ## Run the kind-based playpen e2e suite
$(GOTEST) -tags=e2e ./e2e/playpen -v -timeout=10m
.PHONY: racer-controller racer-controller-build racer-test racer-server-test racer-envtest racer-envtest-ci racer-generate
racer-controller: racer-server-test racer-controller-build ## Test and build the Racer controller
racer-controller-build: ## Build the Racer controller without lint/test
@mkdir -p bin
timeout --signal=TERM --kill-after=10s 300s $(GOBUILD) -trimpath -ldflags '$(STAMP_LDFLAGS)' -o bin/racer-controller ./cmd/racer-controller
racer-server-test: ## Lint and race-test the Racer server
timeout --signal=TERM --kill-after=10s 300s $(GOLINT) ./api/racer/... ./internal/racer/... ./cmd/racer-controller/...
timeout --signal=TERM --kill-after=10s 300s $(GOTEST) -timeout=5m -race ./api/racer/... ./internal/racer/... ./cmd/racer-controller/...
racer-test: racer-server-test ## Check the Racer controller
$(SETUP_ENVTEST):
@mkdir -p bin tmp/envtest-tools
TMPDIR="$(CURDIR)/tmp/envtest-tools" GOBIN="$(CURDIR)/bin" timeout --signal=TERM --kill-after=10s 300s $(GOCMD) install sigs.k8s.io/controller-runtime/tools/setup-envtest@$(SETUP_ENVTEST_VERSION)
mv bin/setup-envtest "$(SETUP_ENVTEST)"
racer-envtest-ci: $(SETUP_ENVTEST) ## Provision pinned local API-server assets and require Racer envtest
@mkdir -p tmp/racer-envtest
@assets=$$(TMPDIR="$(CURDIR)/tmp/racer-envtest" timeout --signal=TERM --kill-after=10s 300s "$(SETUP_ENVTEST)" use $(ENVTEST_K8S_VERSION) --bin-dir "$(CURDIR)/bin/envtest" -p path) && \
$(MAKE) racer-envtest KUBEBUILDER_ASSETS="$$assets"
racer-envtest: ## Run real API-server, manager election, TLS and crash-recovery tests
@test -n "$(KUBEBUILDER_ASSETS)" || { echo "Set KUBEBUILDER_ASSETS to repository-local envtest binaries"; exit 1; }
@mkdir -p tmp/racer-envtest
TMPDIR="$(CURDIR)/tmp/racer-envtest" KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" timeout --signal=TERM --kill-after=10s 300s $(GOTEST) -race ./internal/racer ./internal/racer/authority -run '^TestEnvtest' -count=1 -v -timeout=5m
racer-generate: ## Generate Racer deepcopy and CRD artifacts
timeout --signal=TERM --kill-after=10s 300s $(GOCMD) generate ./api/racer/v1alpha1
build: machina-manifests token-refresher-manifests machine-ops-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Build all Go packages
$(GOBUILD) ./...
generate: install-protoc ## Run go generate for API types (deepcopy, CRDs) and protobuf
PATH="$(PROTOC_DIR)/bin:$$PATH" $(GOCMD) generate $(GO_PACKAGES)
vulncheck: machina-manifests token-refresher-manifests machine-ops-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Run govulncheck; fails only on reachable vulnerabilities that have a final-release fix
@# The JSON stream is the documented programmatic interface. The gate owns
@# the verdict, so govulncheck is not asked for one: in JSON mode it exits 0
@# whether or not it found anything, and a non-zero exit here means the scan
@# itself failed, which must still fail the target. Progress goes to stderr
@# and stays visible.
@mkdir -p tmp
$(GOCMD) tool govulncheck -format json $(GO_PACKAGE_PATTERNS) > tmp/govulncheck.json
$(GOCMD) run ./hack/cmd/vulncheck-gate tmp/govulncheck.json
gomod: ## Tidy go.mod and go.sum
$(GOMOD) tidy
license-check: ## Verify project-owned source license declarations
@set -e; \
[ "$$(sha256sum LICENSE | cut -d' ' -f1)" = "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4" ] || { \
echo "ERROR: LICENSE is not the Apache License 2.0 text." >&2; \
exit 1; \
}; \
stale="$$(git grep --untracked -nI -E \
'Licensed under the MIT License|SPDX-License-Identifier:[[:space:]]*MIT|org\.opencontainers\.image\.licenses="MIT"|MIT License\]\(LICENSE\)|[; ]MIT License<|license[[:space:]]*[=:][[:space:]]*"?MIT"?' -- \
':(top)**' \
':(top,exclude)Makefile' \
':(top,exclude)NOTICE' \
':(top,exclude)frontend/package-lock.json' \
':(top,exclude)hack/cmd/notice/**' || { status=$$?; [ "$$status" -eq 1 ] || exit "$$status"; })"; \
notice_stale="$$(git grep --untracked -nI -E \
'Licensed under the MIT License|SPDX-License-Identifier:[[:space:]]*MIT' \
-- ':(top)hack/cmd/notice/**' || { status=$$?; [ "$$status" -eq 1 ] || exit "$$status"; })"; \
if [ -n "$$stale$$notice_stale" ]; then \
echo "ERROR: stale repository-owned MIT declaration(s):" >&2; \
[ -z "$$stale" ] || printf '%s\n' "$$stale" >&2; \
[ -z "$$notice_stale" ] || printf '%s\n' "$$notice_stale" >&2; \
exit 1; \
fi; \
missing="$$(git ls-files -- '*.go' \
':(exclude)**/vendor/**' \
':(exclude)**/third_party/**' \
':(exclude)**/node_modules/**' \
':(exclude)**/target/**' | while IFS= read -r file; do \
case "$$file" in \
*.go) grep -qE '^// Code generated .* DO NOT EDIT\.$$' "$$file" && continue ;; \
esac; \
grep -qF 'SPDX-License-Identifier: Apache-2.0' "$$file" || printf '%s\n' "$$file"; \
done)"; \
if [ -n "$$missing" ]; then \
echo "ERROR: tracked hand-written Go source missing SPDX-License-Identifier: Apache-2.0:" >&2; \
printf '%s\n' "$$missing" >&2; \
exit 1; \
fi
notice: ## Regenerate NOTICE from Go, npm, Cargo, and pinned native dependencies
@if [ ! -d "$(NET_FRONTEND_DIR)/node_modules" ]; then \
echo "ERROR: $(NET_FRONTEND_DIR)/node_modules not found." >&2; \
echo "Run: (cd $(NET_FRONTEND_DIR) && npm ci)" >&2; \
exit 1; \
fi
$(GOCMD) run ./hack/cmd/notice generate --output NOTICE
notice-check: ## Verify NOTICE is in sync with Go, npm, Cargo, and pinned native dependencies
@if [ ! -d "$(NET_FRONTEND_DIR)/node_modules" ]; then \
echo "ERROR: $(NET_FRONTEND_DIR)/node_modules not found." >&2; \
echo "Run: (cd $(NET_FRONTEND_DIR) && npm ci)" >&2; \
exit 1; \
fi
$(GOCMD) run ./hack/cmd/notice check --notice NOTICE
.PHONY: toolchain-shell
toolchain-shell: ## Drop into the toolchain container with the repo mounted at /project (builds the image on first use)
@./images/toolchain/toolchain.sh
.PHONY: toolchain-build
toolchain-build: ## Rebuild the toolchain container image (otherwise built lazily on first toolchain-shell use)
@TOOLCHAIN_REBUILD=1 ./images/toolchain/toolchain.sh true
##@ Build
kubectl-unbounded-build: machina-manifests net-manifests unbounded-operator-manifests ## Build the kubectl-unbounded binary (no lint/test)
$(GOBUILD) -ldflags '$(KUBECTL_UNBOUNDED_LDFLAGS)' -o $(KUBECTL_UNBOUNDED_BIN) $(KUBECTL_UNBOUNDED_CMD)/main.go
kubectl-unbounded: test kubectl-unbounded-build ## Build the kubectl-unbounded plugin (implies test)
forge: test ## Build the forge dev tool (implies test)
$(GOBUILD) -o $(FORGE_BIN) $(FORGE_CMD)/main.go
relctl-build: ## Build the relctl release tool (no lint/test)
$(GOBUILD) -o $(RELCTL_BIN) $(RELCTL_CMD)/main.go
relctl: test relctl-build ## Build the relctl release tool (implies test)
agent-artifacts-builder-build: ## Build the offline agent artifacts builder (no lint/test)
$(GOBUILD) -o $(AGENT_ARTIFACTS_BUILDER_BIN) $(AGENT_ARTIFACTS_BUILDER_CMD)/main.go
agent-artifacts-builder: test agent-artifacts-builder-build ## Build the offline agent artifacts builder (implies test)
ORCADEV_BIN=bin/orcadev
ORCADEV_CMD=./hack/cmd/orcadev
orcadev: test ## Build the orcadev dev/debug tool (implies test)
$(GOBUILD) -o $(ORCADEV_BIN) $(ORCADEV_CMD)/main.go
.PHONY: inventory-all
inventory-all: inventory-agent inventory-aggregator inventory-inspector inventory-viewer ## Build all inventory components
.PHONY: inventory-agent
inventory-agent: test inventory-agent-amd64 inventory-agent-arm64 ## Build inventory-agent for amd64 and arm64, symlink to host arch (implies test)
@HOST_ARCH=$$(uname -m); \
case "$$HOST_ARCH" in \
x86_64) ARCH=amd64 ;; \
aarch64) ARCH=arm64 ;; \
*) echo "unsupported architecture: $$HOST_ARCH" >&2; exit 1 ;; \
esac; \
ln -sf inventory-agent-$$ARCH $(INVENTORY_AGENT_BIN)
# inventory-agent-build honors GOOS/GOARCH from the environment so the
# container image can cross-compile natively on the build host, matching the
# other component -build targets. The -amd64/-arm64 variants stay for local
# use, where both are wanted side by side.
.PHONY: inventory-agent-build
inventory-agent-build: ## Build the inventory-agent binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(INVENTORY_AGENT_BIN) $(INVENTORY_AGENT_CMD)
.PHONY: inventory-agent-amd64
inventory-agent-amd64: ## Build inventory-agent for linux/amd64
GOOS=linux GOARCH=amd64 $(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(INVENTORY_AGENT_BIN)-amd64 $(INVENTORY_AGENT_CMD)
.PHONY: inventory-agent-arm64
inventory-agent-arm64: ## Build inventory-agent for linux/arm64
GOOS=linux GOARCH=arm64 $(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(INVENTORY_AGENT_BIN)-arm64 $(INVENTORY_AGENT_CMD)
.PHONY: inventory-aggregator-build
inventory-aggregator-build: ## Build the inventory-aggregator binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(INVENTORY_AGGREGATOR_BIN) $(INVENTORY_AGGREGATOR_CMD)
.PHONY: inventory-aggregator
inventory-aggregator: test inventory-aggregator-build ## Build the inventory-aggregator (implies test)
.PHONY: inventory-inspector-build
inventory-inspector-build: ## Build the inventory-inspector binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(INVENTORY_INSPECTOR_BIN) $(INVENTORY_INSPECTOR_CMD)
.PHONY: inventory-inspector
inventory-inspector: test inventory-inspector-build ## Build the inventory-inspector (implies test)
.PHONY: inventory-viewer-build
inventory-viewer-build: ## Build the inventory-viewer binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(INVENTORY_VIEWER_BIN) $(INVENTORY_VIEWER_CMD)
.PHONY: inventory-viewer
inventory-viewer: test inventory-viewer-build ## Build the inventory-viewer web server (implies test)
unbounded-agent: test ## Build the unbounded-agent for linux (implies test)
GOOS=linux $(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(AGENT_BIN) $(AGENT_CMD)/main.go
machina-build: machina-manifests ## Build the machina binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(MACHINA_BIN) $(MACHINA_CMD)/main.go
machina: test machina-build ## Build the machina controller (implies test)
.PHONY: token-refresher-build
token-refresher-build: ## Build the token-refresher binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(TOKEN_REFRESHER_BIN) $(TOKEN_REFRESHER_CMD)/main.go
.PHONY: token-refresher
token-refresher: test token-refresher-build ## Build token-refresher (implies test)
machine-ops-controller-build: machine-ops-manifests ## Build the machine-ops-controller binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(MACHINE_OPS_CONTROLLER_BIN) $(MACHINE_OPS_CONTROLLER_CMD)
machine-ops-controller: test machine-ops-controller-build ## Build the machine-ops-controller (implies test)
metalman-build: ## Build the metalman binary (no lint/test)
$(GOBUILD) -ldflags '$(METALMAN_LDFLAGS)' -o $(METALMAN_BIN) $(METALMAN_CMD)/main.go
metalman: test metalman-build ## Build the metalman controller (implies test)
unbounded-operator-build: machina-manifests token-refresher-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Build the unbounded-operator binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(UNBOUNDED_OPERATOR_BIN) $(UNBOUNDED_OPERATOR_CMD)/main.go
unbounded-operator: test unbounded-operator-build ## Build the unbounded-operator (implies test)
##@ Net Binaries
unbounded-net-controller-build: ## Build the unbounded-net-controller binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(NET_CONTROLLER_BIN) $(NET_CONTROLLER_CMD)
unbounded-net-controller: test unbounded-net-controller-build ## Build the unbounded-net-controller (implies test)
unbounded-net-node-build: ## Build the unbounded-net-node binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(NET_NODE_BIN) $(NET_NODE_CMD)
unbounded-net-node: test unbounded-net-node-build ## Build the unbounded-net-node (implies test)
unbounded-net-routeplan-debug: test ## Build the routeplan debug tool (implies test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(NET_ROUTEPLAN_DEBUG_BIN) $(NET_ROUTEPLAN_DEBUG_CMD)
unping-build: ## Build the unping utility binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(UNPING_BIN) $(UNPING_CMD)
unping: test unping-build ## Build the unping utility (implies test)
unroute-build: ## Build the unroute utility binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(UNROUTE_BIN) $(UNROUTE_CMD)
unroute: test unroute-build ## Build the unroute utility (implies test)
##@ Gantry (peer-to-peer OCI distribution)
gantry-build: ## Build the gantry binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(GANTRY_BIN) $(GANTRY_CMD)
gantry: test gantry-build ## Build gantry (implies test)
gantry-manifests: $(HELM) ## Render the Gantry operator profile into deploy/gantry/rendered
@mkdir -p $(GANTRY_MANIFEST_RENDERED_DIR)
@find $(GANTRY_MANIFEST_RENDERED_DIR) -mindepth 1 -not -name .gitignore -delete
@rm -rf $(GANTRY_OPERATOR_RENDER_DIR)
$(HELM) template gantry $(GANTRY_CHART_DIR) \
--namespace $(GANTRY_NAMESPACE) \
--values $(GANTRY_CHART_DIR)/values-operator.yaml \
--skip-schema-validation \
--set-string image.reference=$(GANTRY_IMAGE) \
--output-dir $(GANTRY_OPERATOR_RENDER_DIR)
@cp $(GANTRY_OPERATOR_RENDER_DIR)/gantry/templates/*.yaml $(GANTRY_MANIFEST_RENDERED_DIR)/
@rm -rf $(GANTRY_SUPPORT_RENDER_DIR)
$(GOCMD) run ./hack/cmd/render-manifests \
--templates-dir deploy/gantry \
--output-dir $(GANTRY_SUPPORT_RENDER_DIR) \
--set Namespace=$(GANTRY_NAMESPACE)
@cp -R $(GANTRY_SUPPORT_RENDER_DIR)/. $(GANTRY_MANIFEST_RENDERED_DIR)/
@rm -rf $(GANTRY_OPERATOR_RENDER_DIR) $(GANTRY_SUPPORT_RENDER_DIR)
@echo "Rendered gantry manifests into $(GANTRY_MANIFEST_RENDERED_DIR) (namespace: $(GANTRY_NAMESPACE))"
gantry-chart-lint: $(HELM) ## Validate the standalone Gantry Helm chart
$(HELM) lint $(GANTRY_CHART_DIR)
gantry-chart-package: gantry-chart-lint ## Package the standalone Gantry Helm chart
@mkdir -p $(GANTRY_CHART_PACKAGE_DIR)
@rm -f $(GANTRY_CHART_PACKAGE_DIR)/gantry-$(GANTRY_CHART_VERSION).tgz
@rm -rf $(GANTRY_CHART_STAGE_DIR)
$(GOCMD) run ./hack/cmd/gantry-chart-stage \
--source $(GANTRY_CHART_DIR) \
--output $(GANTRY_CHART_STAGE_DIR) \
--image-repository $(GANTRY_CHART_IMAGE_REPOSITORY)
$(HELM) package $(GANTRY_CHART_STAGE_DIR) \
--version $(GANTRY_CHART_VERSION) \
--app-version $(GANTRY_CHART_APP_VERSION) \
--destination $(GANTRY_CHART_PACKAGE_DIR)
@rm -rf $(GANTRY_CHART_STAGE_DIR)
# Inventory render knobs. SSLMode/Password feed the database config and
# secret templates; Password is base64-encoded data and defaults empty so
# the generic target stays secret-free (hack/inventory-dev/local.sh supplies
# a generated value).
INVENTORY_SSL_MODE ?= disable
INVENTORY_PG_PASSWORD_B64 ?=
inventory-manifests: ## Render inventory deployment manifests into deploy/inventory/rendered
@mkdir -p $(INVENTORY_MANIFEST_RENDERED_DIR)
@find $(INVENTORY_MANIFEST_RENDERED_DIR) -mindepth 1 -not -name .gitignore -delete
$(GOCMD) run ./hack/cmd/render-manifests \
--templates-dir $(INVENTORY_MANIFEST_TEMPLATES_DIR) \
--output-dir $(INVENTORY_MANIFEST_RENDERED_DIR) \
--set Namespace=$(INVENTORY_NAMESPACE) \
--set AggregatorImage=$(INVENTORY_AGGREGATOR_IMAGE) \
--set InspectorImage=$(INVENTORY_INSPECTOR_IMAGE) \
--set ViewerImage=$(INVENTORY_VIEWER_IMAGE) \
--set SSLMode=$(INVENTORY_SSL_MODE) \
--set Password=$(INVENTORY_PG_PASSWORD_B64)
@echo "Rendered inventory manifests into $(INVENTORY_MANIFEST_RENDERED_DIR) (namespace: $(INVENTORY_NAMESPACE))"
##@ Container Images
#
# Trivy (image scanning)
# ----------------------
# Set TRIVY=1 (or any non-empty value) on the make command line to scan after
# each image-*-local build, e.g.:
# TRIVY=1 make image-net-node-local
# TRIVY=1 make images-local
#
# Knobs (all overridable on the command line or environment):
# TRIVY Enable scanning when non-empty. Default: unset (no scan).
# TRIVY_VERSION Trivy CLI version. Default: 0.69.3 (matches CI).
# TRIVY_SEVERITY Comma-separated severities. Default: CRITICAL,HIGH.
# TRIVY_EXIT_CODE Exit code on findings. Default: 1 (fail). Set 0 to warn-only.
# TRIVY_IMAGE Override the trivy container image entirely.
# Default: aquasec/trivy:$(TRIVY_VERSION).
# TRIVY_CACHE_DIR Host dir for the trivy DB cache.
# Default: $$HOME/.cache/trivy.
TRIVY ?=
TRIVY_VERSION ?= 0.69.3
TRIVY_SEVERITY ?= CRITICAL,HIGH
TRIVY_EXIT_CODE ?= 1
TRIVY_IMAGE ?= aquasec/trivy:$(TRIVY_VERSION)
TRIVY_CACHE_DIR ?= $(HOME)/.cache/trivy
# Single-line shell command; expands to nothing when TRIVY is empty.
# Usage in a recipe: $(call trivy-maybe,image:tag)
#
# We pipe the image to trivy via `image save` + `--input` so the same
# recipe works with both docker and podman without needing a daemon
# socket mounted into the trivy container.
TRIVY_SCAN_CMD = mkdir -p $(TRIVY_CACHE_DIR) && \
tmp=$$(mktemp -t trivy-scan-XXXXXX.tar) && trap 'rm -f $$tmp' EXIT && \
$(CONTAINER_ENGINE) image save -o $$tmp $(1) && \
$(CONTAINER_ENGINE) run --rm \
-v $$tmp:/scan.tar:ro \
-v $(TRIVY_CACHE_DIR):/root/.cache/trivy \
$(TRIVY_IMAGE) image \
--severity $(TRIVY_SEVERITY) \
--exit-code $(TRIVY_EXIT_CODE) \
--format table \
--input /scan.tar
trivy-maybe = $(if $(strip $(TRIVY)),$(TRIVY_SCAN_CMD))
# Pre-fetch CNI plugins tarballs for local image builds.
# The Dockerfile reads resources/cni-plugins-linux-<arch>-<version>.tgz; this
# pattern rule fetches it on demand when the file is missing.
resources/cni-plugins-linux-%-$(CNI_PLUGINS_VERSION).tgz:
@mkdir -p resources
curl -fsSL \
"https://github.com/containernetworking/plugins/releases/download/$(CNI_PLUGINS_VERSION)/cni-plugins-linux-$*-$(CNI_PLUGINS_VERSION).tgz" \
-o $@
.PHONY: image-inventory-all-local
image-inventory-all-local: image-inventory-aggregator-local image-inventory-inspector-local image-inventory-viewer-local
.PHONY: image-inventory-all-push
image-inventory-all-push: image-inventory-aggregator-push image-inventory-inspector-push image-inventory-viewer-push
.PHONY: image-inventory-aggregator-local
image-inventory-aggregator-local: ## Build the inventory-aggregator container image
$(CONTAINER_ENGINE) build \
--build-arg VERSION=$(VERSION) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t inventory-aggregator:$(INVENTORY_AGGREGATOR_TAG) -t $(INVENTORY_AGGREGATOR_IMAGE) \
-f ./images/inventory/aggregator/Containerfile .
$(call trivy-maybe,$(INVENTORY_AGGREGATOR_IMAGE))
.PHONY: image-inventory-aggregator-push
image-inventory-aggregator-push: image-inventory-aggregator-local ## Build and push the inventory-aggregator container image
$(CONTAINER_ENGINE) push $(INVENTORY_AGGREGATOR_IMAGE)
.PHONY: image-inventory-inspector-local
image-inventory-inspector-local: ## Build the inventory-inspector container image
$(CONTAINER_ENGINE) build \
--build-arg VERSION=$(VERSION) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t inventory-inspector:$(INVENTORY_INSPECTOR_TAG) -t $(INVENTORY_INSPECTOR_IMAGE) \
-f ./images/inventory/inspector/Containerfile .
$(call trivy-maybe,$(INVENTORY_INSPECTOR_IMAGE))
.PHONY: image-inventory-inspector-push
image-inventory-inspector-push: image-inventory-inspector-local ## Build and push the inventory-inspector container image
$(CONTAINER_ENGINE) push $(INVENTORY_INSPECTOR_IMAGE)
.PHONY: image-inventory-viewer-local
image-inventory-viewer-local: ## Build the inventory-viewer container image
$(CONTAINER_ENGINE) build \
--build-arg VERSION=$(VERSION) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t inventory-viewer:$(INVENTORY_VIEWER_TAG) -t $(INVENTORY_VIEWER_IMAGE) \
-f ./images/inventory/viewer/Containerfile .
$(call trivy-maybe,$(INVENTORY_VIEWER_IMAGE))
.PHONY: image-inventory-viewer-push
image-inventory-viewer-push: image-inventory-viewer-local ## Build and push the inventory-viewer container image
$(CONTAINER_ENGINE) push $(INVENTORY_VIEWER_IMAGE)
image-machina-local: ## Build the machina container image locally (single-arch)
$(CONTAINER_ENGINE) build \
--build-arg VERSION=$(VERSION) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t machina:$(VERSION_TAG) -t $(MACHINA_IMAGE) \
-f ./images/machina/Containerfile .
$(call trivy-maybe,$(MACHINA_IMAGE))
# Retained for backwards compatibility with external callers (release pipelines).
machina-oci: image-machina-local ## Alias for image-machina-local
machina-oci-push: machina-oci ## Build and push the machina container image
$(CONTAINER_ENGINE) push $(MACHINA_IMAGE)
image-token-refresher-local: ## Build the token-refresher container image locally (single-arch)
$(CONTAINER_ENGINE) build \
--build-arg VERSION=$(VERSION) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t token-refresher:$(VERSION_TAG) -t $(TOKEN_REFRESHER_IMAGE) \
-f ./images/token-refresher/Containerfile .
$(call trivy-maybe,$(TOKEN_REFRESHER_IMAGE))
image-machine-ops-controller-local: ## Build the machine-ops-controller container image locally (single-arch)
$(CONTAINER_ENGINE) build \
--build-arg VERSION=$(VERSION) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t machine-ops-controller:$(VERSION_TAG) -t $(MACHINE_OPS_CONTROLLER_IMAGE) \
-f ./images/machine-ops-controller/Containerfile .
$(call trivy-maybe,$(MACHINE_OPS_CONTROLLER_IMAGE))
machine-ops-controller-oci: image-machine-ops-controller-local ## Alias for image-machine-ops-controller-local