github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/proxyupdater/proxyupdater.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package proxyupdater
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"path"
    10  
    11  	"github.com/juju/loggo"
    12  	"github.com/juju/utils"
    13  	"github.com/juju/utils/exec"
    14  	"github.com/juju/utils/os"
    15  	"github.com/juju/utils/packaging/commands"
    16  	"github.com/juju/utils/packaging/config"
    17  	proxyutils "github.com/juju/utils/proxy"
    18  	"github.com/juju/utils/series"
    19  
    20  	"github.com/juju/juju/api/environment"
    21  	"github.com/juju/juju/api/watcher"
    22  	"github.com/juju/juju/worker"
    23  )
    24  
    25  var (
    26  	logger = loggo.GetLogger("juju.worker.proxyupdater")
    27  
    28  	// ProxyDirectory is the directory containing the proxy file that contains
    29  	// the environment settings for the proxies based on the environment
    30  	// config values.
    31  	ProxyDirectory = "/home/ubuntu"
    32  
    33  	// proxySettingsRegistryPath is the Windows registry path where the proxy settings are saved
    34  	proxySettingsRegistryPath = `HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings`
    35  
    36  	// ProxyFile is the name of the file to be stored in the ProxyDirectory.
    37  	ProxyFile = ".juju-proxy"
    38  
    39  	// Started is a function that is called when the worker has started.
    40  	Started = func() {}
    41  )
    42  
    43  // proxyWorker is responsible for monitoring the juju environment
    44  // configuration and making changes on the physical (or virtual) machine as
    45  // necessary to match the environment changes.  Examples of these types of
    46  // changes are apt proxy configuration and the juju proxies stored in the juju
    47  // proxy file.
    48  type proxyWorker struct {
    49  	api      *environment.Facade
    50  	aptProxy proxyutils.Settings
    51  	proxy    proxyutils.Settings
    52  
    53  	writeSystemFiles bool
    54  	// The whole point of the first value is to make sure that the the files
    55  	// are written out the first time through, even if they are the same as
    56  	// "last" time, as the initial value for last time is the zeroed struct.
    57  	// There is the possibility that the files exist on disk with old
    58  	// settings, and the environment has been updated to now not have them. We
    59  	// need to make sure that the disk reflects the environment, so the first
    60  	// time through, even if the proxies are empty, we write the files to
    61  	// disk.
    62  	first bool
    63  }
    64  
    65  var _ worker.NotifyWatchHandler = (*proxyWorker)(nil)
    66  
    67  // New returns a worker.Worker that updates proxy environment variables for the
    68  // process; and, if writeSystemFiles is true, for the whole machine.
    69  var New = func(api *environment.Facade, writeSystemFiles bool) worker.Worker {
    70  	logger.Debugf("write system files: %v", writeSystemFiles)
    71  	envWorker := &proxyWorker{
    72  		api:              api,
    73  		writeSystemFiles: writeSystemFiles,
    74  		first:            true,
    75  	}
    76  	return worker.NewNotifyWorker(envWorker)
    77  }
    78  
    79  func (w *proxyWorker) writeEnvironmentFile() error {
    80  	// Writing the environment file is handled by executing the script for two
    81  	// primary reasons:
    82  	//
    83  	// 1: In order to have the local provider specify the environment settings
    84  	// for the machine agent running on the host, this worker needs to run,
    85  	// but it shouldn't be touching any files on the disk.  If however there is
    86  	// an ubuntu user, it will. This shouldn't be a problem.
    87  	//
    88  	// 2: On cloud-instance ubuntu images, the ubuntu user is uid 1000, but in
    89  	// the situation where the ubuntu user has been created as a part of the
    90  	// manual provisioning process, the user will exist, and will not have the
    91  	// same uid/gid as the default cloud image.
    92  	//
    93  	// It is easier to shell out to check both these things, and is also the
    94  	// same way that the file is written in the cloud-init process, so
    95  	// consistency FTW.
    96  	filePath := path.Join(ProxyDirectory, ProxyFile)
    97  	result, err := exec.RunCommands(exec.RunParams{
    98  		Commands: fmt.Sprintf(
    99  			`[ -e %s ] && (printf '%%s\n' %s > %s && chown ubuntu:ubuntu %s)`,
   100  			ProxyDirectory,
   101  			utils.ShQuote(w.proxy.AsScriptEnvironment()),
   102  			filePath, filePath),
   103  		WorkingDir: ProxyDirectory,
   104  	})
   105  	if err != nil {
   106  		return err
   107  	}
   108  	if result.Code != 0 {
   109  		logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr)
   110  	}
   111  	return nil
   112  }
   113  
   114  func (w *proxyWorker) writeEnvironmentToRegistry() error {
   115  	// On windows we write the proxy settings to the registry.
   116  	setProxyScript := `$value_path = "%s"
   117      $new_proxy = "%s"
   118      $proxy_val = Get-ItemProperty -Path $value_path -Name ProxySettings
   119      if ($? -eq $false){ New-ItemProperty -Path $value_path -Name ProxySettings -PropertyType String -Value $new_proxy }else{ Set-ItemProperty -Path $value_path -Name ProxySettings -Value $new_proxy }
   120      `
   121  	result, err := exec.RunCommands(exec.RunParams{
   122  		Commands: fmt.Sprintf(
   123  			setProxyScript,
   124  			proxySettingsRegistryPath,
   125  			w.proxy.Http),
   126  	})
   127  	if err != nil {
   128  		return err
   129  	}
   130  	if result.Code != 0 {
   131  		logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr)
   132  	}
   133  	return nil
   134  }
   135  
   136  func (w *proxyWorker) writeEnvironment() error {
   137  	// TODO(dfc) this should be replaced with a switch on os.HostOS()
   138  	osystem, err := series.GetOSFromSeries(series.HostSeries())
   139  	if err != nil {
   140  		return err
   141  	}
   142  	switch osystem {
   143  	case os.Windows:
   144  		return w.writeEnvironmentToRegistry()
   145  	default:
   146  		return w.writeEnvironmentFile()
   147  	}
   148  }
   149  
   150  func (w *proxyWorker) handleProxyValues(proxySettings proxyutils.Settings) {
   151  	proxySettings.SetEnvironmentValues()
   152  	if proxySettings != w.proxy || w.first {
   153  		logger.Debugf("new proxy settings %#v", proxySettings)
   154  		w.proxy = proxySettings
   155  		if w.writeSystemFiles {
   156  			if err := w.writeEnvironment(); err != nil {
   157  				// It isn't really fatal, but we should record it.
   158  				logger.Errorf("error writing proxy environment file: %v", err)
   159  			}
   160  		}
   161  	}
   162  }
   163  
   164  // getPackageCommander is a helper function which returns the
   165  // package commands implementation for the current system.
   166  func getPackageCommander() (commands.PackageCommander, error) {
   167  	return commands.NewPackageCommander(series.HostSeries())
   168  }
   169  
   170  func (w *proxyWorker) handleAptProxyValues(aptSettings proxyutils.Settings) error {
   171  	if w.writeSystemFiles && (aptSettings != w.aptProxy || w.first) {
   172  		logger.Debugf("new apt proxy settings %#v", aptSettings)
   173  		paccmder, err := getPackageCommander()
   174  		if err != nil {
   175  			return err
   176  		}
   177  		w.aptProxy = aptSettings
   178  
   179  		// Always finish with a new line.
   180  		content := paccmder.ProxyConfigContents(w.aptProxy) + "\n"
   181  		err = ioutil.WriteFile(config.AptProxyConfigFile, []byte(content), 0644)
   182  		if err != nil {
   183  			// It isn't really fatal, but we should record it.
   184  			logger.Errorf("error writing apt proxy config file: %v", err)
   185  		}
   186  	}
   187  	return nil
   188  }
   189  
   190  func (w *proxyWorker) onChange() error {
   191  	env, err := w.api.EnvironConfig()
   192  	if err != nil {
   193  		return err
   194  	}
   195  	w.handleProxyValues(env.ProxySettings())
   196  	err = w.handleAptProxyValues(env.AptProxySettings())
   197  	if err != nil {
   198  		return err
   199  	}
   200  	return nil
   201  }
   202  
   203  // SetUp is defined on the worker.NotifyWatchHandler interface.
   204  func (w *proxyWorker) SetUp() (watcher.NotifyWatcher, error) {
   205  	// We need to set this up initially as the NotifyWorker sucks up the first
   206  	// event.
   207  	err := w.onChange()
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  	w.first = false
   212  	Started()
   213  	return w.api.WatchForEnvironConfigChanges()
   214  }
   215  
   216  // Handle is defined on the worker.NotifyWatchHandler interface.
   217  func (w *proxyWorker) Handle(_ <-chan struct{}) error {
   218  	return w.onChange()
   219  }
   220  
   221  // TearDown is defined on the worker.NotifyWatchHandler interface.
   222  func (w *proxyWorker) TearDown() error {
   223  	// Nothing to cleanup, only state is the watcher
   224  	return nil
   225  }