github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/common/unitstatus.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"gopkg.in/juju/charm.v6-unstable/hooks"
    10  
    11  	"github.com/juju/juju/state"
    12  	"github.com/juju/juju/status"
    13  	"github.com/juju/juju/worker/uniter/operation"
    14  )
    15  
    16  // StatusAndErr pairs a StatusInfo with an error associated with
    17  // retrieving it.
    18  type StatusAndErr struct {
    19  	Status status.StatusInfo
    20  	Err    error
    21  }
    22  
    23  // UnitStatusGetter defines the unit functionality required to
    24  // determine unit agent and workload status.
    25  type UnitStatusGetter interface {
    26  	AgentStatus() (status.StatusInfo, error)
    27  	Status() (status.StatusInfo, error)
    28  	AgentPresence() (bool, error)
    29  	Name() string
    30  	Life() state.Life
    31  }
    32  
    33  // UnitStatus returns the unit agent and workload status for a given
    34  // unit, with special handling for agent presence.
    35  func UnitStatus(unit UnitStatusGetter) (agent StatusAndErr, workload StatusAndErr) {
    36  	agent.Status, agent.Err = unit.AgentStatus()
    37  	workload.Status, workload.Err = unit.Status()
    38  
    39  	if !canBeLost(agent.Status, workload.Status) {
    40  		// The unit is allocating or installing - there's no point in
    41  		// enquiring about the agent liveness.
    42  		return
    43  	}
    44  
    45  	agentAlive, err := unit.AgentPresence()
    46  	if err != nil {
    47  		return
    48  	}
    49  	if unit.Life() != state.Dead && !agentAlive {
    50  		// If the unit is in error, it would be bad to throw away
    51  		// the error information as when the agent reconnects, that
    52  		// error information would then be lost.
    53  		if workload.Status.Status != status.Error {
    54  			workload.Status.Status = status.Unknown
    55  			workload.Status.Message = fmt.Sprintf("agent lost, see 'juju show-status-log %s'", unit.Name())
    56  		}
    57  		agent.Status.Status = status.Lost
    58  		agent.Status.Message = "agent is not communicating with the server"
    59  	}
    60  	return
    61  }
    62  
    63  func canBeLost(agent, workload status.StatusInfo) bool {
    64  	switch agent.Status {
    65  	case status.Allocating:
    66  		return false
    67  	case status.Executing:
    68  		return agent.Message != operation.RunningHookMessage(string(hooks.Install))
    69  	}
    70  
    71  	// TODO(fwereade/wallyworld): we should have an explicit place in the model
    72  	// to tell us when we've hit this point, instead of piggybacking on top of
    73  	// status and/or status history.
    74  
    75  	return isWorkloadInstalled(workload)
    76  }
    77  
    78  func isWorkloadInstalled(workload status.StatusInfo) bool {
    79  	switch workload.Status {
    80  	case status.Maintenance:
    81  		return workload.Message != status.MessageInstallingCharm
    82  	case status.Waiting:
    83  		switch workload.Message {
    84  		case status.MessageWaitForMachine:
    85  		case status.MessageInstallingAgent:
    86  		case status.MessageInitializingAgent:
    87  			return false
    88  		}
    89  	}
    90  	return true
    91  }