github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/cmd/jujud/agent/unit.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package agent
     5  
     6  import (
     7  	"fmt"
     8  	"runtime"
     9  	"time"
    10  
    11  	"github.com/juju/cmd"
    12  	"github.com/juju/errors"
    13  	"github.com/juju/loggo"
    14  	"github.com/juju/names"
    15  	"github.com/juju/utils/featureflag"
    16  	"github.com/juju/utils/voyeur"
    17  	"gopkg.in/natefinch/lumberjack.v2"
    18  	"launchpad.net/gnuflag"
    19  	"launchpad.net/tomb"
    20  
    21  	"github.com/juju/juju/agent"
    22  	"github.com/juju/juju/cmd/jujud/agent/unit"
    23  	cmdutil "github.com/juju/juju/cmd/jujud/util"
    24  	"github.com/juju/juju/network"
    25  	jujuversion "github.com/juju/juju/version"
    26  	"github.com/juju/juju/worker"
    27  	"github.com/juju/juju/worker/dependency"
    28  	"github.com/juju/juju/worker/logsender"
    29  	"github.com/juju/juju/worker/uniter"
    30  )
    31  
    32  var (
    33  	agentLogger = loggo.GetLogger("juju.jujud")
    34  )
    35  
    36  // UnitAgent is a cmd.Command responsible for running a unit agent.
    37  type UnitAgent struct {
    38  	cmd.CommandBase
    39  	tomb tomb.Tomb
    40  	AgentConf
    41  	configChangedVal *voyeur.Value
    42  	UnitName         string
    43  	runner           worker.Runner
    44  	bufferedLogs     logsender.LogRecordCh
    45  	setupLogging     func(agent.Config) error
    46  	logToStdErr      bool
    47  	ctx              *cmd.Context
    48  
    49  	// Used to signal that the upgrade worker will not
    50  	// reboot the agent on startup because there are no
    51  	// longer any immediately pending agent upgrades.
    52  	// Channel used as a selectable bool (closed means true).
    53  	initialUpgradeCheckComplete chan struct{}
    54  }
    55  
    56  // NewUnitAgent creates a new UnitAgent value properly initialized.
    57  func NewUnitAgent(ctx *cmd.Context, bufferedLogs logsender.LogRecordCh) *UnitAgent {
    58  	return &UnitAgent{
    59  		AgentConf:        NewAgentConf(""),
    60  		configChangedVal: voyeur.NewValue(true),
    61  		ctx:              ctx,
    62  		initialUpgradeCheckComplete: make(chan struct{}),
    63  		bufferedLogs:                bufferedLogs,
    64  	}
    65  }
    66  
    67  // Info returns usage information for the command.
    68  func (a *UnitAgent) Info() *cmd.Info {
    69  	return &cmd.Info{
    70  		Name:    "unit",
    71  		Purpose: "run a juju unit agent",
    72  	}
    73  }
    74  
    75  func (a *UnitAgent) SetFlags(f *gnuflag.FlagSet) {
    76  	a.AgentConf.AddFlags(f)
    77  	f.StringVar(&a.UnitName, "unit-name", "", "name of the unit to run")
    78  	f.BoolVar(&a.logToStdErr, "log-to-stderr", false, "whether to log to standard error instead of log files")
    79  }
    80  
    81  // Init initializes the command for running.
    82  func (a *UnitAgent) Init(args []string) error {
    83  	if a.UnitName == "" {
    84  		return cmdutil.RequiredError("unit-name")
    85  	}
    86  	if !names.IsValidUnit(a.UnitName) {
    87  		return fmt.Errorf(`--unit-name option expects "<service>/<n>" argument`)
    88  	}
    89  	if err := a.AgentConf.CheckArgs(args); err != nil {
    90  		return err
    91  	}
    92  	a.runner = worker.NewRunner(cmdutil.IsFatal, cmdutil.MoreImportant, worker.RestartDelay)
    93  
    94  	if !a.logToStdErr {
    95  		if err := a.ReadConfig(a.Tag().String()); err != nil {
    96  			return err
    97  		}
    98  		agentConfig := a.CurrentConfig()
    99  
   100  		// the writer in ctx.stderr gets set as the loggo writer in github.com/juju/cmd/logging.go
   101  		a.ctx.Stderr = &lumberjack.Logger{
   102  			Filename:   agent.LogFilename(agentConfig),
   103  			MaxSize:    300, // megabytes
   104  			MaxBackups: 2,
   105  		}
   106  
   107  	}
   108  
   109  	return nil
   110  }
   111  
   112  // Stop stops the unit agent.
   113  func (a *UnitAgent) Stop() error {
   114  	a.runner.Kill()
   115  	return a.tomb.Wait()
   116  }
   117  
   118  // Run runs a unit agent.
   119  func (a *UnitAgent) Run(ctx *cmd.Context) error {
   120  	defer a.tomb.Done()
   121  	if err := a.ReadConfig(a.Tag().String()); err != nil {
   122  		return err
   123  	}
   124  	agentConfig := a.CurrentConfig()
   125  
   126  	agentLogger.Infof("unit agent %v start (%s [%s])", a.Tag().String(), jujuversion.Current, runtime.Compiler)
   127  	if flags := featureflag.String(); flags != "" {
   128  		logger.Warningf("developer feature flags enabled: %s", flags)
   129  	}
   130  	network.SetPreferIPv6(agentConfig.PreferIPv6())
   131  
   132  	// Sometimes there are upgrade steps that are needed for each unit.
   133  	// There are plans afoot to unify the unit and machine agents. When
   134  	// this happens, there will be a simple helper function for the upgrade
   135  	// steps to run something for each unit on the machine. Until then, we
   136  	// need to have the uniter do it, as the overhead of getting a full
   137  	// upgrade process in the unit agent out weights the current benefits.
   138  	// So.. since the upgrade steps are all idempotent, we will just call
   139  	// the upgrade steps when we start the uniter. To be clear, these
   140  	// should move back to the upgrade package when we do unify the agents.
   141  	runUpgrades(agentConfig.Tag(), agentConfig.DataDir())
   142  
   143  	a.runner.StartWorker("api", a.APIWorkers)
   144  	err := cmdutil.AgentDone(logger, a.runner.Wait())
   145  	a.tomb.Kill(err)
   146  	return err
   147  }
   148  
   149  // runUpgrades is a temporary fix to deal with upgrade steps that need
   150  // to be run for each unit. This function cannot fail. Errors in the
   151  // upgrade steps are logged, but the uniter will attempt to continue.
   152  // Worst case, we are no worse off than we are today, best case, things
   153  // actually work properly. Only simple upgrade steps that don't use the API
   154  // are available now. If we need really complex steps using the API, there
   155  // should be significant steps to unify the agents first.
   156  func runUpgrades(tag names.Tag, dataDir string) {
   157  	unitTag, ok := tag.(names.UnitTag)
   158  	if !ok {
   159  		logger.Errorf("unit agent tag not a unit tag: %v", tag)
   160  		return
   161  	}
   162  	if err := uniter.AddStoppedFieldToUniterState(unitTag, dataDir); err != nil {
   163  		logger.Errorf("Upgrade step failed - add Stopped field to uniter state: %v", err)
   164  	}
   165  	if err := uniter.AddInstalledToUniterState(unitTag, dataDir); err != nil {
   166  		logger.Errorf("Upgrade step failed - installed boolean needs to be set in the uniter local state: %v", err)
   167  	}
   168  }
   169  
   170  // APIWorkers returns a dependency.Engine running the unit agent's responsibilities.
   171  func (a *UnitAgent) APIWorkers() (worker.Worker, error) {
   172  	manifolds := unit.Manifolds(unit.ManifoldsConfig{
   173  		Agent:               agent.APIHostPortsSetter{a},
   174  		LogSource:           a.bufferedLogs,
   175  		LeadershipGuarantee: 30 * time.Second,
   176  		AgentConfigChanged:  a.configChangedVal,
   177  	})
   178  
   179  	config := dependency.EngineConfig{
   180  		IsFatal:     cmdutil.IsFatal,
   181  		WorstError:  cmdutil.MoreImportantError,
   182  		ErrorDelay:  3 * time.Second,
   183  		BounceDelay: 10 * time.Millisecond,
   184  	}
   185  	engine, err := dependency.NewEngine(config)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	if err := dependency.Install(engine, manifolds); err != nil {
   190  		if err := worker.Stop(engine); err != nil {
   191  			logger.Errorf("while stopping engine with bad manifolds: %v", err)
   192  		}
   193  		return nil, err
   194  	}
   195  	return engine, nil
   196  }
   197  
   198  func (a *UnitAgent) Tag() names.Tag {
   199  	return names.NewUnitTag(a.UnitName)
   200  }
   201  
   202  func (a *UnitAgent) ChangeConfig(mutate agent.ConfigMutator) error {
   203  	err := a.AgentConf.ChangeConfig(mutate)
   204  	a.configChangedVal.Set(true)
   205  	return errors.Trace(err)
   206  }