Run the controller as a Go bundle the host starts as a process (hq issue 213, 2 of 2) #253
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
ARG GO_BASE=golang:1.25-alpine
|
ARG GO_BASE=golang:1.25-alpine
|
||||||
# The control plane's image.
|
# The control plane's image — for genesis and the lab only. The mesh runs the controller as a Go
|
||||||
|
# bundle the host starts as a process (module.json; novox/hq issue 213), and builds no image of it.
|
||||||
#
|
#
|
||||||
# novox/hq ADR 0006: this image is pinned by digest in the bundle the host carries, fetched on a
|
# novox/hq ADR 0006: this image is pinned by digest in the bundle the host carries, fetched on a
|
||||||
# machine where no mesh exists yet, and run before there is anything to check it against. So it
|
# machine where no mesh exists yet, and run before there is anything to check it against. So it
|
||||||
|
|||||||
@@ -27,17 +27,21 @@ build:
|
|||||||
IMAGE ?= mesh-controller:$(VERSION)
|
IMAGE ?= mesh-controller:$(VERSION)
|
||||||
DEV_TAG ?= mesh-controller:development
|
DEV_TAG ?= mesh-controller:development
|
||||||
|
|
||||||
# The base the module declares, read from the manifest rather than written here twice.
|
# The Go base the image is built on.
|
||||||
#
|
#
|
||||||
# **`make image` was broken and stayed broken**, because the Dockerfile's fallback base was a Go
|
# **`make image` was broken and stayed broken**, because the Dockerfile's fallback base was a Go
|
||||||
# older than go.mod asks for: every build died at `go mod download` with "go.mod requires go >=
|
# older than go.mod asks for: every build died at `go mod download` with "go.mod requires go >=
|
||||||
# 1.26.0", and the pipeline never saw it because the pipeline passes the declared base in. Anybody
|
# 1.26.0", and the pipeline never saw it because the pipeline passes the declared base in. Anybody
|
||||||
# building the image by hand hit it and had to find the digest themselves (novox/hq 04-ISSUES/146,
|
# building the image by hand hit it and had to find the digest themselves (novox/hq 04-ISSUES/146,
|
||||||
# what it cost).
|
# what it cost).
|
||||||
GO_BASE ?= $(shell python3 -c "import json;print(next(o['image'] for o in json.load(open('module.json'))['build']['on'] if o['arg']=='GO_BASE'))" 2>/dev/null)
|
#
|
||||||
|
# **Pinned here since the manifest stopped building an image** (novox/hq issue 213): the mesh builds
|
||||||
|
# the controller as a Go bundle with its own toolchain, and only `make image` — genesis and the lab —
|
||||||
|
# still needs a Go base. The digest is the one the manifest declared until then.
|
||||||
|
GO_BASE ?= golang@sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c
|
||||||
|
|
||||||
image:
|
image:
|
||||||
@test -n "$(GO_BASE)" || { echo "module.json declares no GO_BASE; pass GO_BASE=<image> or fix the manifest"; exit 1; }
|
@test -n "$(GO_BASE)" || { echo "no GO_BASE; pass GO_BASE=<image>"; exit 1; }
|
||||||
docker build --build-arg GO_BASE=$(GO_BASE) --build-arg VERSION=$(VERSION) -t $(IMAGE) -t $(DEV_TAG) .
|
docker build --build-arg GO_BASE=$(GO_BASE) --build-arg VERSION=$(VERSION) -t $(IMAGE) -t $(DEV_TAG) .
|
||||||
@echo
|
@echo
|
||||||
@docker image inspect $(IMAGE) --format 'built {{.RepoTags}} {{.Size}} bytes'
|
@docker image inspect $(IMAGE) --format 'built {{.RepoTags}} {{.Size}} bytes'
|
||||||
@@ -48,7 +52,7 @@ BUILDER_IMAGE ?= mesh-builder:$(VERSION)
|
|||||||
BUILDER_DEV_TAG ?= mesh-builder:development
|
BUILDER_DEV_TAG ?= mesh-builder:development
|
||||||
|
|
||||||
builder-image:
|
builder-image:
|
||||||
@test -n "$(GO_BASE)" || { echo "module.json declares no GO_BASE; pass GO_BASE=<image> or fix the manifest"; exit 1; }
|
@test -n "$(GO_BASE)" || { echo "no GO_BASE; pass GO_BASE=<image>"; exit 1; }
|
||||||
docker build --build-arg GO_BASE=$(GO_BASE) -f cmd/mesh-builder/Dockerfile -t $(BUILDER_IMAGE) -t $(BUILDER_DEV_TAG) .
|
docker build --build-arg GO_BASE=$(GO_BASE) -f cmd/mesh-builder/Dockerfile -t $(BUILDER_IMAGE) -t $(BUILDER_DEV_TAG) .
|
||||||
@echo
|
@echo
|
||||||
@docker image inspect $(BUILDER_IMAGE) --format 'built {{.RepoTags}} {{.Size}} bytes'
|
@docker image inspect $(BUILDER_IMAGE) --format 'built {{.RepoTags}} {{.Size}} bytes'
|
||||||
|
|||||||
@@ -193,6 +193,13 @@ passes every check that only looks at the message.
|
|||||||
|
|
||||||
## The image
|
## The image
|
||||||
|
|
||||||
|
**The mesh no longer runs the controller from it** (novox/hq issue 213). The module declares a Go
|
||||||
|
bundle, `controller`, which the host on the controller's machine unpacks and runs as the process
|
||||||
|
`mesh-controller` under the account of the same name (ADR 0188 §1, §3). The image stays for what
|
||||||
|
still runs a container of the controller: genesis, which raises the first controller from it and
|
||||||
|
installs the module from its manifest (mesh-host `internal/bootstrap`), and the lab. Neither is the
|
||||||
|
mesh's own build any more — `make image` builds it.
|
||||||
|
|
||||||
`FROM scratch`, holding one statically linked binary and nothing else — no shell, no package
|
`FROM scratch`, holding one statically linked binary and nothing else — no shell, no package
|
||||||
manager, no libc, no CA certificates.
|
manager, no libc, no CA certificates.
|
||||||
|
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ func TestTheRegistryTrustAndEveryImageFollowThePortTheNodeGaveTheStore(t *testin
|
|||||||
//
|
//
|
||||||
// Composed from the control plane's own manifest against a real inventory: the store's module is
|
// Composed from the control plane's own manifest against a real inventory: the store's module is
|
||||||
// given 6852 on this node the way genesis or an operator gives it, and the control plane's
|
// given 6852 on this node the way genesis or an operator gives it, and the control plane's
|
||||||
// container is told so beside the sealed connection genesis wrote.
|
// process is told so beside the sealed connection genesis wrote.
|
||||||
func TestTheControlPlaneIsToldWhereTheNodePutTheStoreAndTheBroker(t *testing.T) {
|
func TestTheControlPlaneIsToldWhereTheNodePutTheStoreAndTheBroker(t *testing.T) {
|
||||||
open := aMesh(t)
|
open := aMesh(t)
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
@@ -157,8 +157,8 @@ func TestTheControlPlaneIsToldWhereTheNodePutTheStoreAndTheBroker(t *testing.T)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
control, err := withSeatPorts(m).Resolve([]catalogue.Built{{Name: "server", Kind: catalogue.ArtifactImage,
|
control, err := withSeatPorts(m).Resolve([]catalogue.Built{{Name: "controller", Kind: catalogue.ArtifactBundle,
|
||||||
Reference: "registry.example/control@" + aDigest}})
|
Reference: "https://registry.example/mesh-controller/controller.tar.gz", Digest: aDigest}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -200,12 +200,12 @@ func TestTheControlPlaneIsToldWhereTheNodePutTheStoreAndTheBroker(t *testing.T)
|
|||||||
|
|
||||||
var env map[string]any
|
var env map[string]any
|
||||||
for _, r := range composed(t, open, "anchor").Resources {
|
for _, r := range composed(t, open, "anchor").Resources {
|
||||||
if r["id"] == "mesh-controller.server" {
|
if r["id"] == "mesh-controller.controller" {
|
||||||
env, _ = r["env"].(map[string]any)
|
env, _ = r["env"].(map[string]any)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if env == nil {
|
if env == nil {
|
||||||
t.Fatal("the control plane's container is not in its own node's declaration")
|
t.Fatal("the control plane's process is not in its own node's declaration")
|
||||||
}
|
}
|
||||||
for key, want := range map[string]string{
|
for key, want := range map[string]string{
|
||||||
"MESH_STORE_INVENTORY_PORT": "6852",
|
"MESH_STORE_INVENTORY_PORT": "6852",
|
||||||
@@ -238,7 +238,7 @@ func withSeatPorts(m catalogue.Manifest) catalogue.Manifest {
|
|||||||
out := m
|
out := m
|
||||||
out.Resources = nil
|
out.Resources = nil
|
||||||
for _, r := range m.Resources {
|
for _, r := range m.Resources {
|
||||||
if r["type"] != "container" {
|
if r["type"] != "container" && r["type"] != "process" {
|
||||||
out.Resources = append(out.Resources, r)
|
out.Resources = append(out.Resources, r)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package catalogue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// novox/hq issue 213: the controller is a Go program and was the one piece of the mesh's own Go
|
||||||
|
// code still shipped as an image (ADR 0188 §1). Its own manifest, composed for the machine that runs
|
||||||
|
// it, is a Go bundle run by the host as a process — and no container.
|
||||||
|
func TestTheControllerIsAProcessAndNoContainer(t *testing.T) {
|
||||||
|
raw, err := os.ReadFile("../../module.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
m, err := ParseManifest(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("the controller's own manifest does not parse:\n%v", err)
|
||||||
|
}
|
||||||
|
if m.Build == nil || len(m.Build.Artifacts) != 1 {
|
||||||
|
t.Fatalf("the controller builds %+v; it is one bundle", m.Build)
|
||||||
|
}
|
||||||
|
a := m.Build.Artifacts[0]
|
||||||
|
if a.Kind != ArtifactBundle || a.Language != "go" || a.System == "" || BinaryOf(a) != "mesh-controller" {
|
||||||
|
t.Fatalf("the controller's artifact is %+v, not a Go bundle naming its system and binary", a)
|
||||||
|
}
|
||||||
|
for _, c := range m.Capabilities {
|
||||||
|
if c == "container-runtime" {
|
||||||
|
t.Error("the controller still requires a container runtime on its machine")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
digest := "sha256:" + strings.Repeat("c", 64)
|
||||||
|
control, err := m.Resolve([]Built{{Name: a.Name, Kind: ArtifactBundle,
|
||||||
|
Reference: ArtifactStoreScheme + "mesh-controller/" + a.Name + "@" + digest, Digest: digest}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// The node's runtime does not launch it: it serves its seat's verbs itself.
|
||||||
|
if loads := control.Bundles[0].Loads; len(loads) != 0 {
|
||||||
|
t.Errorf("the node's runtime would launch the controller as a tools bundle: %v", loads)
|
||||||
|
}
|
||||||
|
|
||||||
|
needed := map[string]map[string]string{"mesh-controller": {}}
|
||||||
|
for name := range m.OwnSecrets {
|
||||||
|
needed["mesh-controller"][name] = "sealed-" + name
|
||||||
|
}
|
||||||
|
out, err := Resolution{Node: "anchor", Modules: []Manifest{control}}.Declaration(Rendering{
|
||||||
|
Needed: needed, ArtifactStore: "anchor.internal:5100",
|
||||||
|
Seats: map[string]map[int]int{"mesh-store": {5432: 6852}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("the controller does not compose: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var process, step map[string]any
|
||||||
|
account, firstSecret := -1, -1
|
||||||
|
for i, r := range out {
|
||||||
|
switch {
|
||||||
|
case r["type"] == "container":
|
||||||
|
t.Errorf("the controller's declaration still runs a container: %v", r)
|
||||||
|
case r["id"] == "mesh-controller.controller":
|
||||||
|
process = r
|
||||||
|
case r["id"] == "mesh-controller.controller-prepare":
|
||||||
|
step = r
|
||||||
|
if process != nil {
|
||||||
|
t.Error("the controller's preparation is placed after the process it prepares for")
|
||||||
|
}
|
||||||
|
case r["type"] == "user" && r["name"] == "mesh-controller":
|
||||||
|
account = i
|
||||||
|
case strings.HasPrefix(fmt.Sprint(r["id"]), "mesh-controller.needs-") && firstSecret < 0:
|
||||||
|
firstSecret = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if process == nil {
|
||||||
|
t.Fatalf("the controller's process is not in its declaration: %v", out)
|
||||||
|
}
|
||||||
|
if run, _ := json.Marshal(process["run"]); string(run) != `["./mesh-controller","serve"]` {
|
||||||
|
t.Errorf("the controller is run as %s, not its own bundle's binary", run)
|
||||||
|
}
|
||||||
|
if process["source"] != "anchor.internal:5100/mesh-controller/"+a.Name+"@"+digest || process["digest"] != digest {
|
||||||
|
t.Errorf("the controller's bundle is fetched from %v (%v)", process["source"], process["digest"])
|
||||||
|
}
|
||||||
|
// The user: an account the host declares, which owns what the process reads.
|
||||||
|
if process["user"] != "mesh-controller" || account < 0 {
|
||||||
|
t.Errorf("the controller runs as %v, and the account declared is at %d", process["user"], account)
|
||||||
|
}
|
||||||
|
if firstSecret >= 0 && account > firstSecret {
|
||||||
|
t.Error("the controller's secrets are written before the account they belong to exists")
|
||||||
|
}
|
||||||
|
for _, r := range out {
|
||||||
|
if strings.HasPrefix(fmt.Sprint(r["id"]), "mesh-controller.needs-") && r["owner"] != "mesh-controller" {
|
||||||
|
t.Errorf("%v belongs to %v, which the controller's process cannot read", r["id"], r["owner"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dir := fileNamed(out, "mesh-controller.mesh-state"); dir == nil || dir["owner"] != "mesh-controller" {
|
||||||
|
t.Errorf("the controller's state directory is not its account's to enter: %v", dir)
|
||||||
|
}
|
||||||
|
// Each mount became a path the process reads: nothing it is told is a path inside a container.
|
||||||
|
state := fmt.Sprint(fileNamed(out, "mesh-controller.mesh-state")["path"])
|
||||||
|
env, _ := process["env"].(map[string]any)
|
||||||
|
for key, value := range env {
|
||||||
|
v := fmt.Sprint(value)
|
||||||
|
if strings.HasPrefix(v, "/run/secrets") || strings.HasPrefix(v, "/broker-tls") {
|
||||||
|
t.Errorf("%s=%s is a path inside the container the controller no longer runs in", key, v)
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(key, "_FILE") && !strings.HasPrefix(v, state+"/") {
|
||||||
|
t.Errorf("%s=%s is not one of the files the mesh places for it", key, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if env["MESH_BROKER_CERTIFICATE"] != "/var/lib/mesh-broker-tls/tls.crt" {
|
||||||
|
t.Errorf("the controller reads the broker's certificate from %v", env["MESH_BROKER_CERTIFICATE"])
|
||||||
|
}
|
||||||
|
if env["MESH_STORE_INVENTORY_PORT"] != "6852" {
|
||||||
|
t.Errorf("the controller is told the store is on %v; the node put it on 6852", env["MESH_STORE_INVENTORY_PORT"])
|
||||||
|
}
|
||||||
|
// The handover: the container it ran as goes only once this is running.
|
||||||
|
if got, _ := json.Marshal(process["replaces"]); string(got) != `["mesh-controller.server"]` {
|
||||||
|
t.Errorf("the controller's process replaces %s, not the container it ran as", got)
|
||||||
|
}
|
||||||
|
// And its state is prepared first, by the same program as the same account.
|
||||||
|
if step == nil || step["run-once"] != true || step["user"] != "mesh-controller" {
|
||||||
|
t.Fatalf("the controller's preparation is %v", step)
|
||||||
|
}
|
||||||
|
if run, _ := json.Marshal(step["run"]); string(run) != `["./mesh-controller","prepare"]` {
|
||||||
|
t.Errorf("the controller's preparation runs %s", run)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,7 +101,7 @@ func TestTheControlPlanesOwnAddressesFollowTheNodesPorts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// The manifest itself names them now; withSeatPorts is a no-op on it, and this holds it so.
|
// The manifest itself names them now; withSeatPorts is a no-op on it, and this holds it so.
|
||||||
for _, r := range m.Resources {
|
for _, r := range m.Resources {
|
||||||
if r["type"] != "container" {
|
if r["type"] != "process" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
env, _ := r["env"].(map[string]any)
|
env, _ := r["env"].(map[string]any)
|
||||||
@@ -113,8 +113,9 @@ func TestTheControlPlanesOwnAddressesFollowTheNodesPorts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
m = withSeatPorts(m)
|
m = withSeatPorts(m)
|
||||||
control, err := m.Resolve([]Built{{
|
control, err := m.Resolve([]Built{{
|
||||||
Name: "server", Kind: ArtifactImage,
|
Name: "controller", Kind: ArtifactBundle,
|
||||||
Reference: ArtifactStoreScheme + "mesh-controller/server@sha256:" + strings.Repeat("c", 64),
|
Reference: ArtifactStoreScheme + "mesh-controller/controller@sha256:" + strings.Repeat("c", 64),
|
||||||
|
Digest: "sha256:" + strings.Repeat("c", 64),
|
||||||
}})
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -135,9 +136,9 @@ func TestTheControlPlanesOwnAddressesFollowTheNodesPorts(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("the control plane does not compose: %v", err)
|
t.Fatalf("the control plane does not compose: %v", err)
|
||||||
}
|
}
|
||||||
server := fileNamed(out, "mesh-controller.server")
|
server := fileNamed(out, "mesh-controller.controller")
|
||||||
if server == nil {
|
if server == nil {
|
||||||
t.Fatalf("the control plane's container is not in the declaration: %v", out)
|
t.Fatalf("the control plane's process is not in the declaration: %v", out)
|
||||||
}
|
}
|
||||||
env, _ := server["env"].(map[string]any)
|
env, _ := server["env"].(map[string]any)
|
||||||
for key, want := range map[string]string{
|
for key, want := range map[string]string{
|
||||||
@@ -151,8 +152,8 @@ func TestTheControlPlanesOwnAddressesFollowTheNodesPorts(t *testing.T) {
|
|||||||
t.Errorf("the control plane is told %s=%v; the node put it on %s", key, env[key], want)
|
t.Errorf("the control plane is told %s=%v; the node put it on %s", key, env[key], want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if got := server["image"]; got != "anchor.internal:5100/mesh-controller/server@sha256:"+strings.Repeat("c", 64) {
|
if got := server["source"]; got != "anchor.internal:5100/mesh-controller/controller@sha256:"+strings.Repeat("c", 64) {
|
||||||
t.Errorf("the control plane's own image is %v, not routed through the store", got)
|
t.Errorf("the control plane's own bundle is fetched from %v, not routed through the store", got)
|
||||||
}
|
}
|
||||||
|
|
||||||
// And on a mesh where the foundation is where genesis raised it, nothing is added.
|
// And on a mesh where the foundation is where genesis raised it, nothing is added.
|
||||||
@@ -160,7 +161,7 @@ func TestTheControlPlanesOwnAddressesFollowTheNodesPorts(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
env, _ = fileNamed(out, "mesh-controller.server")["env"].(map[string]any)
|
env, _ = fileNamed(out, "mesh-controller.controller")["env"].(map[string]any)
|
||||||
if env["MESH_STORE_INVENTORY_PORT"] != "" {
|
if env["MESH_STORE_INVENTORY_PORT"] != "" {
|
||||||
t.Errorf("with no settings, the control plane is told %v", env)
|
t.Errorf("with no settings, the control plane is told %v", env)
|
||||||
}
|
}
|
||||||
@@ -186,7 +187,7 @@ func withSeatPorts(m Manifest) Manifest {
|
|||||||
out := m
|
out := m
|
||||||
out.Resources = nil
|
out.Resources = nil
|
||||||
for _, r := range m.Resources {
|
for _, r := range m.Resources {
|
||||||
if r["type"] != "container" {
|
if r["type"] != "container" && r["type"] != "process" {
|
||||||
out.Resources = append(out.Resources, r)
|
out.Resources = append(out.Resources, r)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func TestTheManifestsOwnPlaceholderUnfilledLeavesTheStoreWhereTheFileSays(t *tes
|
|||||||
}
|
}
|
||||||
var written string
|
var written string
|
||||||
for _, r := range m.Resources {
|
for _, r := range m.Resources {
|
||||||
if r.Type == "container" {
|
if r.Type == "process" {
|
||||||
written = r.Env["MESH_STORE_INVENTORY_PORT"]
|
written = r.Env["MESH_STORE_INVENTORY_PORT"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-38
@@ -2,9 +2,6 @@
|
|||||||
"module": "mesh-controller",
|
"module": "mesh-controller",
|
||||||
"version": "1",
|
"version": "1",
|
||||||
"slug": "control",
|
"slug": "control",
|
||||||
"capabilities": [
|
|
||||||
"container-runtime"
|
|
||||||
],
|
|
||||||
"claims": [
|
"claims": [
|
||||||
{
|
{
|
||||||
"name": "mesh-controller",
|
"name": "mesh-controller",
|
||||||
@@ -26,7 +23,7 @@
|
|||||||
"broker-address": "${dir:mesh-state}/broker-address",
|
"broker-address": "${dir:mesh-state}/broker-address",
|
||||||
"bus": "${dir:mesh-state}/bus"
|
"bus": "${dir:mesh-state}/bus"
|
||||||
},
|
},
|
||||||
"secrets-owner": "65534:65534",
|
"secrets-owner": "mesh-controller",
|
||||||
"prepares": true,
|
"prepares": true,
|
||||||
"tools": [
|
"tools": [
|
||||||
"tools",
|
"tools",
|
||||||
@@ -43,62 +40,58 @@
|
|||||||
"build"
|
"build"
|
||||||
],
|
],
|
||||||
"resources": [
|
"resources": [
|
||||||
|
{
|
||||||
|
"id": "account",
|
||||||
|
"type": "user",
|
||||||
|
"name": "mesh-controller",
|
||||||
|
"shell": "/usr/bin/nologin",
|
||||||
|
"home": "/var/lib/mesh-controller"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "mesh-state",
|
"id": "mesh-state",
|
||||||
"type": "directory",
|
"type": "directory",
|
||||||
"mode": "0700",
|
"mode": "0700",
|
||||||
"place": "mesh"
|
"place": "mesh",
|
||||||
|
"owner": "mesh-controller"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "server",
|
"id": "controller",
|
||||||
"type": "container",
|
"type": "process",
|
||||||
"name": "mesh-controller",
|
"name": "mesh-controller",
|
||||||
"network": "host",
|
"artifact": "controller",
|
||||||
"args": [
|
"run": [
|
||||||
|
"./mesh-controller",
|
||||||
"serve"
|
"serve"
|
||||||
],
|
],
|
||||||
|
"user": "mesh-controller",
|
||||||
"env": {
|
"env": {
|
||||||
"MESH_BROKER_CERTIFICATE": "/broker-tls/tls.crt",
|
"MESH_BROKER_CERTIFICATE": "/var/lib/mesh-broker-tls/tls.crt",
|
||||||
"MESH_STORE_INVENTORY_FILE": "/run/secrets/inventory",
|
"MESH_STORE_INVENTORY_FILE": "${dir:mesh-state}/inventory",
|
||||||
"MESH_STORE_IDENTITY_FILE": "/run/secrets/identity",
|
"MESH_STORE_IDENTITY_FILE": "${dir:mesh-state}/identity",
|
||||||
"MESH_STORE_LICENCES_FILE": "/run/secrets/licences",
|
"MESH_STORE_LICENCES_FILE": "${dir:mesh-state}/licences",
|
||||||
"MESH_BROKER_MANAGEMENT_FILE": "/run/secrets/broker-management",
|
"MESH_BROKER_MANAGEMENT_FILE": "${dir:mesh-state}/broker-management",
|
||||||
"MESH_BROKER_ADDRESS_FILE": "/run/secrets/broker-address",
|
"MESH_BROKER_ADDRESS_FILE": "${dir:mesh-state}/broker-address",
|
||||||
"MESH_STORE_INVENTORY_PORT": "${seat:mesh-store:5432}",
|
"MESH_STORE_INVENTORY_PORT": "${seat:mesh-store:5432}",
|
||||||
"MESH_STORE_IDENTITY_PORT": "${seat:mesh-store:5432}",
|
"MESH_STORE_IDENTITY_PORT": "${seat:mesh-store:5432}",
|
||||||
"MESH_STORE_LICENCES_PORT": "${seat:mesh-store:5432}",
|
"MESH_STORE_LICENCES_PORT": "${seat:mesh-store:5432}",
|
||||||
"MESH_BROKER_MANAGEMENT_PORT": "${seat:mesh-broker:15672}",
|
"MESH_BROKER_MANAGEMENT_PORT": "${seat:mesh-broker:15672}",
|
||||||
"MESH_BROKER_ADDRESS_PORT": "${seat:mesh-broker:5671}",
|
"MESH_BROKER_ADDRESS_PORT": "${seat:mesh-broker:5671}",
|
||||||
"MESH_BUS_NATS_FILE": "/run/secrets/bus"
|
"MESH_BUS_NATS_FILE": "${dir:mesh-state}/bus"
|
||||||
},
|
},
|
||||||
"volumes": [
|
"replaces": [
|
||||||
"/var/lib/mesh-broker-tls:/broker-tls:ro",
|
"server"
|
||||||
"${dir:mesh-state}/inventory:/run/secrets/inventory:ro",
|
|
||||||
"${dir:mesh-state}/identity:/run/secrets/identity:ro",
|
|
||||||
"${dir:mesh-state}/licences:/run/secrets/licences:ro",
|
|
||||||
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
|
|
||||||
"${dir:mesh-state}/bus:/run/secrets/bus:ro",
|
|
||||||
"${dir:mesh-state}/broker-management:/run/secrets/broker-management:ro",
|
|
||||||
"${dir:mesh-state}/broker-address:/run/secrets/broker-address:ro"
|
|
||||||
],
|
|
||||||
"artifact": "server",
|
|
||||||
"restart-on": [
|
|
||||||
"control-env"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"name": "server",
|
"name": "controller",
|
||||||
"kind": "image",
|
"kind": "bundle",
|
||||||
"from": "Dockerfile"
|
"language": "go",
|
||||||
}
|
"system": "arch",
|
||||||
],
|
"from": "cmd/mesh-controller",
|
||||||
"on": [
|
"binary": "mesh-controller"
|
||||||
{
|
|
||||||
"arg": "GO_BASE",
|
|
||||||
"image": "golang@sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user