github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/hostkeyreporter/worker.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package hostkeyreporter
     5  
     6  import (
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"gopkg.in/tomb.v1"
    14  
    15  	"github.com/juju/juju/worker"
    16  	"github.com/juju/juju/worker/dependency"
    17  )
    18  
    19  var logger = loggo.GetLogger("juju.worker.hostkeyreporter")
    20  
    21  // Facade exposes controller functionality to a Worker.
    22  type Facade interface {
    23  	ReportKeys(machineId string, publicKeys []string) error
    24  }
    25  
    26  // Config defines the parameters of the hostkeyreporter worker.
    27  type Config struct {
    28  	Facade    Facade
    29  	MachineId string
    30  	RootDir   string
    31  }
    32  
    33  // Validate returns an error if Config cannot drive a hostkeyreporter.
    34  func (config Config) Validate() error {
    35  	if config.Facade == nil {
    36  		return errors.NotValidf("nil Facade")
    37  	}
    38  	if config.MachineId == "" {
    39  		return errors.NotValidf("empty MachineId")
    40  	}
    41  	return nil
    42  }
    43  
    44  // New returns a Worker backed by config, or an error.
    45  func New(config Config) (worker.Worker, error) {
    46  	if err := config.Validate(); err != nil {
    47  		return nil, errors.Trace(err)
    48  	}
    49  	w := &hostkeyreporter{config: config}
    50  	go func() {
    51  		defer w.tomb.Done()
    52  		w.tomb.Kill(w.run())
    53  	}()
    54  	return w, nil
    55  }
    56  
    57  // Worker waits for a model migration to be active, then locks down the
    58  // configured fortress and implements the migration.
    59  type hostkeyreporter struct {
    60  	tomb   tomb.Tomb
    61  	config Config
    62  }
    63  
    64  // Kill implements worker.Worker.
    65  func (w *hostkeyreporter) Kill() {
    66  	w.tomb.Kill(nil)
    67  }
    68  
    69  // Wait implements worker.Worker.
    70  func (w *hostkeyreporter) Wait() error {
    71  	return w.tomb.Wait()
    72  }
    73  
    74  func (w *hostkeyreporter) run() error {
    75  	keys, err := w.readSSHKeys()
    76  	if err != nil {
    77  		return errors.Trace(err)
    78  	}
    79  	if len(keys) < 1 {
    80  		return errors.New("no SSH host keys found")
    81  	}
    82  	err = w.config.Facade.ReportKeys(w.config.MachineId, keys)
    83  	if err != nil {
    84  		return errors.Trace(err)
    85  	}
    86  	logger.Debugf("%d SSH host keys reported for machine %s", len(keys), w.config.MachineId)
    87  	return dependency.ErrUninstall
    88  }
    89  
    90  func (w *hostkeyreporter) readSSHKeys() ([]string, error) {
    91  	sshDir := w.sshDir()
    92  	_, err := os.Stat(sshDir)
    93  	if os.IsNotExist(err) {
    94  		logger.Errorf("%s doesn't exist - giving up", sshDir)
    95  		return nil, dependency.ErrUninstall
    96  	}
    97  	if err != nil {
    98  		return nil, errors.Trace(err)
    99  	}
   100  
   101  	filenames, err := filepath.Glob(sshDir + "/ssh_host_*_key.pub")
   102  	if err != nil {
   103  		return nil, errors.Trace(err)
   104  	}
   105  	keys := make([]string, 0, len(filenames))
   106  	for _, filename := range filenames {
   107  		key, err := ioutil.ReadFile(filename)
   108  		if err != nil {
   109  			logger.Debugf("unable to read SSH host key (skipping): %v", err)
   110  			continue
   111  		}
   112  		keys = append(keys, string(key))
   113  	}
   114  	return keys, nil
   115  }
   116  
   117  func (w *hostkeyreporter) sshDir() string {
   118  	return filepath.Join(w.config.RootDir, "etc", "ssh")
   119  }