github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/utils"
    14  	"github.com/juju/utils/exec"
    15  	"github.com/juju/utils/os"
    16  	"github.com/juju/utils/packaging/commands"
    17  	"github.com/juju/utils/packaging/config"
    18  	proxyutils "github.com/juju/utils/proxy"
    19  	"github.com/juju/utils/series"
    20  
    21  	"github.com/juju/juju/watcher"
    22  	"github.com/juju/juju/worker"
    23  )
    24  
    25  var (
    26  	logger = loggo.GetLogger("juju.worker.proxyupdater")
    27  )
    28  
    29  type Config struct {
    30  	Directory      string
    31  	RegistryPath   string
    32  	Filename       string
    33  	API            API
    34  	ExternalUpdate func(proxyutils.Settings) error
    35  }
    36  
    37  // API is an interface that is provided to New
    38  // which can be used to fetch the API host ports
    39  type API interface {
    40  	ProxyConfig() (proxyutils.Settings, proxyutils.Settings, error)
    41  	WatchForProxyConfigAndAPIHostPortChanges() (watcher.NotifyWatcher, error)
    42  }
    43  
    44  // proxyWorker is responsible for monitoring the juju environment
    45  // configuration and making changes on the physical (or virtual) machine as
    46  // necessary to match the environment changes.  Examples of these types of
    47  // changes are apt proxy configuration and the juju proxies stored in the juju
    48  // proxy file.
    49  type proxyWorker struct {
    50  	aptProxy proxyutils.Settings
    51  	proxy    proxyutils.Settings
    52  
    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  	config Config
    63  }
    64  
    65  // NewWorker returns a worker.Worker that updates proxy environment variables for the
    66  // process and for the whole machine.
    67  var NewWorker = func(config Config) (worker.Worker, error) {
    68  	envWorker := &proxyWorker{
    69  		first:  true,
    70  		config: config,
    71  	}
    72  	w, err := watcher.NewNotifyWorker(watcher.NotifyConfig{
    73  		Handler: envWorker,
    74  	})
    75  	if err != nil {
    76  		return nil, errors.Trace(err)
    77  	}
    78  	return w, nil
    79  }
    80  
    81  func (w *proxyWorker) writeEnvironmentFile() error {
    82  	// Writing the environment file is handled by executing the script:
    83  	//
    84  	// On cloud-instance ubuntu images, the ubuntu user is uid 1000, but in
    85  	// the situation where the ubuntu user has been created as a part of the
    86  	// manual provisioning process, the user will exist, and will not have the
    87  	// same uid/gid as the default cloud image.
    88  	//
    89  	// It is easier to shell out to check, and is also the same way that the file
    90  	// is written in the cloud-init process, so consistency FTW.
    91  	filePath := path.Join(w.config.Directory, w.config.Filename)
    92  	result, err := exec.RunCommands(exec.RunParams{
    93  		Commands: fmt.Sprintf(
    94  			`[ -e %s ] && (printf '%%s\n' %s > %s && chown ubuntu:ubuntu %s)`,
    95  			w.config.Directory,
    96  			utils.ShQuote(w.proxy.AsScriptEnvironment()),
    97  			filePath, filePath),
    98  		WorkingDir: w.config.Directory,
    99  	})
   100  
   101  	if err != nil {
   102  		return err
   103  	}
   104  	if result.Code != 0 {
   105  		logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr)
   106  	}
   107  	return nil
   108  }
   109  
   110  func (w *proxyWorker) writeEnvironmentToRegistry() error {
   111  	// On windows we write the proxy settings to the registry.
   112  	setProxyScript := `$value_path = "%s"
   113      $new_proxy = "%s"
   114      $proxy_val = Get-ItemProperty -Path $value_path -Name ProxySettings
   115      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 }
   116      `
   117  
   118  	if w.config.RegistryPath == "" {
   119  		err := fmt.Errorf("config.RegistryPath is empty")
   120  		logger.Errorf("writeEnvironmentToRegistry couldn't write proxy settings to registry: %s", err)
   121  		return err
   122  	}
   123  
   124  	result, err := exec.RunCommands(exec.RunParams{
   125  		Commands: fmt.Sprintf(
   126  			setProxyScript,
   127  			w.config.RegistryPath,
   128  			w.proxy.Http),
   129  	})
   130  	if err != nil {
   131  		return err
   132  	}
   133  	if result.Code != 0 {
   134  		logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr)
   135  	}
   136  	return nil
   137  }
   138  
   139  func (w *proxyWorker) writeEnvironment() error {
   140  	switch os.HostOS() {
   141  	case os.Windows:
   142  		return w.writeEnvironmentToRegistry()
   143  	default:
   144  		return w.writeEnvironmentFile()
   145  	}
   146  }
   147  
   148  func (w *proxyWorker) handleProxyValues(proxySettings proxyutils.Settings) {
   149  	proxySettings.SetEnvironmentValues()
   150  	if proxySettings != w.proxy || w.first {
   151  		logger.Debugf("new proxy settings %#v", proxySettings)
   152  		w.proxy = proxySettings
   153  		if err := w.writeEnvironment(); err != nil {
   154  			// It isn't really fatal, but we should record it.
   155  			logger.Errorf("error writing proxy environment file: %v", err)
   156  		}
   157  		if externalFunc := w.config.ExternalUpdate; externalFunc != nil {
   158  			if err := externalFunc(proxySettings); err != nil {
   159  				// It isn't really fatal, but we should record it.
   160  				logger.Errorf("%v", err)
   161  			}
   162  		}
   163  	}
   164  }
   165  
   166  // getPackageCommander is a helper function which returns the
   167  // package commands implementation for the current system.
   168  func getPackageCommander() (commands.PackageCommander, error) {
   169  	return commands.NewPackageCommander(series.HostSeries())
   170  }
   171  
   172  func (w *proxyWorker) handleAptProxyValues(aptSettings proxyutils.Settings) error {
   173  	if aptSettings != w.aptProxy || w.first {
   174  		logger.Debugf("new apt proxy settings %#v", aptSettings)
   175  		paccmder, err := getPackageCommander()
   176  		if err != nil {
   177  			return err
   178  		}
   179  		w.aptProxy = aptSettings
   180  
   181  		// Always finish with a new line.
   182  		content := paccmder.ProxyConfigContents(w.aptProxy) + "\n"
   183  		err = ioutil.WriteFile(config.AptProxyConfigFile, []byte(content), 0644)
   184  		if err != nil {
   185  			// It isn't really fatal, but we should record it.
   186  			logger.Errorf("error writing apt proxy config file: %v", err)
   187  		}
   188  	}
   189  	return nil
   190  }
   191  
   192  func (w *proxyWorker) onChange() error {
   193  	proxySettings, APTProxySettings, err := w.config.API.ProxyConfig()
   194  	if err != nil {
   195  		return err
   196  	}
   197  
   198  	w.handleProxyValues(proxySettings)
   199  	return w.handleAptProxyValues(APTProxySettings)
   200  }
   201  
   202  // SetUp is defined on the worker.NotifyWatchHandler interface.
   203  func (w *proxyWorker) SetUp() (watcher.NotifyWatcher, error) {
   204  	// We need to set this up initially as the NotifyWorker sucks up the first
   205  	// event.
   206  	err := w.onChange()
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	w.first = false
   211  	return w.config.API.WatchForProxyConfigAndAPIHostPortChanges()
   212  }
   213  
   214  // Handle is defined on the worker.NotifyWatchHandler interface.
   215  func (w *proxyWorker) Handle(_ <-chan struct{}) error {
   216  	return w.onChange()
   217  }
   218  
   219  // TearDown is defined on the worker.NotifyWatchHandler interface.
   220  func (w *proxyWorker) TearDown() error {
   221  	// Nothing to cleanup, only state is the watcher
   222  	return nil
   223  }