Documentation
¶
Overview ¶
Package worker executes durable deployment operations outside the HTTP process.
Index ¶
- type DeploymentProvisioner
- type Detector
- type DirectoryEntitlement
- type DirectorySyncer
- type Discoverer
- type Dispatcher
- type GitHubAppKeyReader
- type GitHubSyncer
- type Option
- type OwnershipIndexer
- type PreparedSource
- type SecretOpener
- type ServiceHour
- type SlotLease
- type SlotLeases
- type TrafficSource
- type TrafficSyncer
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type DeploymentProvisioner ¶
type DeploymentProvisioner interface {
ProvisionApplication(ctx context.Context, appID, name, url string) error
ProvisionGoLink(ctx context.Context, appID, alias, url string) error
DeleteApplication(ctx context.Context, appID string) error
}
DeploymentProvisioner is an optional external directory integration. Each operation must be idempotent: deployments and teardowns may be retried after ambiguous external responses without duplicating or orphaning applications.
type Detector ¶
type Detector struct {
// contains filtered or unexported fields
}
Detector performs the advisory repository scans RequestDetection queues: Dockerfile discovery, compose preview, and a database guess.
It runs independently of Dispatcher, deliberately. A detection touches no application, deployment or provider state, so it needs none of Dispatcher's application-lock fencing, heartbeat, or interrupted-recovery machinery -- see github.com/conductorone/apphub/internal/controlplane.DetectionState's doc comment for why a worker that dies mid-scan can simply leave the record Queued for the next poll to retry, safely.
Every scan uses the same source acquisition a real deploy uses, including GitHub App credentials for a private repository. That is why detection cannot run in the authenticated API process: it never holds them (serverconfig.Load skips loading source credentials in "serve" mode). A repository's Dockerfile is discovered by the same isolated worker that will eventually build it.
func NewDetector ¶
func NewDetector(cfg serverconfig.Config, repo cp.Repository, keyReader GitHubAppKeyReader) (*Detector, error)
NewDetector builds a Detector that shallow-clones with a fresh source.Checkout per scan, exactly as the deployment Dispatcher constructs one per deploy.
func NewDetectorWithFactory ¶
func NewDetectorWithFactory(repo cp.Repository, factory func() (Discoverer, error)) (*Detector, error)
NewDetectorWithFactory replaces only source acquisition, for hermetic tests that must not shell out to Git or reach a network.
func (*Detector) Run ¶
Run polls the queued-detection directory until ctx is done. It never returns a non-nil error for a single failed scan or a single failed poll: those are recorded on the detection record or left for the next poll, and a transient Repository outage must not take the whole worker process down with it, unlike a Dispatcher readiness failure at startup.
type DirectoryEntitlement ¶
type DirectoryEntitlement struct {
ID, DisplayName, Description, AppID string
}
DirectoryEntitlement is one entry from an operator-configured identity directory, narrowed to what DirectorySyncer needs.
Declared here rather than imported from credentials/c1directory so this package carries no ConductorOne idiom -- internal/boundary's c1-idiom-fenced rule confines that to credentials/c1 and credentials/c1directory, and internal/worker is not one of those two. The composition root bridges credentials/c1directory.Entitlement into this shape, the same pattern cmd/apphub's logsAdapter uses to keep internal/controlplane free of the AWS SDK.
type DirectorySyncer ¶
type DirectorySyncer struct {
// contains filtered or unexported fields
}
DirectorySyncer periodically syncs ConductorOne groups into durable records the Workspace admin UI reads. The full entitlement catalog is not synced: role mappings and group-gated flags are group membership, and a tenant's unfiltered entitlement list is too large to be a picker.
It runs independently of Dispatcher and Detector for the same reason GitHubSyncer does: this touches no application, deployment, or provider state. Most deployments never configure a directory source at all, so a nil client (checked at the composition root, mirroring GitHubSyncer's optional construction) means this loop is never started rather than started to do nothing.
func NewDirectorySyncer ¶
func NewDirectorySyncer(repo cp.Repository, client directoryAPI, audit cp.AuditWriter) (*DirectorySyncer, error)
NewDirectorySyncer constructs a DirectorySyncer. client is typically credentials/c1directory.Client, adapted to directoryAPI by the composition root. audit may be nil, which records no summary entries.
type Discoverer ¶
type Discoverer interface {
Discover(ctx context.Context, src deploy.Source) (commit string, tree detect.Tree, err error)
Close() error
}
Discoverer is the source-acquisition port Detector needs: fetch one approved repository revision's file tree for advisory introspection, without exposing a build directory. *source.Checkout satisfies it through Discover; Close releases whatever Discover did not already clean up itself.
type Dispatcher ¶
type Dispatcher struct {
// contains filtered or unexported fields
}
Dispatcher owns concurrency and operation lifetimes. Its production execution path always uses the deploy module and a real, per-operation Git checkout.
func New ¶
func New(cfg serverconfig.Config, repo cp.Repository, eligibility cp.Eligibility, providers map[string]compute.Provider, targets map[string]cp.TargetPolicy, keyReader GitHubAppKeyReader, opts ...Option) (*Dispatcher, error)
New assembles a dispatcher for native-identity targets and checks that private source checkout creation and cleanup work. Run owns claiming and job lifetimes; construction neither fetches source nor deploys cloud resources.
func NewWithSourceFactory ¶
func NewWithSourceFactory(cfg serverconfig.Config, repo cp.Repository, eligibility cp.Eligibility, providers map[string]compute.Provider, targets map[string]cp.TargetPolicy, factory func() (PreparedSource, error), opts ...Option) (*Dispatcher, error)
NewWithSourceFactory replaces only source acquisition. Execution still uses the real deployment module, durable repository fencing and supplied provider.
func (*Dispatcher) Run ¶
func (d *Dispatcher) Run(ctx context.Context) error
Run stops claiming on shutdown and lets each independent, deadline-bounded operation finish. After the drain bound it cancels/fences remaining attempts; their active locks remain until an operator confirms the old worker stopped.
type GitHubAppKeyReader ¶
type GitHubAppKeyReader interface {
Get(ctx context.Context) (credentials.Secret, error)
}
GitHubAppKeyReader resolves the admin-managed GitHub App's private key. Declared here rather than imported from internal/ghappkey so this package does not reach the AWS SDK: internal/boundary's aws-sdk-confined rule denies it to every package that is not a named provider boundary, and worker is not one -- only internal/ghappkey and the composition root ever construct the SSM client behind this interface, the same split Dispatcher's PreparedSource keeps around Git.
type GitHubSyncer ¶
type GitHubSyncer struct {
// contains filtered or unexported fields
}
GitHubSyncer periodically syncs the admin-managed GitHub App's installations from GitHub into durable records the Workspace admin UI reads.
It runs independently of Dispatcher and Detector for the same reason Detector does: this touches no application, deployment, or provider state, so it needs none of Dispatcher's fencing or heartbeat machinery.
A tick that finds no App configured yet, or fails to resolve or use its key, is silently skipped rather than treated as an error: this syncer always runs (cmd/apphub derives its key store automatically, with no operator toggle), but most administrators will never get around to creating an App at all, and "not yet configured" must read the same as "transiently unavailable" from a worker that has no way to distinguish an administrator who has not gotten to the Workspace yet from one whose key is temporarily unreachable.
func NewGitHubSyncer ¶
func NewGitHubSyncer(repo cp.Repository, keyReader GitHubAppKeyReader) (*GitHubSyncer, error)
NewGitHubSyncer constructs a GitHubSyncer. keyReader is typically internal/ghappkey.Reader.
type Option ¶
type Option func(*Dispatcher)
Option configures optional dispatcher capabilities.
func WithDeploymentProvisioner ¶
func WithDeploymentProvisioner(provisioner DeploymentProvisioner) Option
WithDeploymentProvisioner enables C1 provisioning for workspace flags that opt in. An unconfigured provisioner is never treated as successful.
func WithSecretOpener ¶
func WithSecretOpener(opener SecretOpener) Option
WithSecretOpener lets deployments apply the secret values the control plane sealed for them. Without it such a deployment fails before touching the target.
type OwnershipIndexer ¶
type OwnershipIndexer struct {
// contains filtered or unexported fields
}
OwnershipIndexer files the ApplicationOwnerKind index entries an application's owner set names but the store lacks.
It exists for applications written before owner sets: those carry one ownerUserId, read as a one-user owner set (see ApplicationRecord's UnmarshalJSON), and have no index entry, so "my applications" would miss them. It only ever creates index entries, never touches an application record, so it cannot fence a running deployment.
func NewOwnershipIndexer ¶
func NewOwnershipIndexer(repo cp.Repository) (*OwnershipIndexer, error)
NewOwnershipIndexer builds an indexer over repo.
type PreparedSource ¶
type PreparedSource interface {
deploy.SourceFetcher
Prepare(context.Context, deploy.Source) (string, error)
Close() error
}
PreparedSource is the operation-scoped source port. Injection is available to trusted Go assembly (including hermetic integration), never API/config input.
type SecretOpener ¶
type SecretOpener interface {
Open(ctx context.Context, subject cp.SecretSubject, ciphertext []byte) (compute.SecretValue, error)
}
SecretOpener decrypts a value the control plane sealed for one deployment. It must refuse a ciphertext sealed for any other subject.
type ServiceHour ¶
type ServiceHour struct {
Service string
Hour time.Time
Class int
Requests int64
First time.Time
Last time.Time
}
ServiceHour is one ingress service's requests of one status class (2 for 2xx, and so on) in one UTC hour.
type SlotLease ¶
type SlotLease struct {
// contains filtered or unexported fields
}
SlotLease is one held slot. Its methods are safe for concurrent use.
func (*SlotLease) Confirm ¶
Confirm reads the lease back and checks it is still this holder's and has time left. The runner calls it before trusting the slot's contents.
func (*SlotLease) Lost ¶
func (s *SlotLease) Lost() <-chan struct{}
Lost is closed when the lease can no longer be trusted.
type SlotLeases ¶
type SlotLeases struct {
// contains filtered or unexported fields
}
SlotLeases grants exclusive use of build slots across every worker, through one BuildSlotLease record per slot in the control-plane store.
A slot is taken by a compare-and-swap write only if its record is absent, then kept by renewing it. Expired records are quarantined until an operator verifies the old task stopped and clears the record. Every write names a fresh lease ID, so a worker that lost its lease cannot mistake a future holder's record for its own. See the compute/aws SlotLeaser port for why this cannot be a semaphore inside one process.
func NewSlotLeases ¶
func NewSlotLeases(repo cp.Repository, holder string) (*SlotLeases, error)
NewSlotLeases leases slots in repo on behalf of holder, a name an operator can recognise the worker process by.
type TrafficSource ¶
type TrafficSource interface {
Hourly(ctx context.Context, start, end time.Time) ([]ServiceHour, error)
}
TrafficSource counts ingress requests per service over whole UTC hours. The composition root adapts internal/logs.TrafficReader to it, which keeps this package clear of the AWS SDK.
type TrafficSyncer ¶
type TrafficSyncer struct {
// contains filtered or unexported fields
}
TrafficSyncer periodically counts each application's ingress requests from the access log and stores them as hourly buckets (see internal/controlplane/usage.go).
It runs alongside Dispatcher for the same reason DirectorySyncer does: it touches no deployment or provider state. Every sync recomputes whole hours and replaces them, so running it twice, or on two workers at once, stores the same numbers rather than twice the numbers.
func NewTrafficSyncer ¶
func NewTrafficSyncer(repo cp.Repository, traffic TrafficSource) (*TrafficSyncer, error)
NewTrafficSyncer constructs a TrafficSyncer over an access-log source.