snapd is not in the official repositories, and ADR 0205's archive does not fit a daemon with setuid helpers, so the module declares nothing until research 027 question 1 (P2) builds it into the mesh's own repository. Not even its units: on the laptop they do not exist, and the module would fail there. Ten Go tools that work wherever snapd is installed and say so where it is not: status (with AppArmor's absence from the kernel named), list, info, updates, disk usage with the disabled revisions' share, services, changes, and install, remove and refresh through sudo -n with --no-wait, answering snapd's change id.
154 lines
5.4 KiB
Go
154 lines
5.4 KiB
Go
// The snapd module's tools (novox/hq research 027/02, 026/05): the snaps on this machine, their
|
|
// revisions and the space they take, the store's pending updates, snapd's changes, and installing,
|
|
// removing and refreshing a snap. A Go bundle the node's runtime launches over stdio (ADR 0188,
|
|
// ADR 0193); it runs as the operator account, and an act goes through `sudo -n`.
|
|
//
|
|
// The module installs nothing: snapd is not in the distribution's official repositories (README).
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
)
|
|
|
|
var providedBy = map[string]string{
|
|
"snap": "snapd is not installed; it is not in the official repositories, and this module does not install it (see its README)",
|
|
"systemctl": "the systemd package",
|
|
}
|
|
|
|
func main() {
|
|
if err := stdio.Serve("", tools()); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
var nameArg = map[string]any{"type": "string", "description": "the snap's name"}
|
|
|
|
func tools() []stdio.Tool {
|
|
return []stdio.Tool{
|
|
{
|
|
Name: "snapd_status",
|
|
Description: "Whether snapd is here and working: its version, its units (socket, service, AppArmor loader), " +
|
|
"whether the kernel runs AppArmor (without it strict snaps are not confined), and whether /snap exists " +
|
|
"for classic snaps. (r)",
|
|
Input: map[string]any{},
|
|
Run: func(map[string]any) (any, error) { return Status() },
|
|
},
|
|
{
|
|
Name: "snapd_list",
|
|
Description: "Every installed snap and revision: version, revision, channel tracked, publisher, notes, and " +
|
|
"whether the revision is disabled (kept for rollback, taking space). (r)",
|
|
Input: map[string]any{},
|
|
Run: func(map[string]any) (any, error) { return List() },
|
|
},
|
|
{
|
|
Name: "snapd_info",
|
|
Description: "One snap as the store and snapd describe it: summary, publisher, licence, commands, tracking, and the channels with their versions. (r)",
|
|
Input: map[string]any{"name": nameArg},
|
|
Run: func(args map[string]any) (any, error) {
|
|
name, err := snapName(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return Info(name)
|
|
},
|
|
},
|
|
{
|
|
Name: "snapd_updates",
|
|
Description: "What a refresh would change: each snap with an update, its new version, revision and size. (r)",
|
|
Input: map[string]any{},
|
|
Run: func(map[string]any) (any, error) { return Updates() },
|
|
},
|
|
{
|
|
Name: "snapd_disk_usage",
|
|
Description: "The space each snap's revisions take in /var/lib/snapd/snaps, which of it is disabled revisions " +
|
|
"kept for rollback, and the total. (r)",
|
|
Input: map[string]any{},
|
|
Run: func(map[string]any) (any, error) { return DiskUsage() },
|
|
},
|
|
{
|
|
Name: "snapd_services",
|
|
Description: "The services installed snaps provide, with whether each starts at boot and runs now. (r)",
|
|
Input: map[string]any{},
|
|
Run: func(map[string]any) (any, error) { return Services() },
|
|
},
|
|
{
|
|
Name: "snapd_changes",
|
|
Description: "snapd's recent changes (installs, refreshes, removals): id, status, when, summary; or, with id, " +
|
|
"one change's tasks. An act answers the change id to follow here. (r)",
|
|
Input: map[string]any{"id": map[string]any{"type": "string", "description": "one change's id, for its tasks"}},
|
|
Run: func(args map[string]any) (any, error) {
|
|
id, err := optText(args, "id", "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return Changes(id)
|
|
},
|
|
},
|
|
{
|
|
Name: "snapd_install",
|
|
Description: "Install a snap from the store, optionally from a channel and in classic confinement. snapd " +
|
|
"carries it out in the background; the answer is the change id to follow with snapd_changes. (a)",
|
|
Input: map[string]any{
|
|
"name": nameArg,
|
|
"channel": map[string]any{"type": "string", "description": "a channel such as latest/stable or 3.x/edge"},
|
|
"classic": map[string]any{"type": "boolean", "description": "classic confinement, for a snap that asks for it"},
|
|
},
|
|
Run: func(args map[string]any) (any, error) {
|
|
name, err := snapName(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
channel, err := optText(args, "channel", "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
classic, err := optFlag(args, "classic", false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return Install(name, channel, classic)
|
|
},
|
|
},
|
|
{
|
|
Name: "snapd_remove",
|
|
Description: "Remove a snap, keeping a snapshot of its data unless purge is set. Answers the change id. (a)",
|
|
Input: map[string]any{
|
|
"name": nameArg,
|
|
"purge": map[string]any{"type": "boolean", "description": "remove its data too, without a snapshot"},
|
|
},
|
|
Run: func(args map[string]any) (any, error) {
|
|
name, err := snapName(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
purge, err := optFlag(args, "purge", false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return Remove(name, purge)
|
|
},
|
|
},
|
|
{
|
|
Name: "snapd_refresh",
|
|
Description: "Refresh one snap, or every snap when no name is given. Answers the change id, or that nothing needed it. (a)",
|
|
Input: map[string]any{"name": map[string]any{"type": "string", "description": "the snap to refresh (default all)"}},
|
|
Run: func(args map[string]any) (any, error) {
|
|
name, err := optText(args, "name", "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if name != "" {
|
|
if err := checkSnapName(name); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return Refresh(name)
|
|
},
|
|
},
|
|
}
|
|
}
|