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