Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7b88e4e
feat(signal): add generic certification controller
chokevin Oct 20, 2026
89d5f38
feat(signal): support grouped node certification
chokevin Oct 20, 2026
51ed0a9
signal: add participant backend adapter
chokevin Oct 20, 2026
6451a59
signal: fix adapter formatting
chokevin Oct 21, 2026
dd96266
signal: satisfy pinned adapter formatting
chokevin Oct 21, 2026
1757ee4
Merge upstream main into Signal adapter branch
chokevin Oct 21, 2026
c244e20
Merge latest upstream main into Signal adapter branch
chokevin Oct 21, 2026
1f804d6
signal: add typed workload execution policy
chokevin Oct 21, 2026
5f6a593
Merge latest upstream main into Signal adapter branch
chokevin Oct 21, 2026
45c7425
signal: support participant host networking
chokevin Oct 21, 2026
5513643
Merge latest upstream main into Signal adapter branch
chokevin Oct 21, 2026
3406e82
fix(signal): align script digest contract
chokevin Oct 22, 2026
5b117be
fix(signal): request Kueue topology
chokevin Oct 22, 2026
05d3472
fix(signal): preserve diagnostic results
chokevin Oct 22, 2026
82e6c22
feat(signal): add runtime execution contract
chokevin Oct 22, 2026
f6c1187
fix(signal): group deadline parameters
chokevin Oct 22, 2026
756a449
fix(signal): guard optional immutable fields
chokevin Oct 22, 2026
b517dee
fix(signal): preserve backend failure through cleanup
chokevin Oct 22, 2026
59eb0d5
fix(signal): fence MPI worker readiness
chokevin Oct 22, 2026
b8f78f0
fix(signal): probe MPI workers over SSH
chokevin Oct 22, 2026
f4259c8
fix(signal): harden MPI launcher recovery
chokevin Oct 22, 2026
4bbda32
fix(signal): preserve pre-cleanup backend evidence
chokevin Oct 22, 2026
8bae850
docs(signal): define evidence crash boundary
chokevin Oct 22, 2026
503e4c7
Merge branch 'main' into chokevin-upstream-mpijob-adapter
chokevin Oct 22, 2026
6d81f36
fix(signal): recover canonical workflow outputs
chokevin Oct 22, 2026
fcddc20
feat(signal): group certification checks in one run
chokevin Oct 22, 2026
7cc5f31
Merge remote-tracking branch 'origin/main' into chokevin-upstream-mpi…
chokevin Oct 22, 2026
fee5169
fix(signal): resolve grouped workflow template inputs
chokevin Oct 22, 2026
1d7cfe2
fix(signal): use valid terminal DAG dependencies
chokevin Oct 22, 2026
21e2d04
test(signal): satisfy grouped workflow lint
chokevin Oct 22, 2026
7724a4e
fix(signal): run grouped tasks with adapter service account
chokevin Oct 22, 2026
44bdf83
refactor(signal): trim certification to production path
chokevin Oct 23, 2026
db222a3
fix(signal): fence certification cleanup and validate execution evidence
chokevin Oct 23, 2026
ed49b0b
fix(signal): default backend references to the run namespace
chokevin Oct 23, 2026
7c16675
fix(signal): preserve standalone certification semantics
chokevin Oct 23, 2026
3ada75f
fix(signal): guard one-time certification invariants
chokevin Oct 23, 2026
f20d45f
fix(signal): preserve certification audit state
chokevin Oct 23, 2026
3d7b130
refactor(signal): narrow certification contract
chokevin Oct 24, 2026
5fd8025
fix(signal): harden one-time certification lifecycle
chokevin Oct 24, 2026
16e17e2
fix(signal): satisfy lifecycle lint checks
chokevin Oct 24, 2026
02715a5
fix(signal): align lint spacing
chokevin Oct 24, 2026
cd641dc
fix(signal): separate cancellation evidence steps
chokevin Oct 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ __debug*
tmp/
.tmp/
dist/
deploy/signal/rendered/

# Docker
.dockerignore
Expand Down
71 changes: 65 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ MACHINE_OPS_CONTROLLER_NAME ?= machine-ops-controller
MACHINE_OPS_PROVIDER ?=
MACHINE_OPS_SITE ?=

PROJECT_SIGNAL_CONTROLLER_BIN=bin/project-signal-controller
PROJECT_SIGNAL_CONTROLLER_CMD=./cmd/project-signal-controller
SIGNAL_CONTROLLER_IMAGE ?= example.invalid/project-signal-controller:replace-me
PROJECT_SIGNAL_ADAPTER_BIN=bin/project-signal-adapter
PROJECT_SIGNAL_ADAPTER_CMD=./cmd/project-signal-adapter
SIGNAL_ADAPTER_IMAGE ?= example.invalid/project-signal-adapter@sha256:0000000000000000000000000000000000000000000000000000000000000000
SIGNAL_ADAPTER_BUILD_IMAGE ?= example.invalid/project-signal-adapter:replace-me
SIGNAL_NAMESPACE ?= project-signal
SIGNAL_MANIFEST_TEMPLATES_DIR := deploy/signal
SIGNAL_MANIFEST_RENDERED_DIR := deploy/signal/rendered

METALMAN_BIN=bin/metalman
METALMAN_CMD=./cmd/metalman
NETBOOT_IMAGE ?= $(CONTAINER_REGISTRY)/netboot:$(VERSION_TAG)
Expand Down Expand Up @@ -233,14 +244,14 @@ 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 e2e-racer 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: 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 project-signal-controller project-signal-controller-build project-signal-adapter project-signal-adapter-build signal-manifests image-project-signal-controller-local image-project-signal-adapter-local metalman metalman-build metalman-oci metalman-oci-push unbounded-operator unbounded-operator-build unbounded-operator-manifests playpen-manifests e2e-gantry e2e-playpen e2e-racer 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-ipam unbounded-net-routeplan-debug unping unroute gantry ## Build all binaries (default)
all: kubectl-unbounded forge relctl machina machine-ops-controller project-signal-controller project-signal-adapter token-refresher unbounded-operator unbounded-net-controller unbounded-net-node unbounded-ipam unbounded-net-routeplan-debug unping unroute gantry ## Build all binaries (default)

help: ## Show this help
@echo ""
Expand Down Expand Up @@ -292,6 +303,9 @@ help: ## Show this help
@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 " project-signal-controller Build Project Signal controller"
@echo " project-signal-adapter Build Project Signal participant adapter"
@echo " signal-manifests Render Project Signal manifests"
@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"
Expand All @@ -313,6 +327,8 @@ help: ## Show this help
@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-project-signal-controller-local Build Project Signal controller image"
@echo " image-project-signal-adapter-local Build Project Signal adapter 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"
Expand Down Expand Up @@ -547,13 +563,13 @@ lint-actions: ## Run actionlint over .github/workflows
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
test: machina-manifests token-refresher-manifests machine-ops-manifests signal-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)
test: lint machina-manifests token-refresher-manifests machine-ops-manifests signal-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Run all tests (implies lint)
$(GOTEST) ./...

endif
Expand Down Expand Up @@ -661,13 +677,13 @@ image-racer-object-local: ## Build the racer-object container image locally (sin
-f ./images/racer-object/Containerfile .
$(call trivy-maybe,$(RACER_OBJECT_IMAGE))

build: machina-manifests token-refresher-manifests machine-ops-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Build all Go packages
build: machina-manifests token-refresher-manifests machine-ops-manifests signal-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
vulncheck: machina-manifests token-refresher-manifests machine-ops-manifests signal-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
Expand Down Expand Up @@ -1085,6 +1101,36 @@ machine-ops-controller-oci: image-machine-ops-controller-local ## Alias for imag
machine-ops-controller-oci-push: machine-ops-controller-oci ## Build and push the machine-ops-controller image
$(CONTAINER_ENGINE) push $(MACHINE_OPS_CONTROLLER_IMAGE)

project-signal-controller-build: signal-manifests ## Build Project Signal controller without lint/test
@mkdir -p bin
$(GOBUILD) -trimpath -ldflags '$(STAMP_LDFLAGS)' -o $(PROJECT_SIGNAL_CONTROLLER_BIN) $(PROJECT_SIGNAL_CONTROLLER_CMD)

project-signal-controller: test project-signal-controller-build ## Build Project Signal controller (implies test)

project-signal-adapter-build: signal-manifests ## Build Project Signal adapter without lint/test
@mkdir -p bin
$(GOBUILD) -trimpath -ldflags '$(STAMP_LDFLAGS)' -o $(PROJECT_SIGNAL_ADAPTER_BIN) $(PROJECT_SIGNAL_ADAPTER_CMD)

project-signal-adapter: test project-signal-adapter-build ## Build Project Signal adapter (implies test)

image-project-signal-controller-local: ## Build Project Signal controller image locally
$(CONTAINER_ENGINE) build \
--build-arg VERSION=$(VERSION) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t project-signal-controller:$(VERSION_TAG) -t $(SIGNAL_CONTROLLER_IMAGE) \
-f ./images/project-signal-controller/Containerfile .
$(call trivy-maybe,$(SIGNAL_CONTROLLER_IMAGE))

image-project-signal-adapter-local: ## Build Project Signal adapter image locally
$(CONTAINER_ENGINE) build \
--build-arg VERSION=$(VERSION) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t project-signal-adapter:$(VERSION_TAG) -t $(SIGNAL_ADAPTER_BUILD_IMAGE) \
-f ./images/project-signal-adapter/Containerfile .
$(call trivy-maybe,$(SIGNAL_ADAPTER_BUILD_IMAGE))

MACHINA_NAMESPACE ?= $(UNBOUNDED_NAMESPACE)
MACHINA_API_SERVER_ENDPOINT ?=
MACHINA_MANIFEST_TEMPLATES_DIR := deploy/machina
Expand Down Expand Up @@ -1157,6 +1203,19 @@ machine-ops-manifests: ## Render machine-ops-controller manifests into deploy/ma
--set APIServerEndpoint=$(MACHINE_OPS_API_SERVER_ENDPOINT)
@echo "Rendered machine-ops manifests into $(MACHINE_OPS_MANIFEST_RENDERED_DIR) (image: $(MACHINE_OPS_CONTROLLER_IMAGE))"

signal-manifests: ## Render Project Signal manifests into deploy/signal/rendered
@mkdir -p $(SIGNAL_MANIFEST_RENDERED_DIR)/crd
@find $(SIGNAL_MANIFEST_RENDERED_DIR) -mindepth 1 -not -name .gitignore -delete
$(GOCMD) run ./hack/cmd/render-manifests \
--templates-dir $(SIGNAL_MANIFEST_TEMPLATES_DIR) \
--output-dir $(SIGNAL_MANIFEST_RENDERED_DIR) \
--set Namespace=$(SIGNAL_NAMESPACE) \
--set ControllerImage=$(SIGNAL_CONTROLLER_IMAGE) \
--set AdapterImage=$(SIGNAL_ADAPTER_IMAGE)
@mkdir -p $(SIGNAL_MANIFEST_RENDERED_DIR)/crd
@cp $(SIGNAL_MANIFEST_TEMPLATES_DIR)/crd/*.yaml $(SIGNAL_MANIFEST_RENDERED_DIR)/crd/
@echo "Rendered Project Signal manifests into $(SIGNAL_MANIFEST_RENDERED_DIR) (image: $(SIGNAL_CONTROLLER_IMAGE))"

playpen-manifests: ## Render playpen operator and runner manifests into deploy/playpen/rendered
@mkdir -p $(PLAYPEN_MANIFEST_RENDERED_DIR)
@find $(PLAYPEN_MANIFEST_RENDERED_DIR) -mindepth 1 -not -name .gitignore -delete
Expand Down
129 changes: 129 additions & 0 deletions api/signal/v1alpha1/certificationprofile_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0

package v1alpha1

import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type ScriptReference struct {
Name string `json:"name"`
Key string `json:"key"`
// Digest is the approved algorithm-qualified SHA-256 digest of the script bytes.
// +kubebuilder:validation:Pattern=`^sha256:[0-9a-f]{64}$`
Digest string `json:"digest"`
}

type CheckID string

type ParticipantRoleConstraint struct {
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=63
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
Name string `json:"name"`
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=64
Count int32 `json:"count"`
// Selector further constrains participants assigned to this role.
Selector metav1.LabelSelector `json:"selector,omitempty"`
}

type ParticipantTopologyConstraint struct {
// Role names the participant role whose selected Nodes are evaluated.
Role string `json:"role"`
// LabelKey is the Node label used to identify topology domains.
LabelKey string `json:"labelKey"`
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=64
MinDistinctValues int32 `json:"minDistinctValues"`
}

type CheckParticipantPolicy struct {
// +kubebuilder:validation:MaxItems=16
Roles []string `json:"roles,omitempty"`
}

// +kubebuilder:validation:XValidation:rule="duration(self.backendActiveDeadline) > duration('0s')",message="backendActiveDeadline must be positive"
// +kubebuilder:validation:XValidation:rule="!has(self.scriptEnvironment) || self.scriptEnvironment.all(k, size(k) <= 253 && size(self.scriptEnvironment[k]) <= 4096)",message="scriptEnvironment names must not exceed 253 characters and values must not exceed 4096 characters"
type CertificationCheck struct {
// ID and Order are derived from the check's position and are never serialized.
ID CheckID `json:"-"`
Order int32 `json:"-"`
Script ScriptReference `json:"script"`
RunnerImage string `json:"runnerImage"`
Backend ExecutionBackend `json:"backend"`
WorkloadPolicy WorkloadPolicy `json:"workloadPolicy,omitempty"`
// +kubebuilder:validation:Minimum=0
GPUCount int32 `json:"gpuCount,omitempty"`
// +kubebuilder:validation:MaxProperties=32
ScriptEnvironment map[string]string `json:"scriptEnvironment,omitempty"`
BackendActiveDeadline metav1.Duration `json:"backendActiveDeadline"`
ParticipantPolicy CheckParticipantPolicy `json:"participantPolicy,omitempty"`
}

// +kubebuilder:validation:Enum=Pod;MPIJob
type ExecutionBackend string

const (
ExecutionBackendPod ExecutionBackend = "Pod"
ExecutionBackendMPIJob ExecutionBackend = "MPIJob"
)

type ParticipantPolicy struct {
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=64
Count int32 `json:"count"`

Selector metav1.LabelSelector `json:"selector"`

MinimumAllocatable corev1.ResourceList `json:"minimumAllocatable,omitempty"`
}

type WorkloadHostPath struct {
MountPath string `json:"mountPath"`
Path string `json:"path"`
}

type WorkloadPolicy struct {
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
Privileged bool `json:"privileged,omitempty"`
Capabilities []corev1.Capability `json:"capabilities,omitempty"`
HostNetwork bool `json:"hostNetwork,omitempty"`
WorkingDir string `json:"workingDir,omitempty"`
// +kubebuilder:validation:MaxItems=16
HostPaths []WorkloadHostPath `json:"hostPaths,omitempty"`
}

// +kubebuilder:validation:XValidation:rule="self.checks.all(c, duration(self.deadline) - duration(c.backendActiveDeadline) >= duration('60s'))",message="every backendActiveDeadline must leave at least 60 seconds before deadline"
// +kubebuilder:validation:XValidation:rule="self.checks.map(c, duration(c.backendActiveDeadline) + duration('60s')).sum() <= duration(self.deadline)",message="deadline must cover every sequential backendActiveDeadline plus 60 seconds of evidence time per check"
type CertificationProfileSpec struct {
Participants ParticipantPolicy `json:"participants"`
// +kubebuilder:validation:MaxItems=16
ParticipantRoles []ParticipantRoleConstraint `json:"participantRoles,omitempty"`
// +kubebuilder:validation:MaxItems=16
TopologyConstraints []ParticipantTopologyConstraint `json:"topologyConstraints,omitempty"`
// +kubebuilder:validation:MinItems=15
// +kubebuilder:validation:MaxItems=15
Checks []CertificationCheck `json:"checks"`
Deadline metav1.Duration `json:"deadline"`
}

// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster,shortName=certprofile
// +kubebuilder:printcolumn:name="Checks",type=integer,JSONPath=`.spec.checks.size()`
// +kubebuilder:printcolumn:name="Participants",type=integer,JSONPath=`.spec.participants.count`
type CertificationProfile struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec CertificationProfileSpec `json:"spec"`
}

// +kubebuilder:object:root=true
type CertificationProfileList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CertificationProfile `json:"items"`
}
Loading
Loading