diff --git a/examples/modules/gitea.json b/examples/modules/gitea.json index dd033fa..ba01a97 100644 --- a/examples/modules/gitea.json +++ b/examples/modules/gitea.json @@ -1,36 +1,83 @@ { "module": "gitea", "version": "1", - - "requires": ["postgres-database"], - "contributes": { - "postgres-database": {"name": "gitea"} - }, - "binds": {"postgres-database": "/var/lib/gitea/database.json"}, - "secrets": {"postgres-database": "/var/lib/gitea/database.secret"}, - - "capabilities": ["container-runtime"], - - "listens": [ - {"port": 3000, "protocol": "tcp", "from": "mesh", "why": "the forge, over http"}, - {"port": 2222, "protocol": "tcp", "from": "mesh", - "why": "git over ssh. Not 22: the machine's own daemon holds that, and a module does not take it"} + "requires": [ + "postgres-database" ], - - "own-secrets": {"internal-token": "/var/lib/gitea/internal-token.secret"}, - + "contributes": { + "postgres-database": { + "name": "gitea" + } + }, + "binds": { + "postgres-database": "/var/lib/gitea/database.json" + }, + "secrets": { + "postgres-database": "/var/lib/gitea/database.secret" + }, + "capabilities": [ + "container-runtime" + ], + "listens": [ + { + "port": 3000, + "protocol": "tcp", + "from": "mesh", + "why": "the forge, over http" + }, + { + "port": 2222, + "protocol": "tcp", + "from": "mesh", + "why": "git over ssh. Not 22: the machine's own daemon holds that, and a module does not take it" + } + ], + "own-secrets": { + "internal-token": "/var/lib/gitea/internal-token.secret" + }, "resources": [ - {"id": "state", "type": "directory", "path": "/var/lib/gitea", "mode": "0700"}, - - {"id": "server-env", "type": "file", "path": "/var/lib/gitea/server.env", "mode": "0600", - "content": "GITEA__security__INTERNAL_TOKEN=${secret:internal-token}\nGITEA__database__DB_TYPE=postgres\nGITEA__database__HOST=${bound:postgres-database:at}:${bound:postgres-database:port}\nGITEA__database__NAME=gitea\nGITEA__database__USER=${bound:postgres-database:as}\nGITEA__database__PASSWD=${secret:postgres-database}\n"}, - - {"id": "server", "type": "container", "name": "gitea", - "image": "gitea/gitea@sha256:dfc61e347c8b582df918f4556401bf2cecdfbdb56c5282ae9488dd76fca3e41c", - "env": {"DB_TYPE": "postgres", "USER_UID": "1000", "USER_GID": "1000"}, - "env-file": ["/var/lib/gitea/server.env"], - "ports": ["3000:3000", "2222:22"], - "volumes": ["/services/gitea/gitea:/data"], - "restart-on": ["server-env"]} + { + "id": "state", + "type": "directory", + "path": "/var/lib/gitea", + "mode": "0700" + }, + { + "id": "server-env", + "type": "file", + "path": "/var/lib/gitea/server.env", + "mode": "0600", + "content": "GITEA__security__INTERNAL_TOKEN=${secret:internal-token}\nGITEA__database__DB_TYPE=postgres\nGITEA__database__HOST=${bound:postgres-database:at}:${bound:postgres-database:port}\nGITEA__database__NAME=gitea\nGITEA__database__USER=${bound:postgres-database:as}\nGITEA__database__PASSWD=${secret:postgres-database}\n" + }, + { + "id": "data", + "type": "directory", + "path": "/services/gitea/gitea", + "mode": "0700" + }, + { + "id": "server", + "type": "container", + "name": "gitea", + "image": "gitea/gitea@sha256:dfc61e347c8b582df918f4556401bf2cecdfbdb56c5282ae9488dd76fca3e41c", + "env": { + "DB_TYPE": "postgres", + "USER_UID": "1000", + "USER_GID": "1000" + }, + "env-file": [ + "/var/lib/gitea/server.env" + ], + "ports": [ + "3000:3000", + "2222:22" + ], + "volumes": [ + "/services/gitea/gitea:/data" + ], + "restart-on": [ + "server-env" + ] + } ] } diff --git a/examples/modules/mailu.json b/examples/modules/mailu.json index a156f2d..46bafca 100644 --- a/examples/modules/mailu.json +++ b/examples/modules/mailu.json @@ -1,97 +1,275 @@ { "module": "mailu", "version": "1", - - "capabilities": ["container-runtime"], - + "capabilities": [ + "container-runtime" + ], "listens": [ - {"port": 25, "protocol": "tcp", "from": "anywhere", "why": "mail from other mail servers"}, - {"port": 465, "protocol": "tcp", "from": "anywhere", "why": "submission over TLS"}, - {"port": 587, "protocol": "tcp", "from": "anywhere", "why": "submission"}, - {"port": 993, "protocol": "tcp", "from": "anywhere", "why": "IMAP over TLS"}, - {"port": 7080, "protocol": "tcp", "from": "mesh", "why": "the web interface, behind a proxy"} + { + "port": 25, + "protocol": "tcp", + "from": "anywhere", + "why": "mail from other mail servers" + }, + { + "port": 465, + "protocol": "tcp", + "from": "anywhere", + "why": "submission over TLS" + }, + { + "port": 587, + "protocol": "tcp", + "from": "anywhere", + "why": "submission" + }, + { + "port": 993, + "protocol": "tcp", + "from": "anywhere", + "why": "IMAP over TLS" + }, + { + "port": 7080, + "protocol": "tcp", + "from": "mesh", + "why": "the web interface, behind a proxy" + } ], - "own-secrets": { "secret-key": "/var/lib/mailu/secret-key.secret", "database": "/var/lib/mailu/database.secret", "admin": "/var/lib/mailu/admin.secret" }, - "resources": [ - {"id": "state", "type": "directory", "path": "/var/lib/mailu", "mode": "0700"}, - - {"id": "secret-env", "type": "file", "path": "/var/lib/mailu/secret.env", "mode": "0600", - "content": "SECRET_KEY=${secret:secret-key}\n"}, - {"id": "database-env", "type": "file", "path": "/var/lib/mailu/database.env", "mode": "0600", - "content": "POSTGRES_USER=mailu\nPOSTGRES_DB=mailu\nPOSTGRES_PASSWORD=${secret:database}\nDB_USER=mailu\nDB_NAME=mailu\nDB_PW=${secret:database}\n"}, - {"id": "admin-env", "type": "file", "path": "/var/lib/mailu/admin.env", "mode": "0600", - "content": "INITIAL_ADMIN_PW=${secret:admin}\n"}, - - {"id": "net", "type": "network", "name": "mailu"}, - - {"id": "resolver", "type": "container", "name": "mailu-resolver", - "image": "ghcr.io/mailu/unbound@sha256:142aaad82ad1b0d5b59a5f1303778dba61a3e0a540f5d969c48862bcc99f6f5d", - "network": "mailu", - "env-file": ["/var/lib/mailu/secret.env"]}, - - {"id": "redis", "type": "container", "name": "mailu-redis", - "image": "redis@sha256:1db42ccef14898aa29bae778452d567534b59c107129cbc1163fb552de184d3c", - "network": "mailu", - "volumes": ["/services/mailu/data/redis:/data"]}, - - {"id": "admindb", "type": "container", "name": "mailu-admindb", - "image": "postgres@sha256:7456ef82e5f5bc43d997f4781bbd7c0d6389bff397564649a356e206ba473aee", - "network": "mailu", - "env": {"PGDATA": "/var/lib/postgresql/data/pgdata"}, - "env-file": ["/var/lib/mailu/database.env"], - "volumes": ["/services/mailu/data/data/psql_admindb/pgdata:/var/lib/postgresql/data/pgdata"]}, - - {"id": "admin", "type": "container", "name": "mailu-admin", - "image": "ghcr.io/mailu/admin@sha256:dcac20e9cbdad560faef9653b1b5ac0d9266f4098dc00f0e7f0d35f4e70ed8f1", - "network": "mailu", - "env-file": ["/var/lib/mailu/secret.env", "/var/lib/mailu/database.env", "/var/lib/mailu/admin.env"], - "volumes": [ - "/services/mailu/data/data:/data", - "/services/mailu/data/dkim:/dkim" - ]}, - - {"id": "imap", "type": "container", "name": "mailu-imap", - "image": "ghcr.io/mailu/dovecot@sha256:46d18ba51032be8ebd6841aa49c1ef8762c729038c5fd86a081b5b884d478af9", - "network": "mailu", - "env-file": ["/var/lib/mailu/secret.env"], - "volumes": [ - "/services/mailu/data/mail:/mail", - "/services/mailu/data/overrides/dovecot:/overrides:ro" - ]}, - - {"id": "smtp", "type": "container", "name": "mailu-smtp", - "image": "ghcr.io/mailu/postfix@sha256:bbf882880f68849511710b35237a933f3fe80c4b28bf48ff20205dbd1f1433d7", - "network": "mailu", - "env-file": ["/var/lib/mailu/secret.env"], - "volumes": ["/services/mailu/data/mailqueue:/queue"]}, - - {"id": "antispam", "type": "container", "name": "mailu-antispam", - "image": "ghcr.io/mailu/rspamd@sha256:e87ab93dd252cc69499caa5317dd10d445fd4291a7ecf6bca09793c7d475a0c8", - "network": "mailu", - "env-file": ["/var/lib/mailu/secret.env"], - "volumes": ["/services/mailu/data/filter:/var/lib/rspamd"]}, - - {"id": "webmail", "type": "container", "name": "mailu-webmail", - "image": "ghcr.io/mailu/webmail@sha256:076b720fc766e58a97321cdb700e887c2008d6d323685fe59f323088333059dc", - "network": "mailu", - "env-file": ["/var/lib/mailu/secret.env"], - "volumes": ["/services/mailu/data/webmail:/data"]}, - - {"id": "front", "type": "container", "name": "mailu-front", - "image": "ghcr.io/mailu/nginx@sha256:09f28ab6d36367fcacc7994f7021f132ac845bdc05f04bf80906102d11aaa057", - "network": "mailu", - "env-file": ["/var/lib/mailu/secret.env"], - "ports": ["25:25", "465:465", "587:587", "993:993", "7080:80"], - "volumes": [ - "/services/mailu/data/certs:/certs", - "/services/mailu/data/overrides/nginx:/overrides:ro" - ], - "restart-on": ["imap", "smtp", "admin"]} + { + "id": "state", + "type": "directory", + "path": "/var/lib/mailu", + "mode": "0700" + }, + { + "id": "secret-env", + "type": "file", + "path": "/var/lib/mailu/secret.env", + "mode": "0600", + "content": "SECRET_KEY=${secret:secret-key}\n" + }, + { + "id": "database-env", + "type": "file", + "path": "/var/lib/mailu/database.env", + "mode": "0600", + "content": "POSTGRES_USER=mailu\nPOSTGRES_DB=mailu\nPOSTGRES_PASSWORD=${secret:database}\nDB_USER=mailu\nDB_NAME=mailu\nDB_PW=${secret:database}\n" + }, + { + "id": "admin-env", + "type": "file", + "path": "/var/lib/mailu/admin.env", + "mode": "0600", + "content": "INITIAL_ADMIN_PW=${secret:admin}\n" + }, + { + "id": "data-certs", + "type": "directory", + "path": "/services/mailu/data/certs", + "mode": "0700" + }, + { + "id": "data-data", + "type": "directory", + "path": "/services/mailu/data/data", + "mode": "0700" + }, + { + "id": "data-dkim", + "type": "directory", + "path": "/services/mailu/data/dkim", + "mode": "0700" + }, + { + "id": "data-filter", + "type": "directory", + "path": "/services/mailu/data/filter", + "mode": "0700" + }, + { + "id": "data-mail", + "type": "directory", + "path": "/services/mailu/data/mail", + "mode": "0700" + }, + { + "id": "data-mailqueue", + "type": "directory", + "path": "/services/mailu/data/mailqueue", + "mode": "0700" + }, + { + "id": "data-redis", + "type": "directory", + "path": "/services/mailu/data/redis", + "mode": "0700" + }, + { + "id": "data-webmail", + "type": "directory", + "path": "/services/mailu/data/webmail", + "mode": "0700" + }, + { + "id": "data-dovecot", + "type": "directory", + "path": "/services/mailu/data/overrides/dovecot", + "mode": "0700" + }, + { + "id": "data-nginx", + "type": "directory", + "path": "/services/mailu/data/overrides/nginx", + "mode": "0700" + }, + { + "id": "data-pgdata", + "type": "directory", + "path": "/services/mailu/data/data/psql_admindb/pgdata", + "mode": "0700" + }, + { + "id": "net", + "type": "network", + "name": "mailu" + }, + { + "id": "resolver", + "type": "container", + "name": "mailu-resolver", + "image": "ghcr.io/mailu/unbound@sha256:142aaad82ad1b0d5b59a5f1303778dba61a3e0a540f5d969c48862bcc99f6f5d", + "network": "mailu", + "env-file": [ + "/var/lib/mailu/secret.env" + ] + }, + { + "id": "redis", + "type": "container", + "name": "mailu-redis", + "image": "redis@sha256:1db42ccef14898aa29bae778452d567534b59c107129cbc1163fb552de184d3c", + "network": "mailu", + "volumes": [ + "/services/mailu/data/redis:/data" + ] + }, + { + "id": "admindb", + "type": "container", + "name": "mailu-admindb", + "image": "postgres@sha256:7456ef82e5f5bc43d997f4781bbd7c0d6389bff397564649a356e206ba473aee", + "network": "mailu", + "env": { + "PGDATA": "/var/lib/postgresql/data/pgdata" + }, + "env-file": [ + "/var/lib/mailu/database.env" + ], + "volumes": [ + "/services/mailu/data/data/psql_admindb/pgdata:/var/lib/postgresql/data/pgdata" + ] + }, + { + "id": "admin", + "type": "container", + "name": "mailu-admin", + "image": "ghcr.io/mailu/admin@sha256:dcac20e9cbdad560faef9653b1b5ac0d9266f4098dc00f0e7f0d35f4e70ed8f1", + "network": "mailu", + "env-file": [ + "/var/lib/mailu/secret.env", + "/var/lib/mailu/database.env", + "/var/lib/mailu/admin.env" + ], + "volumes": [ + "/services/mailu/data/data:/data", + "/services/mailu/data/dkim:/dkim" + ] + }, + { + "id": "imap", + "type": "container", + "name": "mailu-imap", + "image": "ghcr.io/mailu/dovecot@sha256:46d18ba51032be8ebd6841aa49c1ef8762c729038c5fd86a081b5b884d478af9", + "network": "mailu", + "env-file": [ + "/var/lib/mailu/secret.env" + ], + "volumes": [ + "/services/mailu/data/mail:/mail", + "/services/mailu/data/overrides/dovecot:/overrides:ro" + ] + }, + { + "id": "smtp", + "type": "container", + "name": "mailu-smtp", + "image": "ghcr.io/mailu/postfix@sha256:bbf882880f68849511710b35237a933f3fe80c4b28bf48ff20205dbd1f1433d7", + "network": "mailu", + "env-file": [ + "/var/lib/mailu/secret.env" + ], + "volumes": [ + "/services/mailu/data/mailqueue:/queue" + ] + }, + { + "id": "antispam", + "type": "container", + "name": "mailu-antispam", + "image": "ghcr.io/mailu/rspamd@sha256:e87ab93dd252cc69499caa5317dd10d445fd4291a7ecf6bca09793c7d475a0c8", + "network": "mailu", + "env-file": [ + "/var/lib/mailu/secret.env" + ], + "volumes": [ + "/services/mailu/data/filter:/var/lib/rspamd" + ] + }, + { + "id": "webmail", + "type": "container", + "name": "mailu-webmail", + "image": "ghcr.io/mailu/webmail@sha256:076b720fc766e58a97321cdb700e887c2008d6d323685fe59f323088333059dc", + "network": "mailu", + "env-file": [ + "/var/lib/mailu/secret.env" + ], + "volumes": [ + "/services/mailu/data/webmail:/data" + ] + }, + { + "id": "front", + "type": "container", + "name": "mailu-front", + "image": "ghcr.io/mailu/nginx@sha256:09f28ab6d36367fcacc7994f7021f132ac845bdc05f04bf80906102d11aaa057", + "network": "mailu", + "env-file": [ + "/var/lib/mailu/secret.env" + ], + "ports": [ + "25:25", + "465:465", + "587:587", + "993:993", + "7080:80" + ], + "volumes": [ + "/services/mailu/data/certs:/certs", + "/services/mailu/data/overrides/nginx:/overrides:ro" + ], + "restart-on": [ + "imap", + "smtp", + "admin" + ] + } ] } diff --git a/examples/modules/minio.json b/examples/modules/minio.json index 6b73c12..46aea2d 100644 --- a/examples/modules/minio.json +++ b/examples/modules/minio.json @@ -1,54 +1,115 @@ { "module": "minio", "version": "1", - - "provides": [{"name": "s3-bucket", "scope": "mesh"}], - "capabilities": ["container-runtime"], - + "provides": [ + { + "name": "s3-bucket", + "scope": "mesh" + } + ], + "capabilities": [ + "container-runtime" + ], "listens": [ - {"port": 9000, "protocol": "tcp", "from": "mesh", "why": "the S3 endpoint"} + { + "port": 9000, + "protocol": "tcp", + "from": "mesh", + "why": "the S3 endpoint" + } ], - "serves": { - "s3-bucket": {"port": 9000, "scheme": "http", "region": "us-east-1"} + "s3-bucket": { + "port": 9000, + "scheme": "http", + "region": "us-east-1" + } + }, + "receives": { + "s3-bucket": "/var/lib/minio/grants/mesh.json" + }, + "grants": { + "s3-bucket": "/var/lib/minio/grants" + }, + "own-secrets": { + "root": "/var/lib/minio/root.secret" }, - - "receives": {"s3-bucket": "/var/lib/minio/grants/mesh.json"}, - "grants": {"s3-bucket": "/var/lib/minio/grants"}, - - "own-secrets": {"root": "/var/lib/minio/root.secret"}, - "resources": [ - {"id": "state", "type": "directory", "path": "/var/lib/minio", "mode": "0700"}, - {"id": "grants", "type": "directory", "path": "/var/lib/minio/grants", "mode": "0700"}, - - {"id": "root-env", "type": "file", "path": "/var/lib/minio/root.env", "mode": "0600", - "content": "MINIO_ROOT_USER=meshroot\nMINIO_ROOT_PASSWORD=${secret:root}\n"}, - - {"id": "net", "type": "network", "name": "minio"}, - - {"id": "server", "type": "container", "name": "minio", - "image": "minio/minio@sha256:aefec8a86702aff0b0dcfdd9284bd7ab7c5631cbf9be63275799e6edcb30dfa2", - "network": "minio", - "args": ["server", "/data", "--console-address", ":9001"], - "env-file": ["/var/lib/minio/root.env"], - "ports": ["9000:9000"], - "volumes": ["/services/minio/data/data1-1:/data"], - "restart-on": ["root-env"]}, - - {"id": "provisioner", "type": "container", "name": "mesh-provision-objectstore", - "image": "mesh-provision-objectstore@sha256:0000000000000000000000000000000000000000000000000000000000000000", - "network": "minio", - "env": { - "GRANTS": "/var/lib/minio/grants", - "MESH_OBJECTSTORE_URL": "http://minio:9000", - "MESH_OBJECTSTORE_ROOT_USER": "meshroot", - "MESH_OBJECTSTORE_ROOT_PASSWORD_FILE": "/run/secrets/root" - }, - "volumes": [ - "/var/lib/minio/grants:/var/lib/minio/grants:ro", - "/var/lib/minio/root.secret:/run/secrets/root:ro" - ], - "restart-on": ["grants", "root-env"]} + { + "id": "state", + "type": "directory", + "path": "/var/lib/minio", + "mode": "0700" + }, + { + "id": "grants", + "type": "directory", + "path": "/var/lib/minio/grants", + "mode": "0700" + }, + { + "id": "root-env", + "type": "file", + "path": "/var/lib/minio/root.env", + "mode": "0600", + "content": "MINIO_ROOT_USER=meshroot\nMINIO_ROOT_PASSWORD=${secret:root}\n" + }, + { + "id": "data", + "type": "directory", + "path": "/services/minio/data/data1-1", + "mode": "0700" + }, + { + "id": "net", + "type": "network", + "name": "minio" + }, + { + "id": "server", + "type": "container", + "name": "minio", + "image": "minio/minio@sha256:aefec8a86702aff0b0dcfdd9284bd7ab7c5631cbf9be63275799e6edcb30dfa2", + "network": "minio", + "args": [ + "server", + "/data", + "--console-address", + ":9001" + ], + "env-file": [ + "/var/lib/minio/root.env" + ], + "ports": [ + "9000:9000" + ], + "volumes": [ + "/services/minio/data/data1-1:/data" + ], + "restart-on": [ + "root-env" + ] + }, + { + "id": "provisioner", + "type": "container", + "name": "mesh-provision-objectstore", + "image": "mesh-provision-objectstore@sha256:0000000000000000000000000000000000000000000000000000000000000000", + "network": "minio", + "env": { + "GRANTS": "/var/lib/minio/grants", + "MESH_OBJECTSTORE_URL": "http://minio:9000", + "MESH_OBJECTSTORE_ROOT_USER": "meshroot", + "MESH_OBJECTSTORE_ROOT_PASSWORD_FILE": "/run/secrets/root" + }, + "volumes": [ + "/var/lib/minio/grants:/var/lib/minio/grants:ro", + "/var/lib/minio/root.secret:/run/secrets/root:ro" + ], + "restart-on": [ + "grants", + "root-env" + ] + } ] } diff --git a/examples/modules/modules_test.go b/examples/modules/modules_test.go index 4d31e5d..abb3f21 100644 --- a/examples/modules/modules_test.go +++ b/examples/modules/modules_test.go @@ -505,3 +505,67 @@ func TestEveryImageTheExamplesNameIsOneThisRepositoryBuilds(t *testing.T) { t.Fatal("no example names an image this repository builds, so this proves nothing") } } + +// Every host path a container mounts is a directory the module declared. +// +// **The mesh owns a directory or it does not** (novox/hq 04-ISSUES/026). A bind mount whose source +// does not exist is created by the container runtime as root, with a mode nobody chose — so +// `owner` and `mode` go unapplied on exactly the directories that hold the data. +// +// Worse, the rule that a directory is *kept* rather than removed when it holds something the mesh +// did not put there (ADR 0030) is written in terms of declared directories. An undeclared one is +// not covered by it. So the single rule guarding against data loss reached the configuration and +// not the data. +// +// These manifests were written by carrying compose files across, and a container shape that can +// express a compose file gets filled in like one. This is the check that says so. +func TestEveryMountedPathIsADirectoryTheModuleDeclared(t *testing.T) { + found, _ := filepath.Glob("*.json") + var checked int + for _, name := range found { + m := read(t, name) + declared := map[string]bool{} + for _, r := range m.Resources { + if fmt.Sprint(r["type"]) == "directory" { + declared[fmt.Sprint(r["path"])] = true + } + } + for _, r := range m.Resources { + for _, v := range stringsOfTest(r["volumes"]) { + host, _, _ := strings.Cut(v, ":") + if !strings.HasPrefix(host, "/") { + continue // a named volume, which the runtime owns and the mesh does not + } + checked++ + var covered bool + for d := range declared { + if host == d || strings.HasPrefix(host, strings.TrimRight(d, "/")+"/") { + covered = true + } + } + if !covered { + t.Errorf( + "%s: %v mounts %s and no resource declares it. The runtime will create it "+ + "as root, and the rule that keeps a directory holding data does not "+ + "reach a directory the mesh never declared", + name, r["id"], host) + } + } + } + } + if checked == 0 { + t.Fatal("no example mounts a host path, so this test proves nothing") + } +} + +func stringsOfTest(v any) []string { + list, ok := v.([]any) + if !ok { + return nil + } + out := make([]string, 0, len(list)) + for _, item := range list { + out = append(out, fmt.Sprint(item)) + } + return out +} diff --git a/examples/modules/postgres.json b/examples/modules/postgres.json index 2422811..e240757 100644 --- a/examples/modules/postgres.json +++ b/examples/modules/postgres.json @@ -1,55 +1,110 @@ { "module": "postgres", "version": "1", - - "provides": [{"name": "postgres-database", "scope": "mesh"}], - "capabilities": ["container-runtime"], - + "provides": [ + { + "name": "postgres-database", + "scope": "mesh" + } + ], + "capabilities": [ + "container-runtime" + ], "listens": [ - {"port": 5432, "protocol": "tcp", "from": "mesh", - "why": "modules on any machine that were granted a database"} + { + "port": 5432, + "protocol": "tcp", + "from": "mesh", + "why": "modules on any machine that were granted a database" + } ], - "serves": { - "postgres-database": {"port": 5432} + "postgres-database": { + "port": 5432 + } + }, + "receives": { + "postgres-database": "/var/lib/postgres/grants/mesh.json" + }, + "grants": { + "postgres-database": "/var/lib/postgres/grants" + }, + "own-secrets": { + "superuser": "/var/lib/postgres/superuser.secret" }, - - "receives": {"postgres-database": "/var/lib/postgres/grants/mesh.json"}, - "grants": {"postgres-database": "/var/lib/postgres/grants"}, - - "own-secrets": {"superuser": "/var/lib/postgres/superuser.secret"}, - "resources": [ - {"id": "state", "type": "directory", "path": "/var/lib/postgres", "mode": "0700"}, - {"id": "grants", "type": "directory", "path": "/var/lib/postgres/grants", "mode": "0700"}, - - {"id": "superuser-env", "type": "file", "path": "/var/lib/postgres/superuser.env", - "mode": "0600", - "content": "POSTGRES_PASSWORD=${secret:superuser}\n"}, - - {"id": "net", "type": "network", "name": "postgres"}, - - {"id": "server", "type": "container", "name": "postgres", - "image": "postgres@sha256:7456ef82e5f5bc43d997f4781bbd7c0d6389bff397564649a356e206ba473aee", - "network": "postgres", - "env": {"POSTGRES_USER": "postgres", "POSTGRES_DB": "postgres"}, - "env-file": ["/var/lib/postgres/superuser.env"], - "ports": ["5432:5432"], - "volumes": ["/services/postgres/db-data:/var/lib/postgresql/data"], - "restart-on": ["superuser-env"]}, - - {"id": "provisioner", "type": "container", "name": "mesh-provision-postgres", - "image": "mesh-provision-postgres@sha256:0000000000000000000000000000000000000000000000000000000000000000", - "network": "postgres", - "env": { - "GRANTS": "/var/lib/postgres/grants", - "MESH_PROVISION_POSTGRES": "postgres://postgres@postgres:5432/postgres?sslmode=disable", - "MESH_PROVISION_PASSWORD_FILE": "/run/secrets/superuser" - }, - "volumes": [ - "/var/lib/postgres/grants:/var/lib/postgres/grants:ro", - "/var/lib/postgres/superuser.secret:/run/secrets/superuser:ro" - ], - "restart-on": ["grants", "superuser-env"]} + { + "id": "state", + "type": "directory", + "path": "/var/lib/postgres", + "mode": "0700" + }, + { + "id": "grants", + "type": "directory", + "path": "/var/lib/postgres/grants", + "mode": "0700" + }, + { + "id": "superuser-env", + "type": "file", + "path": "/var/lib/postgres/superuser.env", + "mode": "0600", + "content": "POSTGRES_PASSWORD=${secret:superuser}\n" + }, + { + "id": "data", + "type": "directory", + "path": "/services/postgres/db-data", + "mode": "0700" + }, + { + "id": "net", + "type": "network", + "name": "postgres" + }, + { + "id": "server", + "type": "container", + "name": "postgres", + "image": "postgres@sha256:7456ef82e5f5bc43d997f4781bbd7c0d6389bff397564649a356e206ba473aee", + "network": "postgres", + "env": { + "POSTGRES_USER": "postgres", + "POSTGRES_DB": "postgres" + }, + "env-file": [ + "/var/lib/postgres/superuser.env" + ], + "ports": [ + "5432:5432" + ], + "volumes": [ + "/services/postgres/db-data:/var/lib/postgresql/data" + ], + "restart-on": [ + "superuser-env" + ] + }, + { + "id": "provisioner", + "type": "container", + "name": "mesh-provision-postgres", + "image": "mesh-provision-postgres@sha256:0000000000000000000000000000000000000000000000000000000000000000", + "network": "postgres", + "env": { + "GRANTS": "/var/lib/postgres/grants", + "MESH_PROVISION_POSTGRES": "postgres://postgres@postgres:5432/postgres?sslmode=disable", + "MESH_PROVISION_PASSWORD_FILE": "/run/secrets/superuser" + }, + "volumes": [ + "/var/lib/postgres/grants:/var/lib/postgres/grants:ro", + "/var/lib/postgres/superuser.secret:/run/secrets/superuser:ro" + ], + "restart-on": [ + "grants", + "superuser-env" + ] + } ] }