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