Merge pull request 'Rename hosts to hostname, which also writes /etc/hostname (hq ADR 0223 part 3)' (#78) from hostname-module into main
This commit was merged in pull request #78.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
# hostname
|
||||
|
||||
The machine's names (novox/hq ADR 0199, ADR 0223): it holds the node seat `node-hostname` and owns
|
||||
the two files that say what a machine is called — `/etc/hostname` and the machine's own lines in
|
||||
`/etc/hosts`. It was `hosts`, holding `node-hosts-file`; the seat was renamed, and the old name
|
||||
resolves to it as an alias.
|
||||
|
||||
## What it writes
|
||||
|
||||
- **`/etc/hostname`, whole**: the module's `hostname` setting, and nothing else. There is no
|
||||
default. A machine's name is the operator's: the mesh's name for a machine and the name it calls
|
||||
itself need not be the same, and writing the mesh's name silently would rename a machine. Set it
|
||||
per machine — `settings set hostname '{"hostname": "<name>"}' --node <node>` — before the module
|
||||
is assigned there; without it the module is left out of that machine's declaration, naming the
|
||||
key. A mesh-wide `{"hostname": "${machine:name}"}` makes every machine call itself by its mesh
|
||||
name, and a machine's own setting still overrides it.
|
||||
- **The machine's own lines in `/etc/hosts`**, as the mesh's marked region at the start of the file:
|
||||
`localhost` and `127.0.1.1` with the machine's mesh name. Every other line is the operator's, kept
|
||||
byte for byte and given back when the module goes.
|
||||
|
||||
## When a new name takes effect
|
||||
|
||||
At the machine's **next boot**. The kernel's name is set from `/etc/hostname` when the machine
|
||||
starts; writing the file changes what `hostnamectl` reports as the static name and nothing that is
|
||||
running. The module declares nothing that would set it live: a graphical session's X authority is
|
||||
keyed by the name the session started under, so changing it underneath a running session refuses
|
||||
every new window until the person logs in again. Reboot when that is acceptable, or run
|
||||
`hostnamectl hostname <name>` by hand.
|
||||
|
||||
## Its verbs
|
||||
|
||||
`<node>/node-hostname.entries`, `.add` and `.remove`: the hosts file's lines, each marked whose it
|
||||
is; add one address and its names to the operator's lines; remove a name or an address from them.
|
||||
They change the machine's file and nothing else. `/etc/hostname` has no verb — it is the setting.
|
||||
+2
-2
@@ -72,7 +72,7 @@ func execRunner(ctx context.Context, name string, args ...string) (string, error
|
||||
type Line struct {
|
||||
// Text is the line exactly as it is in the file.
|
||||
Text string `json:"text"`
|
||||
// Owner is whose it is: the block's id (`mesh hosts.own`, or another tool's) or "operator".
|
||||
// Owner is whose it is: the block's id (`mesh hostname.own`, or another tool's) or "operator".
|
||||
Owner string `json:"owner"`
|
||||
// Address and Names are an entry's; absent for a comment or a blank line.
|
||||
Address string `json:"address,omitempty"`
|
||||
@@ -319,7 +319,7 @@ func (h HostsFile) write(ctx context.Context, content string) error {
|
||||
if err := os.WriteFile(staged, []byte(content), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
beside := filepath.Join(filepath.Dir(h.Path), "."+filepath.Base(h.Path)+".hosts-tools")
|
||||
beside := filepath.Join(filepath.Dir(h.Path), "."+filepath.Base(h.Path)+".hostname-tools")
|
||||
if _, err := h.Run(ctx, "install", "-m", "0644", staged, beside); err != nil {
|
||||
return err
|
||||
}
|
||||
+33
-6
@@ -17,10 +17,10 @@ import (
|
||||
const file = "# Static table lookup for hostnames.\n" +
|
||||
"127.0.0.1\tlocaldev.example.com\n" +
|
||||
"127.0.0.1 a.example.com b.example.com\n" +
|
||||
"# BEGIN mesh hosts.own\n" +
|
||||
"# BEGIN mesh hostname.own\n" +
|
||||
"127.0.0.1\tlocalhost\n" +
|
||||
"::1\tlocalhost\n" +
|
||||
"# END mesh hosts.own\n" +
|
||||
"# END mesh hostname.own\n" +
|
||||
"# BEGIN other-tool\n" +
|
||||
"192.0.2.7\tproject.test\n" +
|
||||
"# END other-tool\n"
|
||||
@@ -47,7 +47,7 @@ func TestEveryLineSaysWhoseItIs(t *testing.T) {
|
||||
if lines[0].Address != "" || lines[0].Owner != Operator {
|
||||
t.Errorf("a comment is the operator's and no entry: %+v", lines[0])
|
||||
}
|
||||
if lines[3].Owner != "mesh hosts.own" || lines[4].Owner != "mesh hosts.own" || lines[6].Owner != "mesh hosts.own" {
|
||||
if lines[3].Owner != "mesh hostname.own" || lines[4].Owner != "mesh hostname.own" || lines[6].Owner != "mesh hostname.own" {
|
||||
t.Errorf("the mesh's block, its markers included: %+v", lines[3:7])
|
||||
}
|
||||
if lines[8].Owner != "other-tool" || !reflect.DeepEqual(lines[8].Names, []string{"project.test"}) {
|
||||
@@ -146,7 +146,7 @@ func TestRemoveTakesOneNameOrOneAddressAndBlocksStayByteForByte(t *testing.T) {
|
||||
if err != nil || n != 2 {
|
||||
t.Fatalf("%d %v", n, err)
|
||||
}
|
||||
if !strings.Contains(all, "# BEGIN mesh hosts.own\n127.0.0.1\tlocalhost\n") {
|
||||
if !strings.Contains(all, "# BEGIN mesh hostname.own\n127.0.0.1\tlocalhost\n") {
|
||||
t.Errorf("the mesh's own localhost is not the operator's to remove: %q", all)
|
||||
}
|
||||
if !reflect.DeepEqual(blocks(all), blocks(file)) {
|
||||
@@ -158,7 +158,7 @@ func TestRemoveRefusesANameOnlyABlockWritesNamingWhose(t *testing.T) {
|
||||
if _, _, err := WithRemoved(file, "project.test"); err == nil || !strings.Contains(err.Error(), "written by other-tool") {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
if _, _, err := WithRemoved(file, "::1"); err == nil || !strings.Contains(err.Error(), "written by mesh hosts.own") {
|
||||
if _, _, err := WithRemoved(file, "::1"); err == nil || !strings.Contains(err.Error(), "written by mesh hostname.own") {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
if _, n, err := WithRemoved(file, "nowhere.test"); err != nil || n != 0 {
|
||||
@@ -200,7 +200,7 @@ func TestTheVerbsWriteTheFileWholeBesideItAndRenameItOver(t *testing.T) {
|
||||
if err != nil || !added.Added || added.Line != "192.0.2.9\tlab.test" {
|
||||
t.Fatalf("%+v %v", added, err)
|
||||
}
|
||||
beside := filepath.Join(dir, ".hosts.hosts-tools")
|
||||
beside := filepath.Join(dir, ".hosts.hostname-tools")
|
||||
if len(calls) != 2 || calls[0][0] != "install" || calls[0][len(calls[0])-1] != beside ||
|
||||
!reflect.DeepEqual(calls[1], []string{"mv", "-f", beside, path}) {
|
||||
t.Errorf("%v", calls)
|
||||
@@ -284,3 +284,30 @@ func TestNamesAreSplitOnSpacesAndCommasOrTakenAsAList(t *testing.T) {
|
||||
t.Errorf("%v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// /etc/hostname is the module's whole file (novox/hq ADR 0223), and what it says is the operator's
|
||||
// `hostname` setting — never the mesh's name for the machine written silently: on the mesh this was
|
||||
// built for, three of four machines call themselves something else, and renaming a machine is the
|
||||
// operator's to decide.
|
||||
func TestTheMachinesNameIsTheOperatorsSetting(t *testing.T) {
|
||||
raw, err := os.ReadFile("../../module.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var m struct {
|
||||
Resources []map[string]any `json:"resources"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, r := range m.Resources {
|
||||
if r["path"] != "/etc/hostname" {
|
||||
continue
|
||||
}
|
||||
if r["id"] != "name" || r["into"] != nil || r["content"] != "${setting:hostname}\n" {
|
||||
t.Errorf("/etc/hostname is not written whole from the hostname setting: %v", r)
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Error("the module does not write /etc/hostname")
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
// hosts-tools (novox/hq ADR 0199): the hosts file's tools. One binary, launched by the machine's tool
|
||||
// runtime and speaking MCP to it over stdio through the Go SDK (ADR 0193, ADR 0198): the
|
||||
// node-hosts-file seat's three verbs — the file's lines with whose each is, add an operator's line,
|
||||
// remove one. They change the machine's file and nothing else; the controller holds none of it.
|
||||
// hostname-tools (novox/hq ADR 0199, ADR 0223): the tools of the machine's names. One binary,
|
||||
// launched by the machine's tool runtime and speaking MCP to it over stdio through the Go SDK (ADR
|
||||
// 0193, ADR 0198): the node-hostname seat's three verbs — the hosts file's lines with whose each
|
||||
// is, add an operator's line, remove one. They change the machine's file and nothing else; the
|
||||
// controller holds none of it. /etc/hostname has no verb: it is the module's resource, set by the
|
||||
// module's `hostname` setting.
|
||||
//
|
||||
// stdout is the MCP channel; everything this module says, it says on stderr.
|
||||
package main
|
||||
@@ -17,11 +19,11 @@ import (
|
||||
)
|
||||
|
||||
// Seat is the role this module holds.
|
||||
const Seat = "node-hosts-file"
|
||||
const Seat = "node-hostname"
|
||||
|
||||
func main() {
|
||||
if err := stdio.Serve("", tools(HostsFile{Path: HostsPath, Run: execRunner})); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[hosts] %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, "[hostname] %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -61,7 +63,7 @@ func namesArg(a map[string]any) []string {
|
||||
}
|
||||
|
||||
// verb is one of the seat's verbs: listed as `<seat>.<verb>`, so the runtime serves it on the seat's
|
||||
// subject, as <node>/node-hosts-file.<verb>.
|
||||
// subject, as <node>/node-hostname.<verb>.
|
||||
func verb(name, description string, input map[string]any, run func(a map[string]any) (any, error)) stdio.Tool {
|
||||
return stdio.Tool{Name: Seat + "." + name, Description: description, Input: input, Run: run}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module hosts
|
||||
module hostname
|
||||
|
||||
go 1.25.0
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"module": "hostname",
|
||||
"version": "1",
|
||||
"claims": [
|
||||
{
|
||||
"name": "node-hostname",
|
||||
"scope": "node",
|
||||
"serves": [
|
||||
"entries",
|
||||
"add",
|
||||
"remove"
|
||||
]
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "own",
|
||||
"type": "file",
|
||||
"path": "/etc/hosts",
|
||||
"mode": "0644",
|
||||
"into": "block",
|
||||
"at": "start",
|
||||
"content": "# The machine's own names (module hostname, novox/hq ADR 0199, ADR 0223). Every line outside this\n# block is the operator's: kept across every push, changed through the node-hostname verbs add and\n# remove, and given back when this module goes. The mesh's names are not here: the mesh's resolver\n# answers them.\n127.0.0.1\tlocalhost\n::1\tlocalhost\n127.0.1.1\t${machine:name}\n"
|
||||
},
|
||||
{
|
||||
"id": "name",
|
||||
"type": "file",
|
||||
"path": "/etc/hostname",
|
||||
"mode": "0644",
|
||||
"content": "${setting:hostname}\n"
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
"artifacts": [
|
||||
{
|
||||
"name": "tools",
|
||||
"kind": "bundle",
|
||||
"language": "go",
|
||||
"system": "arch",
|
||||
"from": "cmd/hostname-tools",
|
||||
"binary": "hostname-tools",
|
||||
"loads": [
|
||||
"hostname-tools"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"module": "hosts",
|
||||
"version": "1",
|
||||
"claims": [
|
||||
{
|
||||
"name": "node-hosts-file",
|
||||
"scope": "node",
|
||||
"serves": [
|
||||
"entries",
|
||||
"add",
|
||||
"remove"
|
||||
]
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "own",
|
||||
"type": "file",
|
||||
"path": "/etc/hosts",
|
||||
"mode": "0644",
|
||||
"into": "block",
|
||||
"at": "start",
|
||||
"content": "# The machine's own names (module hosts, novox/hq ADR 0199). Every line outside this block is the\n# operator's: kept across every push, changed through the node-hosts-file verbs add and remove, and\n# given back when this module goes. The mesh's names are not here: the mesh's resolver answers them.\n127.0.0.1\tlocalhost\n::1\tlocalhost\n127.0.1.1\t${machine:name}\n"
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
"artifacts": [
|
||||
{
|
||||
"name": "tools",
|
||||
"kind": "bundle",
|
||||
"language": "go",
|
||||
"system": "arch",
|
||||
"from": "cmd/hosts-tools",
|
||||
"binary": "hosts-tools",
|
||||
"loads": [
|
||||
"hosts-tools"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user