github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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  	"io"
     9  	"runtime"
    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  	"gopkg.in/natefinch/lumberjack.v2"
    17  	"launchpad.net/gnuflag"
    18  	"launchpad.net/tomb"
    19  
    20  	"github.com/juju/juju/agent"
    21  	"github.com/juju/juju/api"
    22  	"github.com/juju/juju/api/leadership"
    23  	cmdutil "github.com/juju/juju/cmd/jujud/util"
    24  	"github.com/juju/juju/network"
    25  	"github.com/juju/juju/tools"
    26  	"github.com/juju/juju/version"
    27  	"github.com/juju/juju/worker"
    28  	"github.com/juju/juju/worker/apiaddressupdater"
    29  	workerlogger "github.com/juju/juju/worker/logger"
    30  	"github.com/juju/juju/worker/proxyupdater"
    31  	"github.com/juju/juju/worker/rsyslog"
    32  	"github.com/juju/juju/worker/uniter"
    33  	"github.com/juju/juju/worker/upgrader"
    34  )
    35  
    36  var (
    37  	agentLogger         = loggo.GetLogger("juju.jujud")
    38  	reportClosedUnitAPI = func(io.Closer) {}
    39  )
    40  
    41  // UnitAgent is a cmd.Command responsible for running a unit agent.
    42  type UnitAgent struct {
    43  	cmd.CommandBase
    44  	tomb tomb.Tomb
    45  	AgentConf
    46  	UnitName         string
    47  	runner           worker.Runner
    48  	setupLogging     func(agent.Config) error
    49  	logToStdErr      bool
    50  	ctx              *cmd.Context
    51  	apiStateUpgrader APIStateUpgrader
    52  
    53  	// Used to signal that the upgrade worker will not
    54  	// reboot the agent on startup because there are no
    55  	// longer any immediately pending agent upgrades.
    56  	// Channel used as a selectable bool (closed means true).
    57  	initialAgentUpgradeCheckComplete chan struct{}
    58  }
    59  
    60  // NewUnitAgent creates a new UnitAgent value properly initialized.
    61  func NewUnitAgent(ctx *cmd.Context) *UnitAgent {
    62  	return &UnitAgent{
    63  		AgentConf: NewAgentConf(""),
    64  		ctx:       ctx,
    65  		initialAgentUpgradeCheckComplete: make(chan struct{}),
    66  	}
    67  }
    68  
    69  func (a *UnitAgent) getUpgrader(st *api.State) APIStateUpgrader {
    70  	if a.apiStateUpgrader != nil {
    71  		return a.apiStateUpgrader
    72  	}
    73  	return st.Upgrader()
    74  }
    75  
    76  // Info returns usage information for the command.
    77  func (a *UnitAgent) Info() *cmd.Info {
    78  	return &cmd.Info{
    79  		Name:    "unit",
    80  		Purpose: "run a juju unit agent",
    81  	}
    82  }
    83  
    84  func (a *UnitAgent) SetFlags(f *gnuflag.FlagSet) {
    85  	a.AgentConf.AddFlags(f)
    86  	f.StringVar(&a.UnitName, "unit-name", "", "name of the unit to run")
    87  	f.BoolVar(&a.logToStdErr, "log-to-stderr", false, "whether to log to standard error instead of log files")
    88  }
    89  
    90  // Init initializes the command for running.
    91  func (a *UnitAgent) Init(args []string) error {
    92  	if a.UnitName == "" {
    93  		return cmdutil.RequiredError("unit-name")
    94  	}
    95  	if !names.IsValidUnit(a.UnitName) {
    96  		return fmt.Errorf(`--unit-name option expects "<service>/<n>" argument`)
    97  	}
    98  	if err := a.AgentConf.CheckArgs(args); err != nil {
    99  		return err
   100  	}
   101  	a.runner = worker.NewRunner(cmdutil.IsFatal, cmdutil.MoreImportant)
   102  
   103  	if !a.logToStdErr {
   104  		if err := a.ReadConfig(a.Tag().String()); err != nil {
   105  			return err
   106  		}
   107  		agentConfig := a.CurrentConfig()
   108  
   109  		// the writer in ctx.stderr gets set as the loggo writer in github.com/juju/cmd/logging.go
   110  		a.ctx.Stderr = &lumberjack.Logger{
   111  			Filename:   agent.LogFilename(agentConfig),
   112  			MaxSize:    300, // megabytes
   113  			MaxBackups: 2,
   114  		}
   115  
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  // Stop stops the unit agent.
   122  func (a *UnitAgent) Stop() error {
   123  	a.runner.Kill()
   124  	return a.tomb.Wait()
   125  }
   126  
   127  // Run runs a unit agent.
   128  func (a *UnitAgent) Run(ctx *cmd.Context) error {
   129  	defer a.tomb.Done()
   130  	if err := a.ReadConfig(a.Tag().String()); err != nil {
   131  		return err
   132  	}
   133  	agentConfig := a.CurrentConfig()
   134  
   135  	agentLogger.Infof("unit agent %v start (%s [%s])", a.Tag().String(), version.Current, runtime.Compiler)
   136  	if flags := featureflag.String(); flags != "" {
   137  		logger.Warningf("developer feature flags enabled: %s", flags)
   138  	}
   139  
   140  	network.InitializeFromConfig(agentConfig)
   141  	a.runner.StartWorker("api", a.APIWorkers)
   142  	err := cmdutil.AgentDone(logger, a.runner.Wait())
   143  	a.tomb.Kill(err)
   144  	return err
   145  }
   146  
   147  func (a *UnitAgent) APIWorkers() (_ worker.Worker, err error) {
   148  	agentConfig := a.CurrentConfig()
   149  	dataDir := agentConfig.DataDir()
   150  	hookLock, err := cmdutil.HookExecutionLock(dataDir)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  	st, entity, err := OpenAPIState(agentConfig, a)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	unitTag, err := names.ParseUnitTag(entity.Tag())
   159  	if err != nil {
   160  		return nil, errors.Trace(err)
   161  	}
   162  	// Ensure that the environment uuid is stored in the agent config.
   163  	// Luckily the API has it recorded for us after we connect.
   164  	if agentConfig.Environment().Id() == "" {
   165  		err := a.ChangeConfig(func(setter agent.ConfigSetter) error {
   166  			environTag, err := st.EnvironTag()
   167  			if err != nil {
   168  				return errors.Annotate(err, "no environment uuid set on api")
   169  			}
   170  
   171  			return setter.Migrate(agent.MigrateParams{
   172  				Environment: environTag,
   173  			})
   174  		})
   175  		if err != nil {
   176  			logger.Warningf("unable to save environment uuid: %v", err)
   177  			// Not really fatal, just annoying.
   178  		}
   179  	}
   180  
   181  	defer func() {
   182  		if err != nil {
   183  			st.Close()
   184  			reportClosedUnitAPI(st)
   185  		}
   186  	}()
   187  
   188  	// Before starting any workers, ensure we record the Juju version this unit
   189  	// agent is running.
   190  	currentTools := &tools.Tools{Version: version.Current}
   191  	apiStateUpgrader := a.getUpgrader(st)
   192  	if err := apiStateUpgrader.SetVersion(agentConfig.Tag().String(), currentTools.Version); err != nil {
   193  		return nil, errors.Annotate(err, "cannot set unit agent version")
   194  	}
   195  
   196  	runner := worker.NewRunner(cmdutil.ConnectionIsFatal(logger, st), cmdutil.MoreImportant)
   197  	// start proxyupdater first to ensure proxy settings are correct
   198  	runner.StartWorker("proxyupdater", func() (worker.Worker, error) {
   199  		return proxyupdater.New(st.Environment(), false), nil
   200  	})
   201  	runner.StartWorker("upgrader", func() (worker.Worker, error) {
   202  		return upgrader.NewAgentUpgrader(
   203  			st.Upgrader(),
   204  			agentConfig,
   205  			agentConfig.UpgradedToVersion(),
   206  			func() bool { return false },
   207  			a.initialAgentUpgradeCheckComplete,
   208  		), nil
   209  	})
   210  	runner.StartWorker("logger", func() (worker.Worker, error) {
   211  		return workerlogger.NewLogger(st.Logger(), agentConfig), nil
   212  	})
   213  	runner.StartWorker("uniter", func() (worker.Worker, error) {
   214  		uniterFacade, err := st.Uniter()
   215  		if err != nil {
   216  			return nil, errors.Trace(err)
   217  		}
   218  		uniterParams := uniter.UniterParams{
   219  			uniterFacade,
   220  			unitTag,
   221  			leadership.NewClient(st),
   222  			dataDir,
   223  			hookLock,
   224  			uniter.NewMetricsTimerChooser(),
   225  			uniter.NewUpdateStatusTimer(),
   226  		}
   227  		return uniter.NewUniter(&uniterParams), nil
   228  	})
   229  
   230  	runner.StartWorker("apiaddressupdater", func() (worker.Worker, error) {
   231  		uniterFacade, err := st.Uniter()
   232  		if err != nil {
   233  			return nil, errors.Trace(err)
   234  		}
   235  		return apiaddressupdater.NewAPIAddressUpdater(uniterFacade, a), nil
   236  	})
   237  	runner.StartWorker("rsyslog", func() (worker.Worker, error) {
   238  		return cmdutil.NewRsyslogConfigWorker(st.Rsyslog(), agentConfig, rsyslog.RsyslogModeForwarding)
   239  	})
   240  	return cmdutil.NewCloseWorker(logger, runner, st), nil
   241  }
   242  
   243  func (a *UnitAgent) Tag() names.Tag {
   244  	return names.NewUnitTag(a.UnitName)
   245  }