agentstate

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 6, 2026 License: AGPL-3.0 Imports: 13 Imported by: 0

Documentation

Overview

Package agentstate owns the agent's per-instance state directory: the stable instance ID (R31) and the remote configuration cache (R7).

Layout (the OCI download caches under .compliance-framework/{plugins,policies} are shared and unchanged):

.compliance-framework/state/<key>/   key = hex(sha256(abs config path))[:16]; dir 0700
  instance-id                         0644, UUID + "\n"
  remote-config.json                  0600

The package is a leaf: it never imports cmd.

Index

Constants

View Source
const (
	// StateRoot is the default parent of every per-config state directory, relative to the
	// working directory like the download caches.
	StateRoot = ".compliance-framework/state"
)

Variables

View Source
var ErrCacheCorrupt = errors.New("remote config cache corrupt")

ErrCacheCorrupt is returned by LoadCache when the cache file does not parse or its checksum does not match. The agent reports failed/cache-corrupt once and continues without the cache.

Functions

func CanonicalOverlay

func CanonicalOverlay(raw json.RawMessage) (json.RawMessage, error)

CanonicalOverlay returns raw in the form a save/load round trip of the cache yields: compact, with <, > and & escaped as encoding/json escapes them. SaveCache re-indents the overlay and LoadCache would otherwise return those re-indented bytes, so anything that hashes or compares overlay bytes (the no-ETag rejection key) must use this form, for a fresh 200 body as well as a cached one. An empty overlay is returned unchanged.

func DefaultDir

func DefaultDir(configPath string) (string, error)

DefaultDir returns the default state directory for a config file: StateRoot/<key>, where key is the first 16 hex characters of sha256 of the absolute config path. Moving or renaming the config file therefore changes the directory and the instance ID (R52); containers should pin CCF_STATE_DIR.

func WriteFileAtomic

func WriteFileAtomic(path string, data []byte, perm os.FileMode) error

WriteFileAtomic writes data to a temp file in path's directory, fsyncs it and renames it over path, so readers see either the old or the new content.

Types

type Cache

type Cache struct {
	Version  int             `json:"version"`
	Identity Identity        `json:"identity"`
	Applied  *OverlayRecord  `json:"applied,omitempty"`
	Fetched  *OverlayRecord  `json:"fetched,omitempty"` // newest 200 body; may be the rejected one
	Rejected *RejectedRecord `json:"rejected,omitempty"`
	Checksum string          `json:"checksum"` // sha256 of the JSON with Checksum = ""
}

Cache is the persisted remote configuration state (0600, it may hold values an admin typed).

func (*Cache) IfNoneMatch

func (c *Cache) IfNoneMatch() string

IfNoneMatch is the ETag to present on the next fetch: the last 200's raw ETag, falling back to the applied one, or "" for an unconditional fetch.

type Identity

type Identity struct {
	APIURL   string `json:"api_url"`
	ClientID string `json:"client_id"`
}

Identity binds a cache to the API and credentials it was fetched with (R7). A cache whose identity differs from the current one is discarded, so a new client_id or API URL never applies another agent's overlay.

type OverlayRecord

type OverlayRecord struct {
	Revision int64 `json:"revision"`
	// ETag is the RAW ETag header of the 200 response (opaque, e.g. "r<rev>-<uuid>"). It is
	// sent back verbatim as If-None-Match and never built from a revision number (R7).
	ETag      string          `json:"etag"`
	Overlay   json.RawMessage `json:"overlay"`
	FetchedAt time.Time       `json:"fetched_at"`
}

OverlayRecord is one overlay document received from the API.

type RejectedRecord

type RejectedRecord struct {
	Revision int64  `json:"revision"`
	ETag     string `json:"etag"`
	// OverlaySHA256 keys the record with the revision when the response had no ETag.
	OverlaySHA256   string `json:"overlay_sha256,omitempty"`
	BaseFingerprint string `json:"base_fingerprint"`
	Status          string `json:"status"`
	Reason          string `json:"reason"`
	Error           string `json:"error"`
	// Unsafe completes the outcome re-reported after a restart. It is optional: caches
	// written before it existed load (and checksum) unchanged.
	Unsafe []agentconfig.Change `json:"unsafe,omitempty"`
}

RejectedRecord remembers that a fetched overlay was rejected against a given base, so it is not re-prepared on every poll. It is keyed by (ETag, BaseFingerprint); the revision is informational only because a reset API can reuse revision numbers, except when the response carried no ETag: then (Revision, OverlaySHA256) stands in for it.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is one agent instance's state directory. A Store whose directory is not writable keeps working in memory: the agent never fails because it cannot persist state.

func Open

func Open(dir string, logger hclog.Logger) *Store

Open prepares dir (MkdirAll 0700) and probes that it is writable. A failure is logged once as a WARN and the store continues in memory; it is never fatal.

func (*Store) CachePath

func (s *Store) CachePath() string

CachePath returns the cache file path.

func (*Store) Dir

func (s *Store) Dir() string

Dir returns the state directory.

func (*Store) InstanceID

func (s *Store) InstanceID(override string) (uuid.UUID, bool)

InstanceID returns this instance's stable ID and whether it is persisted:

  1. a valid override (flag or CCF_INSTANCE_ID) wins and is not persisted;
  2. otherwise the instance-id file;
  3. otherwise a new ID, written atomically (a corrupt file is replaced);
  4. if the store is not writable, the new ID lives in memory only (one WARN).

The result is memoized: every call on one Store returns the same ID.

func (*Store) LoadCache

func (s *Store) LoadCache(id Identity) (*Cache, error)

LoadCache reads the cache for identity id. A missing file yields an empty cache. A corrupt file yields an empty cache and ErrCacheCorrupt. A cache bound to another identity is discarded (empty cache, nil error, one INFO). Overlays are returned in CanonicalOverlay form.

func (*Store) SaveCache

func (s *Store) SaveCache(c *Cache) error

SaveCache writes the cache atomically (temp file, fsync, rename) with mode 0600. It is a no-op error when the store is not writable. It rewrites c's overlays to their CanonicalOverlay form, so c holds the same bytes a later LoadCache returns.

func (*Store) Writable

func (s *Store) Writable() bool

Writable reports whether the directory accepted a probe write at Open.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL