Documentation
¶
Overview ¶
Package remote resolves Rack remote configuration and credentials for the Spool CLI without ever persisting secrets to durable repository state.
Index ¶
- Constants
- Variables
- func DeleteBranch(ctx context.Context, client *Client, cfg Config, credential string, ...) error
- func EnvVar(mode AuthMode) (string, error)
- func Redact(text string, secrets ...string) string
- type AuthMode
- type BranchCreateRequest
- type BranchListEntry
- type BranchListResult
- type BranchResult
- type Client
- type Config
- type Credential
- type CredentialSource
- type DivergedError
- type FieldReport
- type FieldStatus
- type KeychainStore
- type KeyringStore
- type NonFastForwardError
- type PromptFunc
- type ProtectedBranchError
- type PullResult
- type PushCommitRecord
- type PushRequest
- type PushResult
- type RackError
- type ResolveOptions
- type VersionReport
Constants ¶
const CorrelationIDHeader = "X-Correlation-Id"
CorrelationIDHeader is the HTTP header the CLI attaches to every outbound Rack request, and that Rack's audit log records, so an operator can correlate a single CLI invocation's requests (healthz, push, pull, ...) with the audit events it produced remotely.
const RedactedPlaceholder = "***redacted***"
RedactedPlaceholder replaces any redacted secret value.
Variables ¶
var ( // ErrBranchAlreadyExists reports that Rack rejected a branch creation // because a branch with that name already exists. ErrBranchAlreadyExists = errors.New("rack branch already exists") // ErrBranchSourceNotFound reports that Rack rejected a branch creation // because its named source branch or commit does not exist. ErrBranchSourceNotFound = errors.New("rack branch source not found") // ErrBranchNotFound reports that Rack has no such branch for the // configured repository. ErrBranchNotFound = errors.New("rack remote has no such branch") // ErrBranchRejected reports that Rack rejected a branch lifecycle // request for a reason other than the typed cases above. ErrBranchRejected = errors.New("rack rejected the branch request") // ErrBranchSourceRequired reports a branch creation request that named // neither a source branch nor a source commit. ErrBranchSourceRequired = errors.New("branch source is required") // ErrBranchSourceAmbiguous reports a branch creation request that named // both a source branch and a source commit. ErrBranchSourceAmbiguous = errors.New("branch source must identify a branch or commit, not both") )
var ( // ErrInvalidConfig reports a remote configuration that fails validation. ErrInvalidConfig = errors.New("remote configuration is invalid") // ErrSecretLikeValue reports a non-secret field whose value resembles a // credential and must not be persisted to repository configuration. ErrSecretLikeValue = errors.New("value looks like a credential and cannot be stored as remote configuration") )
var ErrCredentialNotFound = errors.New("no credential available for remote authentication")
ErrCredentialNotFound reports that no credential could be resolved from any configured source.
var ErrInvalidPullEnvelope = errors.New("rack pull response is invalid")
ErrInvalidPullEnvelope indicates Rack's pull response body was malformed, non-canonically encoded, or internally inconsistent (a declared pack length or hash that does not match the transmitted bytes).
var ErrPullBranchNotFound = errors.New("rack remote has no such branch")
ErrPullBranchNotFound reports that Rack has no such branch for the configured repository.
var ErrPullRejected = errors.New("rack rejected the pull")
ErrPullRejected reports that Rack rejected a pull for a reason other than divergence or a missing branch.
var ErrPushRejected = errors.New("rack rejected the push")
ErrPushRejected reports that Rack rejected a push for a reason other than a non-fast-forward base (e.g. a malformed pack or invalid metadata).
var ErrRemoteUnreachable = errors.New("rack remote is unreachable")
ErrRemoteUnreachable reports that the configured Rack endpoint could not be reached or returned an unusable response.
Functions ¶
func DeleteBranch ¶
func DeleteBranch(ctx context.Context, client *Client, cfg Config, credential string, name string) error
DeleteBranch deletes the named remote branch from cfg.Endpoint/cfg.RepoID. credential, when non-empty, is attached per cfg.AuthMode. An attempt to delete the repository's protected default branch is returned as a *ProtectedBranchError (use errors.As); a missing branch is wrapped in ErrBranchNotFound; any other rejection is wrapped in ErrBranchRejected.
func EnvVar ¶
EnvVar returns the conventional environment variable name used to resolve a credential for mode.
Types ¶
type BranchCreateRequest ¶
type BranchCreateRequest struct {
Name string `json:"name"`
SourceBranch string `json:"sourceBranch,omitempty"`
SourceCommit string `json:"sourceCommit,omitempty"`
}
BranchCreateRequest describes a new remote branch to create. Exactly one of SourceBranch or SourceCommit must be set, matching the local branch.Source contract.
type BranchListEntry ¶
type BranchListEntry struct {
Name string `json:"name"`
HeadCommit string `json:"headCommit"`
Default bool `json:"default,omitempty"`
}
BranchListEntry is one branch in a BranchListResult.
type BranchListResult ¶
type BranchListResult struct {
Branches []BranchListEntry `json:"branches"`
}
BranchListResult is Rack's response to a remote branch list request.
func ListBranches ¶
func ListBranches(ctx context.Context, client *Client, cfg Config, credential string) (BranchListResult, error)
ListBranches lists every remote branch for cfg.Endpoint/cfg.RepoID. credential, when non-empty, is attached per cfg.AuthMode.
type BranchResult ¶
BranchResult identifies a remote branch and the wire commit ID it currently points at.
func CreateBranch ¶
func CreateBranch(ctx context.Context, client *Client, cfg Config, credential string, req BranchCreateRequest) (BranchResult, error)
CreateBranch creates a remote branch named req.Name from exactly one of req.SourceBranch or req.SourceCommit against cfg.Endpoint/cfg.RepoID. credential, when non-empty, is attached per cfg.AuthMode. An existing branch of the same name is wrapped in ErrBranchAlreadyExists; a missing source is wrapped in ErrBranchSourceNotFound; any other rejection is wrapped in ErrBranchRejected; an unreachable or malformed-response remote is wrapped in ErrRemoteUnreachable.
func DefaultBranch ¶
func DefaultBranch(ctx context.Context, client *Client, cfg Config, credential string) (BranchResult, error)
DefaultBranch discovers the default branch for cfg.Endpoint/cfg.RepoID. credential, when non-empty, is attached per cfg.AuthMode.
type Client ¶
type Client struct {
HTTPClient *http.Client
// CorrelationID is sent as CorrelationIDHeader on every request this
// Client issues. Callers should construct one Client per CLI command
// invocation (via NewClient) and reuse it for every remote call that
// invocation makes, so Rack's audit log can be correlated end to end.
CorrelationID string
}
Client performs HTTP calls against a configured Rack endpoint.
type Config ¶
type Config struct {
// Endpoint is the Rack HTTP(S) base URL.
Endpoint string `toml:"endpoint"`
// RepoID is the logical Rack repository/tenant identity.
RepoID string `toml:"repo_id"`
// AuthMode selects how credentials for this remote are authenticated.
AuthMode AuthMode `toml:"auth_mode"`
}
Config is the versioned, non-secret Rack remote configuration persisted in a repository's control state. It never contains credentials.
type Credential ¶
type Credential struct {
Value string
Source CredentialSource
}
Credential is a resolved secret value and the source it came from. It is never persisted to disk.
func ResolveCredential ¶
func ResolveCredential(repoID string, mode AuthMode, opts ResolveOptions) (Credential, error)
ResolveCredential resolves a credential for repoID under mode, trying the OS keychain, then the conventional environment variable, then an interactive prompt, in that order. The resolved value is never persisted to disk. It returns ErrCredentialNotFound if no source yields a value.
type CredentialSource ¶
type CredentialSource string
CredentialSource identifies where a resolved credential came from.
const ( // CredentialSourceKeychain reports a credential resolved from the OS // keychain or secret store. CredentialSourceKeychain CredentialSource = "keychain" // CredentialSourceEnv reports a credential resolved from an explicit // process environment variable. CredentialSourceEnv CredentialSource = "env" // CredentialSourceInteractive reports a credential resolved from an // interactive TTY prompt. CredentialSourceInteractive CredentialSource = "interactive" )
type DivergedError ¶
DivergedError reports that the caller's supplied known commit is not an ancestor of Rack's current branch head. ActualHead is Rack's current wire head commit ID for the branch. CorrelationID is the value Rack's audit log recorded for this request.
func (*DivergedError) Error ¶
func (e *DivergedError) Error() string
type FieldReport ¶
type FieldReport struct {
Local uint32 `json:"local"`
Remote *uint32 `json:"remote,omitempty"`
Status FieldStatus `json:"status"`
}
FieldReport is the negotiated status of one graphcontract version field.
type FieldStatus ¶
type FieldStatus string
FieldStatus reports how a single graphcontract version field compares against this build's constants.
const ( // FieldStatusMatch reports that the remote's reported version equals // this build's constant. FieldStatusMatch FieldStatus = "match" // FieldStatusMismatch reports that the remote's reported version // differs from this build's constant. FieldStatusMismatch FieldStatus = "mismatch" // FieldStatusUnknown reports that the remote's response omitted the // field, so no comparison could be made. FieldStatusUnknown FieldStatus = "unknown" )
type KeychainStore ¶
KeychainStore resolves a credential from an OS-native keychain or secret store. Lookup must return ErrCredentialNotFound when no entry exists.
type KeyringStore ¶
type KeyringStore struct{}
KeyringStore resolves credentials from the operating system's native keychain or secret store via github.com/zalando/go-keyring.
func (KeyringStore) Lookup ¶
func (KeyringStore) Lookup(service, account string) (string, error)
Lookup returns the secret stored for service/account, or ErrCredentialNotFound if no entry exists or the platform has no available secret store. It never returns platform-specific error types to callers.
type NonFastForwardError ¶
NonFastForwardError reports that Rack rejected a push because the supplied BaseCommit is no longer the branch's actual head. ActualHead is Rack's current wire head commit ID for the branch; Guidance is a human-readable message from Rack explaining the rejection; CorrelationID is the value Rack's audit log recorded for this request.
func (*NonFastForwardError) Error ¶
func (e *NonFastForwardError) Error() string
type PromptFunc ¶
PromptFunc requests a secret value interactively, typically reading hidden input from a TTY. Implementations must return ErrCredentialNotFound (or wrap it) when no interactive input is available, rather than blocking indefinitely.
func TerminalPrompt ¶
func TerminalPrompt() PromptFunc
TerminalPrompt returns a PromptFunc that reads hidden input from stdin when it is an interactive terminal, echoing the label to stdout first. It returns ErrCredentialNotFound (wrapped) when stdin is not a terminal, rather than blocking on a pipe or redirected input.
type ProtectedBranchError ¶
ProtectedBranchError reports that Rack refused to delete a branch because it is the repository's protected default branch. CorrelationID lets an operator match this failure against Rack's audit log.
func (*ProtectedBranchError) Error ¶
func (e *ProtectedBranchError) Error() string
type PullResult ¶
PullResult is the outcome of a successful pull HTTP call. When UpToDate is true, Packs is empty and HeadCommit is the branch's current wire commit ID. Otherwise Packs holds every hash-verified, canonically-framed v2 pack Rack sent, oldest-to-newest, and HeadCommit is the wire commit ID the caller advances to once every pack is installed.
func Pull ¶
func Pull(ctx context.Context, client *Client, cfg Config, credential string, branch, knownCommit string) (PullResult, error)
Pull fetches new commits for branch from cfg.Endpoint/cfg.RepoID, starting after knownCommit (empty to fetch the branch's entire history). credential, when non-empty, is attached per cfg.AuthMode. A diverged known commit is returned as a *DivergedError (use errors.As); a missing branch is wrapped in ErrPullBranchNotFound; any other rejection is wrapped in ErrPullRejected; an unreachable or malformed-response remote is wrapped in ErrRemoteUnreachable or ErrInvalidPullEnvelope.
type PushCommitRecord ¶
type PushCommitRecord struct {
ID string `json:"id"`
Commit graphcontract.Commit `json:"commit"`
}
PushCommitRecord is one commit to register with Rack as part of a push, matching Rack's expected `metadata.commits[]` wire shape byte-for-byte.
type PushRequest ¶
type PushRequest struct {
Branch string
BaseCommit string
TargetCommit string
Commits []PushCommitRecord
PackHash string
PackFormat uint32
PackData []byte
}
PushRequest describes a native push to send to a Rack remote. Branch, BaseCommit, TargetCommit, Commits, PackHash, and PackFormat become the JSON `metadata` multipart part; PackData becomes the binary `pack` part. Callers (the CLI command layer) build this from a repository.PushPack.
type PushResult ¶
PushResult is Rack's success response to a push: the branch that advanced and the wire commit ID it now points at.
func Push ¶
func Push(ctx context.Context, client *Client, cfg Config, credential string, req PushRequest) (PushResult, error)
Push sends req to cfg.Endpoint/cfg.RepoID and returns Rack's response. credential, when non-empty, is attached per cfg.AuthMode. A non-fast- forward rejection is returned as a *NonFastForwardError (use errors.As); any other rejection is wrapped in ErrPushRejected; an unreachable or malformed-response remote is wrapped in ErrRemoteUnreachable.
type RackError ¶
type RackError struct {
Code string `json:"error"`
Message string `json:"message"`
CorrelationID string `json:"correlationId,omitempty"`
CurrentHead string `json:"currentHead,omitempty"`
}
RackError is the decoded form of Rack's JSON error envelope:
{"error": "...", "message": "...", "correlationId": "...", "currentHead": "..."}
Code is a stable, machine-readable identifier; Message is a human-readable description; CorrelationID lets an operator match this failure against Rack's audit log; CurrentHead, when present, is the branch's actual wire head commit ID (populated on conflict/non-fast-forward responses).
type ResolveOptions ¶
type ResolveOptions struct {
// Keychain resolves a credential from an OS secret store.
Keychain KeychainStore
// Getenv resolves a credential from the process environment. Defaults to
// os.Getenv when nil via ResolveCredential's caller.
Getenv func(string) string
// Prompt resolves a credential interactively as a last resort.
Prompt PromptFunc
}
ResolveOptions configures ResolveCredential's credential sources. Any field may be nil/zero to skip that source.
type VersionReport ¶
type VersionReport struct {
PackFormatVersion FieldReport `json:"packFormatVersion"`
PackIndexFormatVersion FieldReport `json:"packIndexFormatVersion"`
PackManifestFormatVersion FieldReport `json:"packManifestFormatVersion"`
}
VersionReport is the full graphcontract version negotiation result for one remote.
func NegotiateVersions ¶
func NegotiateVersions(ctx context.Context, client *Client, cfg Config, credential string) (VersionReport, error)
NegotiateVersions probes cfg.Endpoint's /healthz and compares any reported graphcontract version fields against this build's constants. credential, when non-empty, is attached to the request per cfg.AuthMode. A remote response that omits a version field is reported explicitly as FieldStatusUnknown rather than causing an error; a remote that cannot be reached at all, or that returns a non-200/undecodable response, causes NegotiateVersions to return a wrapped ErrRemoteUnreachable instead of a VersionReport, which callers should handle explicitly (e.g. `spl remote show` reports this as an "unreachable" status rather than failing).