launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/worker/machineenvironmentworker/machineenvironmentworker.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package machineenvironmentworker
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"path"
    10  
    11  	"github.com/loggo/loggo"
    12  
    13  	"launchpad.net/errgo/errors"
    14  	"launchpad.net/juju-core/agent"
    15  	"launchpad.net/juju-core/juju/osenv"
    16  	"launchpad.net/juju-core/names"
    17  	"launchpad.net/juju-core/provider"
    18  	"launchpad.net/juju-core/state/api/environment"
    19  	"launchpad.net/juju-core/state/api/watcher"
    20  	"launchpad.net/juju-core/utils"
    21  	"launchpad.net/juju-core/utils/exec"
    22  	"launchpad.net/juju-core/worker"
    23  )
    24  
    25  var (
    26  	logger = loggo.GetLogger("juju.worker.machineenvironment")
    27  
    28  	mask = errors.Mask
    29  
    30  	// ProxyDirectory is the directory containing the proxy file that contains
    31  	// the environment settings for the proxies based on the environment
    32  	// config values.
    33  	ProxyDirectory = "/home/ubuntu"
    34  
    35  	// ProxyFile is the name of the file to be stored in the ProxyDirectory.
    36  	ProxyFile = ".juju-proxy"
    37  
    38  	// Started is a function that is called when the worker has started.
    39  	Started = func() {}
    40  )
    41  
    42  // MachineEnvironmentWorker is responsible for monitoring the juju environment
    43  // configuration and making changes on the physical (or virtual) machine as
    44  // necessary to match the environment changes.  Examples of these types of
    45  // changes are apt proxy configuration and the juju proxies stored in the juju
    46  // proxy file.
    47  type MachineEnvironmentWorker struct {
    48  	api      *environment.Facade
    49  	aptProxy osenv.ProxySettings
    50  	proxy    osenv.ProxySettings
    51  
    52  	writeSystemFiles bool
    53  	// The whole point of the first value is to make sure that the the files
    54  	// are written out the first time through, even if they are the same as
    55  	// "last" time, as the initial value for last time is the zeroed struct.
    56  	// There is the possibility that the files exist on disk with old
    57  	// settings, and the environment has been updated to now not have them. We
    58  	// need to make sure that the disk reflects the environment, so the first
    59  	// time through, even if the proxies are empty, we write the files to
    60  	// disk.
    61  	first bool
    62  }
    63  
    64  var _ worker.NotifyWatchHandler = (*MachineEnvironmentWorker)(nil)
    65  
    66  // NewMachineEnvironmentWorker returns a worker.Worker that uses the notify
    67  // watcher returned from the setup.
    68  func NewMachineEnvironmentWorker(api *environment.Facade, agentConfig agent.Config) worker.Worker {
    69  	// We don't write out system files for the local provider on machine zero
    70  	// as that is the host machine.
    71  	writeSystemFiles := !(agentConfig.Tag() == names.MachineTag("0") &&
    72  		agentConfig.Value(agent.JujuProviderType) == provider.Local)
    73  	envWorker := &MachineEnvironmentWorker{
    74  		api:              api,
    75  		writeSystemFiles: writeSystemFiles,
    76  		first:            true,
    77  	}
    78  	return worker.NewNotifyWorker(envWorker)
    79  }
    80  
    81  func (w *MachineEnvironmentWorker) writeEnvironmentFile() error {
    82  	// Writing the environment file is handled by executing the script for two
    83  	// primary reasons:
    84  	//
    85  	// 1: In order to have the local provider specify the environment settings
    86  	// for the machine agent running on the host, this worker needs to run,
    87  	// but it shouldn't be touching any files on the disk.  If however there is
    88  	// an ubuntu user, it will. This shouldn't be a problem.
    89  	//
    90  	// 2: On cloud-instance ubuntu images, the ubuntu user is uid 1000, but in
    91  	// the situation where the ubuntu user has been created as a part of the
    92  	// manual provisioning process, the user will exist, and will not have the
    93  	// same uid/gid as the default cloud image.
    94  	//
    95  	// It is easier to shell out to check both these things, and is also the
    96  	// same way that the file is written in the cloud-init process, so
    97  	// consistency FTW.
    98  	filePath := path.Join(ProxyDirectory, ProxyFile)
    99  	result, err := exec.RunCommands(exec.RunParams{
   100  		Commands: fmt.Sprintf(
   101  			`[ -e %s ] && (printf '%%s\n' %s > %s && chown ubuntu:ubuntu %s)`,
   102  			ProxyDirectory,
   103  			utils.ShQuote(w.proxy.AsScriptEnvironment()),
   104  			filePath, filePath),
   105  		WorkingDir: ProxyDirectory,
   106  	})
   107  	if err != nil {
   108  		return mask(err)
   109  	}
   110  	if result.Code != 0 {
   111  		logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr)
   112  	}
   113  	return nil
   114  }
   115  
   116  func (w *MachineEnvironmentWorker) handleProxyValues(proxySettings osenv.ProxySettings) {
   117  	if proxySettings != w.proxy || w.first {
   118  		logger.Debugf("new proxy settings %#v", proxySettings)
   119  		w.proxy = proxySettings
   120  		w.proxy.SetEnvironmentValues()
   121  		if w.writeSystemFiles {
   122  			if err := w.writeEnvironmentFile(); err != nil {
   123  				// It isn't really fatal, but we should record it.
   124  				logger.Errorf("error writing proxy environment file: %v", err)
   125  			}
   126  		}
   127  	}
   128  }
   129  
   130  func (w *MachineEnvironmentWorker) handleAptProxyValues(aptSettings osenv.ProxySettings) {
   131  	if w.writeSystemFiles && (aptSettings != w.aptProxy || w.first) {
   132  		logger.Debugf("new apt proxy settings %#v", aptSettings)
   133  		w.aptProxy = aptSettings
   134  		// Always finish with a new line.
   135  		content := utils.AptProxyContent(w.aptProxy) + "\n"
   136  		err := ioutil.WriteFile(utils.AptConfFile, []byte(content), 0644)
   137  		if err != nil {
   138  			// It isn't really fatal, but we should record it.
   139  			logger.Errorf("error writing apt proxy config file: %v", err)
   140  		}
   141  	}
   142  }
   143  
   144  func (w *MachineEnvironmentWorker) onChange() error {
   145  	env, err := w.api.EnvironConfig()
   146  	if err != nil {
   147  		return mask(err)
   148  	}
   149  	w.handleProxyValues(env.ProxySettings())
   150  	w.handleAptProxyValues(env.AptProxySettings())
   151  	return nil
   152  }
   153  
   154  // SetUp is defined on the worker.NotifyWatchHandler interface.
   155  func (w *MachineEnvironmentWorker) SetUp() (watcher.NotifyWatcher, error) {
   156  	// We need to set this up initially as the NotifyWorker sucks up the first
   157  	// event.
   158  	err := w.onChange()
   159  	if err != nil {
   160  		return nil, mask(err)
   161  	}
   162  	w.first = false
   163  	Started()
   164  	return w.api.WatchForEnvironConfigChanges()
   165  }
   166  
   167  // Handle is defined on the worker.NotifyWatchHandler interface.
   168  func (w *MachineEnvironmentWorker) Handle() error {
   169  	return w.onChange()
   170  }
   171  
   172  // TearDown is defined on the worker.NotifyWatchHandler interface.
   173  func (w *MachineEnvironmentWorker) TearDown() error {
   174  	// Nothing to cleanup, only state is the watcher
   175  	return nil
   176  }