github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/certupdater/certupdater.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package certupdater
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  
    10  	"github.com/juju/juju/api/watcher"
    11  	"github.com/juju/juju/apiserver/params"
    12  	"github.com/juju/juju/environs/config"
    13  	"github.com/juju/juju/network"
    14  	"github.com/juju/juju/state"
    15  	"github.com/juju/juju/worker"
    16  )
    17  
    18  var logger = loggo.GetLogger("juju.worker.certupdater")
    19  
    20  // CertificateUpdater is responsible for generating state server certificates.
    21  //
    22  // In practice, CertificateUpdater is used by a state server's machine agent to watch
    23  // that server's machines addresses in state, and write a new certificate to the
    24  // agent's config file.
    25  type CertificateUpdater struct {
    26  	addressWatcher AddressWatcher
    27  	getter         StateServingInfoGetter
    28  	setter         StateServingInfoSetter
    29  	configGetter   EnvironConfigGetter
    30  	certChanged    chan params.StateServingInfo
    31  }
    32  
    33  // AddressWatcher is an interface that is provided to NewCertificateUpdater
    34  // which can be used to watch for machine address changes.
    35  type AddressWatcher interface {
    36  	WatchAddresses() state.NotifyWatcher
    37  	Addresses() (addresses []network.Address)
    38  }
    39  
    40  // EnvironConfigGetter is an interface that is provided to NewCertificateUpdater
    41  // which can be used to get environment config.
    42  type EnvironConfigGetter interface {
    43  	EnvironConfig() (*config.Config, error)
    44  }
    45  
    46  // StateServingInfoGetter is an interface that is provided to NewCertificateUpdater
    47  // whose StateServingInfo method will be invoked to get state serving info.
    48  type StateServingInfoGetter interface {
    49  	StateServingInfo() (params.StateServingInfo, bool)
    50  }
    51  
    52  // StateServingInfoSetter defines a function that is called to set a
    53  // StateServingInfo value with a newly generated certificate.
    54  type StateServingInfoSetter func(info params.StateServingInfo) error
    55  
    56  // NewCertificateUpdater returns a worker.Worker that watches for changes to
    57  // machine addresses and then generates a new state server certificate with those
    58  // addresses in the certificate's SAN value.
    59  func NewCertificateUpdater(addressWatcher AddressWatcher, getter StateServingInfoGetter,
    60  	configGetter EnvironConfigGetter, setter StateServingInfoSetter, certChanged chan params.StateServingInfo,
    61  ) worker.Worker {
    62  	return worker.NewNotifyWorker(&CertificateUpdater{
    63  		addressWatcher: addressWatcher,
    64  		configGetter:   configGetter,
    65  		getter:         getter,
    66  		setter:         setter,
    67  		certChanged:    certChanged,
    68  	})
    69  }
    70  
    71  // SetUp is defined on the NotifyWatchHandler interface.
    72  func (c *CertificateUpdater) SetUp() (watcher.NotifyWatcher, error) {
    73  	return c.addressWatcher.WatchAddresses(), nil
    74  }
    75  
    76  // Handle is defined on the NotifyWatchHandler interface.
    77  func (c *CertificateUpdater) Handle() error {
    78  	addresses := c.addressWatcher.Addresses()
    79  	logger.Debugf("new machine addresses: %v", addresses)
    80  
    81  	// Older Juju deployments will not have the CA cert private key
    82  	// available.
    83  	stateInfo, ok := c.getter.StateServingInfo()
    84  	if !ok {
    85  		logger.Warningf("no state serving info, cannot regenerate server certificate")
    86  		return nil
    87  	}
    88  	caPrivateKey := stateInfo.CAPrivateKey
    89  	if caPrivateKey == "" {
    90  		logger.Warningf("no CA cert private key, cannot regenerate server certificate")
    91  		return nil
    92  	}
    93  	// Grab the env config and update a copy with ca cert private key.
    94  	envConfig, err := c.configGetter.EnvironConfig()
    95  	if err != nil {
    96  		return errors.Annotate(err, "cannot read environment config")
    97  	}
    98  	envConfig, err = envConfig.Apply(map[string]interface{}{"ca-private-key": caPrivateKey})
    99  	if err != nil {
   100  		return errors.Annotate(err, "cannot add CA private key to environment config")
   101  	}
   102  
   103  	// For backwards compatibility, we must include "juju-apiserver" as a
   104  	// hostname as that is what clients specify as the hostname for verification.
   105  	// We also explicitly include localhost.
   106  	serverAddrs := []string{"localhost", "juju-apiserver"}
   107  	for _, addr := range addresses {
   108  		if addr.Value == "localhost" {
   109  			continue
   110  		}
   111  		serverAddrs = append(serverAddrs, addr.Value)
   112  	}
   113  
   114  	// Generate a new state server certificate with the machine addresses in the SAN value.
   115  	newCert, newKey, err := envConfig.GenerateStateServerCertAndKey(serverAddrs)
   116  	if err != nil {
   117  		return errors.Annotate(err, "cannot generate state server certificate")
   118  	}
   119  	stateInfo.Cert = string(newCert)
   120  	stateInfo.PrivateKey = string(newKey)
   121  	err = c.setter(stateInfo)
   122  	if err != nil {
   123  		return errors.Annotate(err, "cannot write agent config")
   124  	}
   125  	logger.Infof("State Server cerificate addresses updated to %q", addresses)
   126  	return nil
   127  }
   128  
   129  // TearDown is defined on the NotifyWatchHandler interface.
   130  func (c *CertificateUpdater) TearDown() error {
   131  	close(c.certChanged)
   132  	return nil
   133  }