github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"launchpad.net/tomb"
    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  	if _, err := os.Stat(sshDir); os.IsNotExist(err) {
    93  		logger.Warningf("%s doesn't exist - giving up", sshDir)
    94  		return nil, dependency.ErrUninstall
    95  	} else if err != nil {
    96  		return nil, errors.Trace(err)
    97  	}
    98  
    99  	filenames, err := filepath.Glob(sshDir + "/ssh_host_*_key.pub")
   100  	if err != nil {
   101  		return nil, errors.Trace(err)
   102  	}
   103  	keys := make([]string, 0, len(filenames))
   104  	for _, filename := range filenames {
   105  		key, err := ioutil.ReadFile(filename)
   106  		if err != nil {
   107  			logger.Warningf("unable to read SSH host key (skipping): %v", err)
   108  			continue
   109  		}
   110  		keys = append(keys, string(key))
   111  	}
   112  	return keys, nil
   113  }
   114  
   115  func (w *hostkeyreporter) sshDir() string {
   116  	return filepath.Join(w.config.RootDir, "etc", "ssh")
   117  }