feat: self-update of master and agents (#15)

This commit is contained in:
2026-10-13 11:55:46 +02:00
parent ec28d1e027
commit fe4aca2883
34 changed files with 1167 additions and 115 deletions
+1
View File
@@ -883,6 +883,7 @@ func agentSnippet(cfg Config, token string) string {
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# - /path/to/stacks:/path/to/stacks # mount each stacks directory at the same path, read-write
# (also this agent's own stack directory, or it cannot update itself)
# - /root/.docker/config.json:/docker-config/config.json:ro
`, cfg.AgentImage, cfg.AgentURL, cfg.MasterKey, token)
}
+5 -2
View File
@@ -303,7 +303,8 @@ func TestOverview_GroupsServicesPerMachineAndSwarm(t *testing.T) {
assert.Contains(t, body, `<span class="stk">slusk</span>`)
assert.Contains(t, body, "jellyfin_jellyfin", "Swarm service names are shown as they are")
assert.Contains(t, body, "sha256:0f42497a69fa…", "digests are shortened")
assert.Contains(t, body, "Managed outside")
assert.Contains(t, body, `<span class="tag f-out">This agent</span>`)
assert.NotContains(t, body, "Managed outside")
assert.Less(t, strings.Index(body, ">Swarm</h2>"), strings.Index(body, ">node-2</h2>"), "a cluster is followed by its managers' machines")
assert.Contains(t, body, `class="sub" data-machine="agent-2"`, "the machine filter lists the manager under the cluster")
}
@@ -513,6 +514,7 @@ func TestAgentSnippet(t *testing.T) {
assert.Contains(t, snippet, "UPDATE_TIMEOUT: 5m")
assert.Contains(t, snippet, " # DOCKER_CONFIG: /docker-config\n")
assert.Contains(t, snippet, " # - /path/to/stacks:/path/to/stacks # mount each stacks directory at the same path, read-write\n")
assert.Contains(t, snippet, "also this agent's own stack directory")
assert.Contains(t, snippet, "AGENT_TOKEN: tok")
}
@@ -762,7 +764,8 @@ func TestOverview_ShowsModeToggleAndAvailableBadge(t *testing.T) {
assert.Contains(t, body, `<input type="hidden" name="mode" value="auto">`, "manual services toggle to auto: "+path)
assert.Contains(t, body, `aria-checked="false" disabled title="`+model.ReasonBuilt+`">`, "blocked manual services get a disabled switch: "+path)
assert.Contains(t, body, `<span class="na">`+model.ReasonBuilt+`</span>`, "and say why: "+path)
assert.Contains(t, body, `<span class="na">n/a</span>`, "services managed outside have no switch: "+path)
assert.Contains(t, body, `action="/services/3/mode"`, "the agent's own service has a switch like any other: "+path)
assert.NotContains(t, body, `<span class="na">n/a</span>`, path)
}
}
+3 -4
View File
@@ -79,11 +79,10 @@
{{if .AvailableRef}}<span class="tag f-up" title="{{.AvailableRef}}">Update available<em>{{.AvailableTag}}</em></span>{{end}}
{{if followsNoTag .Service}}<span class="tag f-notag">Follows no tag</span>{{end}}
{{if .LastPollError}}<span class="tag f-err" title="{{.LastPollError}}">Poll failed</span>{{end}}
{{if .Self}}<span class="tag f-out">Managed outside</span>{{end}}
{{if .Self}}<span class="tag f-out">This agent</span>{{end}}
</div>
<div class="mode">
{{if .Self}}<span class="na">n/a</span>
{{else}}<form method="post" action="/services/{{.ID}}/mode">
<form method="post" action="/services/{{.ID}}/mode">
<input type="hidden" name="csrf_token" value="{{$.CSRF}}">
<input type="hidden" name="return" value="overview">
{{if eq .UpdateMode "auto"}}<input type="hidden" name="mode" value="manual">
@@ -91,7 +90,7 @@
{{else}}<input type="hidden" name="mode" value="auto">
<button type="submit" class="sw" role="switch" aria-checked="false"{{if .AutoBlocked}} disabled title="{{.AutoBlocked}}"{{end}}><i></i>Manual</button>
{{if .AutoBlocked}}<span class="na">{{.AutoBlocked}}</span>{{end}}{{end}}
</form>{{end}}
</form>
</div>
</div>
{{end}}