github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/authenticationworker/worker_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package authenticationworker_test
     5  
     6  import (
     7  	"runtime"
     8  	"strings"
     9  	"time"
    10  
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils/ssh"
    13  	sshtesting "github.com/juju/utils/ssh/testing"
    14  	gc "gopkg.in/check.v1"
    15  	"gopkg.in/juju/names.v2"
    16  	"gopkg.in/juju/worker.v1"
    17  
    18  	"github.com/juju/juju/agent"
    19  	"github.com/juju/juju/api"
    20  	"github.com/juju/juju/api/keyupdater"
    21  	jujutesting "github.com/juju/juju/juju/testing"
    22  	"github.com/juju/juju/state"
    23  	coretesting "github.com/juju/juju/testing"
    24  	"github.com/juju/juju/worker/authenticationworker"
    25  )
    26  
    27  type workerSuite struct {
    28  	jujutesting.JujuConnSuite
    29  	stateMachine  *state.Machine
    30  	machine       *state.Machine
    31  	keyupdaterAPI *keyupdater.State
    32  
    33  	existingEnvKey string
    34  	existingKeys   []string
    35  }
    36  
    37  var _ = gc.Suite(&workerSuite{})
    38  
    39  func (s *workerSuite) SetUpTest(c *gc.C) {
    40  	//TODO(bogdanteleaga): Fix this on windows
    41  	if runtime.GOOS == "windows" {
    42  		c.Skip("bug 1403084: authentication worker not implemented yet on windows")
    43  	}
    44  	s.JujuConnSuite.SetUpTest(c)
    45  	// Default ssh user is currently "ubuntu".
    46  	c.Assert(authenticationworker.SSHUser, gc.Equals, "ubuntu")
    47  	// Set the ssh user to empty (the current user) as required by the test infrastructure.
    48  	s.PatchValue(&authenticationworker.SSHUser, "")
    49  
    50  	// Replace the default dummy key in the test environment with a valid one.
    51  	// This will be added to the ssh authorised keys when the agent starts.
    52  	s.setAuthorisedKeys(c, sshtesting.ValidKeyOne.Key+" firstuser@host")
    53  	// Record the existing key with its prefix for testing later.
    54  	s.existingEnvKey = sshtesting.ValidKeyOne.Key + " Juju:firstuser@host"
    55  
    56  	// Set up an existing key (which is not in the environment) in the ssh authorised_keys file.
    57  	s.existingKeys = []string{sshtesting.ValidKeyTwo.Key + " existinguser@host"}
    58  	err := ssh.AddKeys(authenticationworker.SSHUser, s.existingKeys...)
    59  	c.Assert(err, jc.ErrorIsNil)
    60  
    61  	var apiRoot api.Connection
    62  	apiRoot, s.machine = s.OpenAPIAsNewMachine(c)
    63  	c.Assert(apiRoot, gc.NotNil)
    64  	s.keyupdaterAPI = keyupdater.NewState(apiRoot)
    65  	c.Assert(s.keyupdaterAPI, gc.NotNil)
    66  }
    67  
    68  func stop(c *gc.C, w worker.Worker) {
    69  	c.Assert(worker.Stop(w), gc.IsNil)
    70  }
    71  
    72  type mockConfig struct {
    73  	agent.Config
    74  	c   *gc.C
    75  	tag names.Tag
    76  }
    77  
    78  func (mock *mockConfig) Tag() names.Tag {
    79  	return mock.tag
    80  }
    81  
    82  func agentConfig(c *gc.C, tag names.MachineTag) *mockConfig {
    83  	return &mockConfig{c: c, tag: tag}
    84  }
    85  
    86  func (s *workerSuite) setAuthorisedKeys(c *gc.C, keys ...string) {
    87  	keyStr := strings.Join(keys, "\n")
    88  	err := s.Model.UpdateModelConfig(map[string]interface{}{"authorized-keys": keyStr}, nil)
    89  	c.Assert(err, jc.ErrorIsNil)
    90  	s.BackingState.StartSync()
    91  }
    92  
    93  func (s *workerSuite) waitSSHKeys(c *gc.C, expected []string) {
    94  	timeout := time.After(coretesting.LongWait)
    95  	for {
    96  		select {
    97  		case <-timeout:
    98  			c.Fatalf("timeout while waiting for authoirsed ssh keys to change")
    99  		case <-time.After(coretesting.ShortWait):
   100  			keys, err := ssh.ListKeys(authenticationworker.SSHUser, ssh.FullKeys)
   101  			c.Assert(err, jc.ErrorIsNil)
   102  			keysStr := strings.Join(keys, "\n")
   103  			expectedStr := strings.Join(expected, "\n")
   104  			if expectedStr != keysStr {
   105  				continue
   106  			}
   107  			return
   108  		}
   109  	}
   110  }
   111  
   112  func (s *workerSuite) TestKeyUpdateRetainsExisting(c *gc.C) {
   113  	authWorker, err := authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag)))
   114  	c.Assert(err, jc.ErrorIsNil)
   115  	defer stop(c, authWorker)
   116  
   117  	newKey := sshtesting.ValidKeyThree.Key + " user@host"
   118  	s.setAuthorisedKeys(c, newKey)
   119  	newKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:user@host"
   120  	s.waitSSHKeys(c, append(s.existingKeys, newKeyWithCommentPrefix))
   121  }
   122  
   123  func (s *workerSuite) TestNewKeysInJujuAreSavedOnStartup(c *gc.C) {
   124  	newKey := sshtesting.ValidKeyThree.Key + " user@host"
   125  	s.setAuthorisedKeys(c, newKey)
   126  
   127  	authWorker, err := authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag)))
   128  	c.Assert(err, jc.ErrorIsNil)
   129  	defer stop(c, authWorker)
   130  
   131  	newKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:user@host"
   132  	s.waitSSHKeys(c, append(s.existingKeys, newKeyWithCommentPrefix))
   133  }
   134  
   135  func (s *workerSuite) TestDeleteKey(c *gc.C) {
   136  	authWorker, err := authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag)))
   137  	c.Assert(err, jc.ErrorIsNil)
   138  	defer stop(c, authWorker)
   139  
   140  	// Add another key
   141  	anotherKey := sshtesting.ValidKeyThree.Key + " another@host"
   142  	s.setAuthorisedKeys(c, s.existingEnvKey, anotherKey)
   143  	anotherKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:another@host"
   144  	s.waitSSHKeys(c, append(s.existingKeys, s.existingEnvKey, anotherKeyWithCommentPrefix))
   145  
   146  	// Delete the original key and check anotherKey plus the existing keys remain.
   147  	s.setAuthorisedKeys(c, anotherKey)
   148  	s.waitSSHKeys(c, append(s.existingKeys, anotherKeyWithCommentPrefix))
   149  }
   150  
   151  func (s *workerSuite) TestMultipleChanges(c *gc.C) {
   152  	authWorker, err := authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag)))
   153  	c.Assert(err, jc.ErrorIsNil)
   154  	defer stop(c, authWorker)
   155  	s.waitSSHKeys(c, append(s.existingKeys, s.existingEnvKey))
   156  
   157  	// Perform a set to add a key and delete a key.
   158  	// added: key 3
   159  	// deleted: key 1 (existing env key)
   160  	s.setAuthorisedKeys(c, sshtesting.ValidKeyThree.Key+" yetanother@host")
   161  	yetAnotherKeyWithComment := sshtesting.ValidKeyThree.Key + " Juju:yetanother@host"
   162  	s.waitSSHKeys(c, append(s.existingKeys, yetAnotherKeyWithComment))
   163  }
   164  
   165  func (s *workerSuite) TestWorkerRestart(c *gc.C) {
   166  	authWorker, err := authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag)))
   167  	c.Assert(err, jc.ErrorIsNil)
   168  	defer stop(c, authWorker)
   169  	s.waitSSHKeys(c, append(s.existingKeys, s.existingEnvKey))
   170  
   171  	// Stop the worker and delete and add keys from the environment while it is down.
   172  	// added: key 3
   173  	// deleted: key 1 (existing env key)
   174  	stop(c, authWorker)
   175  	s.setAuthorisedKeys(c, sshtesting.ValidKeyThree.Key+" yetanother@host")
   176  
   177  	// Restart the worker and check that the ssh auth keys are as expected.
   178  	authWorker, err = authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag)))
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	defer stop(c, authWorker)
   181  
   182  	yetAnotherKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:yetanother@host"
   183  	s.waitSSHKeys(c, append(s.existingKeys, yetAnotherKeyWithCommentPrefix))
   184  }