Multi-server CLI, more reverse zones, and a fix for new view directories
- CLI: name several servers and groups in the config file; select them with -s/--server NAME|GROUP|all|URL or JARL_SERVER. A command aimed at several servers is dry-run on all of them first and only runs if every one accepts it. Named servers always use their own token. - Reverse zones: /32 and /128 get single-address zones; networks between octet or nibble boundaries (e.g. a /22) are covered by several zones created together, from the CLI and the web UI. - Fix: under jarl.service's RestrictSUIDSGID, creating a view directory failed on chmod with the setgid bit; fall back to the permission bits. - Web UI: the review/live switch is a small header button, and the pending-changes bar only shows when there are pending changes. - TSIG key wording; the keys page points to restricted API tokens. - Docs: several servers (cli.md), a secondary server (operations.md), tokens vs TSIG keys (permissions.md); CHANGELOG. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a7d389be27
commit
785b0006f9
22 files changed
+1375
-185
No files matched your search
@@ -8,6 +8,7 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -30,8 +31,18 @@ type Client struct {
|
||||
Changeset int64
|
||||
DryRun bool
|
||||
Force bool
|
||||
// Preflight lets only requests through that can't change anything:
|
||||
// reads and dry runs. Running a command on several servers uses it to
|
||||
// check the command everywhere before running it anywhere.
|
||||
Preflight bool
|
||||
// Mutated records that a preflight run sent a (dry-run) change.
|
||||
Mutated bool
|
||||
}
|
||||
|
||||
// ErrNoPreflight is returned in Preflight mode for a request that would
|
||||
// change something and has no dry run.
|
||||
var ErrNoPreflight = errors.New("this command can't be checked without running it, so it can't run on several servers at once; run it on one server at a time with --server NAME")
|
||||
|
||||
// Options configure New.
|
||||
type Options struct {
|
||||
Server string
|
||||
@@ -124,6 +135,12 @@ func (e *Error) Check() []CheckMessage {
|
||||
// Do sends a request. body is marshaled as JSON; out (if not nil) receives
|
||||
// the JSON response.
|
||||
func (c *Client) Do(ctx context.Context, method, path string, query url.Values, body, out any) error {
|
||||
if c.Preflight && method != http.MethodGet && path != "/system/check" {
|
||||
if !isDryRun(query, body) {
|
||||
return ErrNoPreflight
|
||||
}
|
||||
c.Mutated = true
|
||||
}
|
||||
u := c.BaseURL + api.Prefix + path
|
||||
if len(query) > 0 {
|
||||
u += "?" + query.Encode()
|
||||
@@ -174,6 +191,26 @@ func (c *Client) Do(ctx context.Context, method, path string, query url.Values,
|
||||
return json.Unmarshal(data, out)
|
||||
}
|
||||
|
||||
// isDryRun reports whether a request asks for a dry run, in its query or
|
||||
// its body.
|
||||
func isDryRun(query url.Values, body any) bool {
|
||||
if query.Get("dry_run") == "1" {
|
||||
return true
|
||||
}
|
||||
switch b := body.(type) {
|
||||
case api.BatchRequest:
|
||||
return b.DryRun
|
||||
case *api.BatchRequest:
|
||||
return b != nil && b.DryRun
|
||||
case map[string]any:
|
||||
v, _ := b["dry_run"].(bool)
|
||||
return v
|
||||
case map[string]bool:
|
||||
return b["dry_run"]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get is Do with GET.
|
||||
func (c *Client) Get(ctx context.Context, path string, query url.Values, out any) error {
|
||||
return c.Do(ctx, http.MethodGet, path, query, nil, out)
|
||||
|
||||
Reference in new issue
Block a user