remote_state.go RawHistoryFolder
1package domain
2
3import (
4 "fmt"
5)
6
7// RemoteAvailability is the monitor's last-known reachability for one
8// registered endpoint. Checking is tracked separately and never replaces
9// availability: an in-flight observation preserves the previous value.
10type RemoteAvailability uint8
11
12const (
13 RemoteAvailabilityUnknown RemoteAvailability = iota + 1
14 RemoteAvailabilityReachable
15 RemoteAvailabilityUnreachable
16 RemoteAvailabilityIncompatible
17 RemoteAvailabilityAuthFailed
18 RemoteAvailabilityInvalidResponse
19 RemoteAvailabilityNoDaemon
20)
21
22// String returns the stable lowercase wire/display token for an availability.
23func (a RemoteAvailability) String() string {
24 switch a {
25 case RemoteAvailabilityUnknown:
26 return "unknown"
27 case RemoteAvailabilityReachable:
28 return "reachable"
29 case RemoteAvailabilityUnreachable:
30 return "unreachable"
31 case RemoteAvailabilityIncompatible:
32 return "incompatible"
33 case RemoteAvailabilityAuthFailed:
34 return "authentication_failed"
35 case RemoteAvailabilityInvalidResponse:
36 return "invalid_response"
37 case RemoteAvailabilityNoDaemon:
38 return "no_daemon"
39 default:
40 return "unknown"
41 }
42}
43
44// RemoteFailureKind classifies why one remote observation failed. It mirrors
45// the observation error classes without carrying raw stderr or endpoints.
46type RemoteFailureKind uint8
47
48const (
49 RemoteFailureNone RemoteFailureKind = iota
50 RemoteFailureTransport
51 RemoteFailureTimeout
52 RemoteFailureTrust
53 RemoteFailureAuthentication
54 RemoteFailureIncompatible
55 RemoteFailureInvalidResponse
56)
57
58// String returns the stable lowercase token for a failure kind.
59func (k RemoteFailureKind) String() string {
60 switch k {
61 case RemoteFailureNone:
62 return "none"
63 case RemoteFailureTransport:
64 return "transport"
65 case RemoteFailureTimeout:
66 return "timeout"
67 case RemoteFailureTrust:
68 return "trust"
69 case RemoteFailureAuthentication:
70 return "authentication"
71 case RemoteFailureIncompatible:
72 return "incompatible"
73 case RemoteFailureInvalidResponse:
74 return "invalid_response"
75 default:
76 return "transport"
77 }
78}
79
80// RemoteFailure is the typed last-failure value carried in snapshots. Err
81// preserves the underlying cause for errors.Is/As callers; Kind is the
82// sanitized classification safe for presentation and notice policy.
83type RemoteFailure struct {
84 Kind RemoteFailureKind
85 Err error
86}
87
88// Error reports the sanitized failure kind.
89func (f RemoteFailure) Error() string { return "remote observation failed: " + f.Kind.String() }
90
91// Unwrap exposes the underlying cause to errors.Is/As.
92func (f RemoteFailure) Unwrap() error { return f.Err }
93
94// RemoteGeneration is a per-registration fencing generation. It increments
95// each time an endpoint is (re-)registered so late results bound to an older
96// generation can never apply to a newer registration of the same endpoint.
97type RemoteGeneration uint64
98
99// RemoteRegistration is the exact configured identity of one monitored
100// endpoint: the endpoint string plus a persistent random incarnation. Full
101// removal followed by re-add creates a new incarnation, so equality on both
102// fields fences ABA re-registration even when the endpoint string matches.
103type RemoteRegistration struct {
104 Endpoint string
105 Incarnation [16]byte
106 Generation RemoteGeneration
107}
108
109// NewRemoteRegistration creates a registration for a validated endpoint
110// with an explicit incarnation. Randomness is the caller's concern:
111// adapters sample fresh incarnations while domain stays deterministic.
112func NewRemoteRegistration(endpoint string, incarnation [16]byte) (RemoteRegistration, error) {
113 if err := ValidateRemoteHostTarget(endpoint); err != nil {
114 return RemoteRegistration{}, err
115 }
116 return RemoteRegistration{Endpoint: endpoint, Incarnation: incarnation, Generation: 1}, nil
117}
118
119// IsZero reports whether the registration carries no identity. A zero
120// incarnation never identifies a host; unbound legacy records stay zero.
121func (r RemoteRegistration) IsZero() bool {
122 return r.Endpoint == "" || r.Incarnation == [16]byte{}
123}
124
125// Equal compares exact configured identity: endpoint, incarnation and
126// generation must all match.
127func (r RemoteRegistration) Equal(other RemoteRegistration) bool {
128 return r.Endpoint == other.Endpoint && r.Incarnation == other.Incarnation && r.Generation == other.Generation
129}
130
131// Validate rejects empty endpoints and zero incarnations.
132func (r RemoteRegistration) Validate() error {
133 if err := ValidateRemoteHostTarget(r.Endpoint); err != nil {
134 return err
135 }
136 if r.Incarnation == [16]byte{} {
137 return fmt.Errorf("remote registration for %q has no incarnation", r.Endpoint)
138 }
139 if r.Generation == 0 {
140 return fmt.Errorf("remote registration for %q has no generation", r.Endpoint)
141 }
142 return nil
143}
144
145// Stable RemoteReason tokens shared by catalogue validation, picker
146// presentation, and daemon projections. Centralizing them here keeps
147// display/diagnostic labels consistent without magic strings.
148const (
149 RemoteReasonRefreshing = "refreshing"
150 RemoteReasonCatalogStale = "catalog_stale"
151 RemoteReasonHostUnreachable = "host_unreachable"
152 RemoteReasonVersionMismatch = "version_mismatch"
153 RemoteReasonSessionDown = "session_down"
154 RemoteReasonSessionBroken = "session_broken"
155 RemoteReasonSessionStopped = "session_stopped"
156 RemoteReasonIdentityChanged = "identity_changed"
157 RemoteReasonNotFound = "not_found"
158 RemoteReasonTimeout = "timeout"
159 RemoteReasonMalformed = "malformed"
160 RemoteReasonAuthFailure = "auth_failure"
161 RemoteReasonNoDaemon = "no_daemon"
162)