// 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) }, }, } }