Permalink
Switch branches/tags
Find file
Fetching contributors…
Cannot retrieve contributors at this time
311 lines (264 sloc) 9.48 KB
// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package state
import (
"time"
)
// Status used to represent the status of an entity, but has recently become
// and applies to "workloads" as well, which we don't currently model, for no
// very clear reason.
//
// Status values currently apply to machine (agents), unit (agents), unit
// (workloads), service (workloads), and volumes.
type Status string
// StatusInfo holds a Status and associated information.
type StatusInfo struct {
Status Status
Message string
Data map[string]interface{}
Since *time.Time
}
// StatusSetter represents a type whose status can be set.
type StatusSetter interface {
SetStatus(status Status, info string, data map[string]interface{}) error
}
// StatusGetter represents a type whose status can be read.
type StatusGetter interface {
Status() (StatusInfo, error)
}
// StatusHistoryGetter instances can fetch their status history.
type StatusHistoryGetter interface {
StatusHistory(size int) ([]StatusInfo, error)
}
// Status values common to machine and unit agents.
const (
// The entity requires human intervention in order to operate
// correctly.
StatusError Status = "error"
// The entity is actively participating in the environment.
// For unit agents, this is a state we preserve for backwards
// compatibility with scripts during the life of Juju 1.x.
// In Juju 2.x, the agent-state will remain “active” and scripts
// will watch the unit-state instead for signals of service readiness.
StatusStarted Status = "started"
)
// Status values specific to machine agents.
const (
// The machine is not yet participating in the environment.
StatusPending Status = "pending"
// The machine's agent will perform no further action, other than
// to set the unit to Dead at a suitable moment.
StatusStopped Status = "stopped"
// The machine ought to be signalling activity, but it cannot be
// detected.
StatusDown Status = "down"
)
// Status values specific to unit agents.
const (
// The machine on which a unit is to be hosted is still being
// spun up in the cloud.
StatusAllocating Status = "allocating"
// The machine on which this agent is running is being rebooted.
// The juju-agent should move from rebooting to idle when the reboot is complete.
StatusRebooting Status = "rebooting"
// The agent is running a hook or action. The human-readable message should reflect
// which hook or action is being run.
StatusExecuting Status = "executing"
// Once the agent is installed and running it will notify the Juju server and its state
// becomes "idle". It will stay "idle" until some action (e.g. it needs to run a hook) or
// error (e.g it loses contact with the Juju server) moves it to a different state.
StatusIdle Status = "idle"
// The unit agent has failed in some way,eg the agent ought to be signalling
// activity, but it cannot be detected. It might also be that the unit agent
// detected an unrecoverable condition and managed to tell the Juju server about it.
StatusFailed Status = "failed"
// The juju agent has has not communicated with the juju server for an unexpectedly long time;
// the unit agent ought to be signalling activity, but none has been detected.
StatusLost Status = "lost"
// ---- Outdated ----
// The unit agent is downloading the charm and running the install hook.
StatusInstalling Status = "installing"
// The unit is being destroyed; the agent will soon mark the unit as “dead”.
// In Juju 2.x this will describe the state of the agent rather than a unit.
StatusStopping Status = "stopping"
)
// Status values specific to services and units, reflecting the
// state of the software itself.
const (
// The unit is not yet providing services, but is actively doing stuff
// in preparation for providing those services.
// This is a "spinning" state, not an error state.
// It reflects activity on the unit itself, not on peers or related units.
StatusMaintenance Status = "maintenance"
// This unit used to exist, we have a record of it (perhaps because of storage
// allocated for it that was flagged to survive it). Nonetheless, it is now gone.
StatusTerminated Status = "terminated"
// A unit-agent has finished calling install, config-changed, and start,
// but the charm has not called status-set yet.
StatusUnknown Status = "unknown"
// The unit is unable to progress to an active state because a service to
// which it is related is not running.
StatusWaiting Status = "waiting"
// The unit needs manual intervention to get back to the Active state.
StatusBlocked Status = "blocked"
// The unit believes it is correctly offering all the services it has
// been asked to offer.
StatusActive Status = "active"
)
// Status values specific to storage.
const (
// StatusAttaching indicates that the storage is being attached to a
// machine.
StatusAttaching Status = "attaching"
// StatusDetaching indicates that the storage is attached to a machine.
StatusAttached Status = "attached"
// StatusDetaching indicates that the storage is being detached
// from a machine.
StatusDetaching Status = "detaching"
// StatusDetached indicates that the storage is not attached to any
// machine.
StatusDetached Status = "detached"
// StatusDestroying indicates that the storage is being destroyed.
StatusDestroying Status = "destroying"
)
const (
MessageInstalling = "installing charm software"
// StorageReadyMessage is the message set to the agent status when all storage
// attachments are properly done.
StorageReadyMessage = "storage ready"
// PreparingStorageMessage is the message set to the agent status before trying
// to attach storages.
PreparingStorageMessage = "preparing storage"
)
// KnownAgentStatus returns true if status has a known value for an agent.
// It includes every status that has ever been valid for a unit or machine agent.
// This is used by the apiserver client facade to filter out unknown values.
func (status Status) KnownAgentStatus() bool {
switch status {
case
StatusAllocating,
StatusError,
StatusFailed,
StatusRebooting,
StatusExecuting,
StatusIdle:
return true
case //Deprecated status vales
StatusPending,
StatusStarted,
StatusStopped,
StatusInstalling,
StatusActive,
StatusStopping,
StatusDown:
return true
default:
return false
}
}
// KnownWorkloadStatus returns true if status has a known value for a workload.
// It includes every status that has ever been valid for a unit agent.
// This is used by the apiserver client facade to filter out unknown values.
func (status Status) KnownWorkloadStatus() bool {
if ValidWorkloadStatus(status) {
return true
}
switch status {
case StatusError: // include error so that we can filter on what the spec says is valid
return true
case // Deprecated statuses
StatusPending,
StatusInstalling,
StatusStarted,
StatusStopped,
StatusDown:
return true
default:
return false
}
}
// ValidWorkloadStatus returns true if status has a valid value (that is to say,
// a value that it's OK to set) for units or services.
func ValidWorkloadStatus(status Status) bool {
switch status {
case
StatusBlocked,
StatusMaintenance,
StatusWaiting,
StatusActive,
StatusUnknown,
StatusTerminated:
return true
default:
return false
}
}
// WorkloadMatches returns true if the candidate matches status,
// taking into account that the candidate may be a legacy
// status value which has been deprecated.
func (status Status) WorkloadMatches(candidate Status) bool {
switch candidate {
case status: // We could be holding an old status ourselves
return true
case StatusDown, StatusStopped:
candidate = StatusTerminated
case StatusInstalling:
candidate = StatusMaintenance
case StatusStarted:
candidate = StatusActive
}
return status == candidate
}
// Matches returns true if the candidate matches status,
// taking into account that the candidate may be a legacy
// status value which has been deprecated.
func (status Status) Matches(candidate Status) bool {
switch candidate {
case StatusDown:
candidate = StatusLost
case StatusStarted:
candidate = StatusActive
case StatusStopped:
candidate = StatusStopping
}
return status == candidate
}
// TranslateLegacyAgentStatus returns the status value clients expect to see for
// agent-state in versions prior to 1.24
func TranslateToLegacyAgentState(agentStatus, workloadStatus Status, workloadMessage string) (Status, bool) {
// Originally AgentState (a member of api.UnitStatus) could hold one of:
// StatusPending
// StatusInstalled
// StatusStarted
// StatusStopped
// StatusError
// StatusDown
// For compatibility reasons we convert modern states (from V2 uniter) into
// four of the old ones: StatusPending, StatusStarted, StatusStopped, or StatusError.
// For the purposes of deriving the legacy status, there's currently no better
// way to determine if a unit is installed.
// TODO(wallyworld) - use status history to see if start hook has run.
isInstalled := workloadStatus != StatusMaintenance || workloadMessage != MessageInstalling
switch agentStatus {
case StatusAllocating:
return StatusPending, true
case StatusError:
return StatusError, true
case StatusRebooting, StatusExecuting, StatusIdle, StatusLost, StatusFailed:
switch workloadStatus {
case StatusError:
return StatusError, true
case StatusTerminated:
return StatusStopped, true
case StatusMaintenance:
if isInstalled {
return StatusStarted, true
} else {
return StatusPending, true
}
default:
return StatusStarted, true
}
}
return "", false
}