Documentation
¶
Index ¶
- Constants
- Variables
- func CheckWakeTarget(name, mac string) error
- func FormatValidation(r *ValidationResult) string
- func PermissionProblem(path string, cfg *Config) (os.FileMode, bool)
- func RegisterAlertRulesCheck(check func(node *yaml.Node) []Finding)
- func Resolve(explicit string) string
- func Save(path string, rev Revision, patch Patch) error
- type AlertConfig
- type AlertsPatch
- type BackupConfig
- type Config
- func (c *Config) FindProxmox(name string) *ProxmoxConfig
- func (c *Config) FindServer(name string) *ServerConfig
- func (c *Config) FindWakeTarget(name string) *WakeTarget
- func (c *Config) ResolveBackupDir() string
- func (c *Config) ResolveBackupRetention() backup.RetentionConfig
- func (c *Config) SelectProxmox(name string) (*ProxmoxConfig, error)
- type Finding
- type NotifyPatch
- type Patch
- type ProxmoxConfig
- func (p ProxmoxConfig) APIPort() int
- func (p ProxmoxConfig) ActionTokenFilePath() string
- func (p ProxmoxConfig) ActionTokenValue() (string, error)
- func (p ProxmoxConfig) HasActionCredential() bool
- func (p ProxmoxConfig) ResolveCredential(action bool) (tokenID, token string, err error)
- func (p ProxmoxConfig) TimeoutDuration() time.Duration
- func (p ProxmoxConfig) TokenFilePath() string
- func (p ProxmoxConfig) TokenValue() (string, error)
- type ProxmoxPatch
- type Revision
- type Secret
- type Section
- type ServerConfig
- type ServerPatch
- type Source
- type ValidationResult
- type WakePatch
- type WakeTarget
- type WatchRuntimeConfig
- type WebConfig
Constants ¶
const ( SeverityError = "error" SeverityWarning = "warning" )
Severity ranks a validation finding. Errors mean the config is wrong and something will not work; warnings mean it parses but probably does not do what the user intended.
Variables ¶
var ErrCredentialWouldMove = fmt.Errorf("that change would send a saved credential to a new address")
ErrCredentialWouldMove is returned when a patch points a stored credential at an address it was not given for.
The reason it is worth refusing rather than warning: a saved password is sent to whatever answers at the address in the file, and changing that address is a one-field edit. Anyone who can reach the write endpoints — or who has taken the token from someone who can — could point a server at a machine of theirs and collect the password on the next refresh. Proxmox is the same shape: the token travels in a header to whatever host the endpoint names.
Sending the credential again with the change is the whole fix. Somebody who knows the password can move the server; somebody who only has the config cannot take it with them.
var ErrFlowStyle = errors.New("that section is written on one line, which homebutler cannot edit in place")
ErrFlowStyle is returned for a section written on one line. Editing text at a position cannot change one value inside `alerts: {cpu: 90}` without rewriting the line, and rewriting it would lose whatever else it holds.
var ErrStale = errors.New("the config file changed on disk since it was read")
ErrStale is returned when the file changed after the revision being saved against was taken.
Functions ¶
func CheckWakeTarget ¶ added in v0.34.0
CheckWakeTarget reports why a target cannot be saved, in the words config validate uses for the same mistake.
It is one function rather than a rule written twice because a save and a `config validate` that disagreed about what a MAC looks like would be two answers to one question — and the disagreement would only surface after the packet had gone nowhere. Wake-on-LAN is fire and forget: nothing replies, so a typed address shows up as a machine that simply did not turn on, which is exactly the case a form has to catch before it writes.
func FormatValidation ¶ added in v0.20.0
func FormatValidation(r *ValidationResult) string
FormatValidation renders a validation result for a terminal.
func PermissionProblem ¶ added in v0.28.0
PermissionProblem reports whether a config file is more open than one holding plaintext secrets should be, and the permissions it found.
One decision, three renderings: Load refuses, config validate reports it as an error, and doctor surfaces it before either has to. #47 and #65 were both about the version of this where each caller kept its own copy of the rule.
func RegisterAlertRulesCheck ¶ added in v1.2.0
RegisterAlertRulesCheck is called by the alerts package at init.
func Resolve ¶ added in v0.2.0
Resolve finds the config file path. See ResolveWithSource for the priority.
func Save ¶ added in v0.33.0
Save applies patch to the config file at path.
The file is edited rather than regenerated: it is written by a person, and keeps their comments, their key order, the keys this version of homebutler does not recognise, and their indentation. Only the values the patch names are touched.
#154 calls it as:
rev, _ := config.ReadRevision(path)
err := config.Save(path, rev, config.Patch{Alerts: &config.AlertsPatch{CPU: &v}})
Types ¶
type AlertConfig ¶
type AlertConfig struct {
CPU float64 `yaml:"cpu"`
Memory float64 `yaml:"memory"`
Disk float64 `yaml:"disk"`
// Ignore names containers whose stopping and starting is intended, such
// as one a cron job stops every night. The watcher reports neither their
// going down nor their recovery (#338). Every other container is still
// watched, as before.
Ignore []string `yaml:"ignore,omitempty"`
}
type AlertsPatch ¶ added in v0.33.0
AlertsPatch changes alert thresholds. A nil field is not written.
type BackupConfig ¶ added in v0.24.0
type BackupConfig struct {
Dir string `yaml:"dir,omitempty"`
Retention backup.RetentionConfig `yaml:"retention,omitempty"`
}
BackupConfig groups everything about where backups go and how many are kept.
`backup: dir:` is the spelling docs/backup.md has documented all along, and the schema only ever had the top-level `backup_dir`. A config copied from the documentation parsed without complaint and then wrote to the home directory, so an operator pointing backups at a NAS got neither the destination they asked for nor any indication they had not. Both spellings are read; the nested one wins.
type Config ¶
type Config struct {
Path string `yaml:"-"` // resolved config file path (not serialized)
Servers []ServerConfig `yaml:"servers"`
Proxmox []ProxmoxConfig `yaml:"proxmox,omitempty"`
Wake []WakeTarget `yaml:"wake,omitempty"`
Alerts AlertConfig `yaml:"alerts"`
Notify notify.ProviderConfig `yaml:"notify,omitempty"`
Watch WatchRuntimeConfig `yaml:"watch,omitempty"`
Backup BackupConfig `yaml:"backup,omitempty"`
BackupDir string `yaml:"backup_dir,omitempty"`
Web WebConfig `yaml:"web,omitempty"`
}
func LoadForDiagnosis ¶ added in v0.28.0
LoadForDiagnosis parses a config without refusing it for open permissions.
Only doctor should call this. The command whose job is to explain what is wrong with a machine cannot be the one command that will not start because something is wrong — a user whose config is 0644 currently meets a refusal they read as homebutler being broken, from every command including the one that would have named the problem.
func (*Config) FindProxmox ¶ added in v0.22.1
func (c *Config) FindProxmox(name string) *ProxmoxConfig
FindProxmox returns the Proxmox endpoint config by name, or nil if absent.
func (*Config) FindServer ¶ added in v0.2.0
func (c *Config) FindServer(name string) *ServerConfig
FindServer returns the server config by name, or nil if not found.
func (*Config) FindWakeTarget ¶
func (c *Config) FindWakeTarget(name string) *WakeTarget
func (*Config) ResolveBackupDir ¶ added in v0.9.0
ResolveBackupDir returns the backup directory from config or the default ~/.homebutler/backups/.
func (*Config) ResolveBackupRetention ¶ added in v0.24.0
func (c *Config) ResolveBackupRetention() backup.RetentionConfig
ResolveBackupRetention returns the configured retention policy, which is unlimited unless the operator asked for something narrower.
func (*Config) SelectProxmox ¶ added in v0.22.1
func (c *Config) SelectProxmox(name string) (*ProxmoxConfig, error)
SelectProxmox returns the named endpoint, or the sole configured endpoint.
type Finding ¶ added in v0.20.0
type Finding struct {
Severity string `json:"severity"`
Field string `json:"field,omitempty"`
Message string `json:"message"`
Hint string `json:"hint,omitempty"`
}
Finding is one problem found in the config.
type NotifyPatch ¶ added in v0.33.0
Patch is the set of settings to change.
It is a patch of the fields being changed rather than a whole document, and that is a decision about secrets rather than a convenience. /api/config redacts a password to bullets, so a caller that sent back what it was given would write those bullets into the file as the password. A field nobody set is not written, so a secret nobody is changing cannot be damaged by a save that was about something else. NotifyPatch changes one notification channel.
Values carries the keys that are not credentials; a key that is absent is left as it is. Secrets carries the ones that are, because those have three states rather than two. Remove deletes the whole block.
type Patch ¶ added in v0.33.0
type Patch struct {
Alerts *AlertsPatch
// Notify is keyed by channel name, as internal/notify spells it.
Notify map[string]*NotifyPatch
Wake []WakePatch
Servers []ServerPatch
Proxmox []ProxmoxPatch
}
type ProxmoxConfig ¶ added in v0.22.1
type ProxmoxConfig struct {
Name string `yaml:"name"`
Host string `yaml:"host"`
Port int `yaml:"port,omitempty"`
TokenID string `yaml:"token_id"`
Token string `yaml:"token,omitempty" json:"-" secret:"true"`
TokenFile string `yaml:"token_file,omitempty"`
Fingerprint string `yaml:"fingerprint,omitempty"`
CAFile string `yaml:"ca_file,omitempty"`
Insecure bool `yaml:"insecure,omitempty"`
Timeout string `yaml:"timeout,omitempty"`
// ActionTokenID/ActionToken/ActionTokenFile configure an optional second
// credential used only for guest power actions (start/reboot/shutdown).
// The credential above stays read-only; if no action credential is
// configured, guest actions are unavailable rather than falling back to
// the read credential.
ActionTokenID string `yaml:"action_token_id,omitempty"`
ActionToken string `yaml:"action_token,omitempty" json:"-" secret:"true"`
ActionTokenFile string `yaml:"action_token_file,omitempty"`
// Guests names guests `watch start` expects to stay running on this
// endpoint. A guest not listed here is observational only: watch never
// alerts on it, deliberately stopped or not.
Guests []proxmox.ExpectedGuest `yaml:"guests,omitempty"`
}
ProxmoxConfig describes one Proxmox VE API endpoint. It is separate from ServerConfig because Proxmox uses its HTTP API rather than SSH.
func (ProxmoxConfig) APIPort ¶ added in v0.22.1
func (p ProxmoxConfig) APIPort() int
APIPort returns the configured API port or Proxmox's default HTTPS port.
func (ProxmoxConfig) ActionTokenFilePath ¶ added in v0.27.0
func (p ProxmoxConfig) ActionTokenFilePath() string
ActionTokenFilePath expands a leading ~/ in the configured action token file path.
func (ProxmoxConfig) ActionTokenValue ¶ added in v0.27.0
func (p ProxmoxConfig) ActionTokenValue() (string, error)
ActionTokenValue returns the action API token from the configured file or inline value, mirroring TokenValue's file-wins precedence.
func (ProxmoxConfig) HasActionCredential ¶ added in v0.27.0
func (p ProxmoxConfig) HasActionCredential() bool
HasActionCredential reports whether an action credential is configured for guest power actions. Guest actions are unavailable when this is false; there is no fallback to the read credential.
func (ProxmoxConfig) ResolveCredential ¶ added in v0.27.0
func (p ProxmoxConfig) ResolveCredential(action bool) (tokenID, token string, err error)
ResolveCredential returns the token ID and token value for either the read or action credential, wrapping resolution failures the same way across every caller. Guest actions never fall back to the read credential: when action is true and no action credential is configured, this fails before any token is read.
func (ProxmoxConfig) TimeoutDuration ¶ added in v0.22.1
func (p ProxmoxConfig) TimeoutDuration() time.Duration
TimeoutDuration returns the configured timeout or the default 10 seconds. Validation reports malformed values before a client is constructed.
func (ProxmoxConfig) TokenFilePath ¶ added in v0.22.1
func (p ProxmoxConfig) TokenFilePath() string
TokenFilePath expands a leading ~/ in the configured token file path.
func (ProxmoxConfig) TokenValue ¶ added in v0.22.1
func (p ProxmoxConfig) TokenValue() (string, error)
TokenValue returns the API token from the configured file or inline value. Validation rejects configuring both sources, but the file takes precedence here to keep token_file the preferred source for direct callers.
type ProxmoxPatch ¶ added in v0.35.0
type ProxmoxPatch struct {
Name string
Rename string
Host *string
Port *int
TokenID *string
Token *Secret
ActionTokenID *string
ActionToken *Secret
Remove bool
}
ProxmoxPatch changes one Proxmox endpoint. The read credential and the one that performs guest actions are separate on purpose, and stay separate here.
The *_file paths are absent for the same reason KeyFile is.
type Revision ¶ added in v0.33.0
type Revision struct {
// contains filtered or unexported fields
}
Revision identifies the config file as it was when it was read.
Someone with the dashboard open and an editor in another window is the ordinary case, not an edge one: without this, saving a threshold from the browser silently discards whatever they wrote in vim. A save carries the revision it was made against and is refused if the file has moved on.
func ParseRevision ¶ added in v0.33.0
ParseRevision reads back what String wrote. An empty string is the revision of a file that was not there, which is how creating one is expressed.
func ReadRevision ¶ added in v0.33.0
ReadRevision returns the revision of the file at path. A file that does not exist has a revision too, so that creating one can also be refused if something else created it first.
type Secret ¶ added in v0.33.0
type Secret struct {
// contains filtered or unexported fields
}
Secret is a credential inside a patch, which has three states rather than two: absent, replaced, or cleared.
An empty string cannot mean "clear". A form submitted with the token box left blank sends an empty string, and reading that as "delete the token" destroys a working configuration because someone changed a threshold on the same page. Clearing is its own call.
func ClearSecret ¶ added in v0.33.0
func ClearSecret() *Secret
ClearSecret removes the stored value. Explicit, and never the result of an empty input.
type Section ¶ added in v0.20.0
type Section struct {
Name string `json:"name"`
Present bool `json:"present"`
Summary string `json:"summary"`
}
Section reports what homebutler made of one top-level config key.
This is the half of the output that answers "why is my notify config being ignored?". A key the file never set reads as "not set", and a key that was set but did not match the schema shows up in Findings instead of here — so the two together account for every line of the file.
type ServerConfig ¶
type ServerConfig struct {
Name string `yaml:"name"`
Host string `yaml:"host"`
Local bool `yaml:"local,omitempty"`
User string `yaml:"user,omitempty"`
Port int `yaml:"port,omitempty"`
KeyFile string `yaml:"key,omitempty"`
Password string `yaml:"password,omitempty" json:"-" secret:"true"`
AuthMode string `yaml:"auth,omitempty"` // "key" (default) or "password"
BinPath string `yaml:"bin,omitempty"` // remote homebutler path (default: homebutler)
}
func (*ServerConfig) SSHBinPath ¶ added in v0.2.0
func (s *ServerConfig) SSHBinPath() string
SSHBinPath returns the remote homebutler binary path.
func (*ServerConfig) SSHPort ¶ added in v0.2.0
func (s *ServerConfig) SSHPort() int
SSHPort returns the configured port or default 22.
func (*ServerConfig) SSHUser ¶ added in v0.2.0
func (s *ServerConfig) SSHUser() string
SSHUser returns the configured user or default "root".
func (*ServerConfig) UseKeyAuth ¶ added in v0.2.0
func (s *ServerConfig) UseKeyAuth() bool
UseKeyAuth returns true if key-based auth should be used (default).
type ServerPatch ¶ added in v0.35.0
type ServerPatch struct {
Name string
Rename string
Host *string
Port *int
User *string
AuthMode *string
Password *Secret
Remove bool
}
ServerPatch changes one server, addressed by the name it has now. A field left nil is not written, so a save about the port cannot disturb the user.
KeyFile is deliberately absent. It names a path on the machine homebutler runs on, and a browser is the wrong place to be choosing one; the CLI still edits it.
type Source ¶ added in v0.20.0
type Source string
Source identifies which resolution rule produced the config path.
func ResolveWithSource ¶ added in v0.20.0
ResolveWithSource finds the config file path using the following priority:
- Explicit path (--config flag)
- $HOMEBUTLER_CONFIG environment variable
- ~/.config/homebutler/config.yaml (XDG standard)
- ./homebutler.yaml (current directory)
Returns an empty path and SourceNone if no config file is found (defaults will be used). Note that the first two rules do not check that the file exists: a path the user named explicitly is returned as-is, so that a mistyped one can be reported rather than silently falling through to the next rule.
type ValidationResult ¶ added in v0.20.0
type ValidationResult struct {
Path string `json:"path"`
Source string `json:"source"`
Exists bool `json:"exists"`
Sections []Section `json:"sections,omitempty"`
Findings []Finding `json:"findings,omitempty"`
Valid bool `json:"valid"`
}
ValidationResult is the outcome of checking a config file.
func Validate ¶ added in v0.20.0
func Validate(explicit string) *ValidationResult
Validate resolves the config the same way every other command does, then reports what it found without applying it. It never contacts a remote server and never writes anything.
Validate deliberately does not go through Load: Load treats a missing file as "use defaults" so that running without a config works, which also means it cannot distinguish that from a path the user typed wrong.
func (*ValidationResult) Errors ¶ added in v0.20.0
func (r *ValidationResult) Errors() int
Errors returns the number of error-severity findings.
func (*ValidationResult) Warnings ¶ added in v0.20.0
func (r *ValidationResult) Warnings() int
Warnings returns the number of warning-severity findings.
type WakeTarget ¶
type WatchRuntimeConfig ¶ added in v0.16.1
type WatchRuntimeConfig struct {
Notify watch.NotifySettings `yaml:"notify,omitempty"`
Flapping watch.FlappingConfig `yaml:"flapping,omitempty"`
Retention watch.RetentionConfig `yaml:"retention,omitempty"`
}
func (*WatchRuntimeConfig) UnmarshalYAML ¶ added in v0.20.0
func (w *WatchRuntimeConfig) UnmarshalYAML(node *yaml.Node) error
UnmarshalYAML accepts both spellings of the watch notification settings:
watch: # canonical
notify:
enabled: true
watch: # flat, as the README documented it
enabled: true
The flat keys were never part of the schema, so a config copied from the README parsed without complaint and then ran with notifications off — the same symptom as #31, from a different cause. Correcting only the docs would have left those configs silently broken, so both spellings are read.
A file containing both forms gets the nested block; the flat keys are a compatibility path, not an override.
type WebConfig ¶ added in v0.39.0
type WebConfig struct {
// Token authenticates the dashboard's API. It lives here rather than in a
// flag because --token is visible in ps to every user on the machine, and
// a supervised dashboard would have to keep it in a unit file that is not
// ours to protect. The secret tag is what makes the 0600 check apply to a
// config that holds one.
Token string `yaml:"token,omitempty" json:"-" secret:"true"`
}
WebConfig holds what the dashboard needs that cannot be passed on a command line.