github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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  	"strings"
     8  	stdtesting "testing"
     9  	"time"
    10  
    11  	gc "launchpad.net/gocheck"
    12  
    13  	"launchpad.net/juju-core/agent"
    14  	jujutesting "launchpad.net/juju-core/juju/testing"
    15  	"launchpad.net/juju-core/state"
    16  	"launchpad.net/juju-core/state/api"
    17  	"launchpad.net/juju-core/state/api/keyupdater"
    18  	"launchpad.net/juju-core/state/testing"
    19  	coretesting "launchpad.net/juju-core/testing"
    20  	"launchpad.net/juju-core/utils/ssh"
    21  	sshtesting "launchpad.net/juju-core/utils/ssh/testing"
    22  	"launchpad.net/juju-core/worker"
    23  	"launchpad.net/juju-core/worker/authenticationworker"
    24  )
    25  
    26  // worstCase is used for timeouts when timing out
    27  // will fail the test. Raising this value should
    28  // not affect the overall running time of the tests
    29  // unless they fail.
    30  const worstCase = 5 * time.Second
    31  
    32  func TestAll(t *stdtesting.T) {
    33  	coretesting.MgoTestPackage(t)
    34  }
    35  
    36  var _ = gc.Suite(&workerSuite{})
    37  
    38  type workerSuite struct {
    39  	jujutesting.JujuConnSuite
    40  	stateMachine  *state.Machine
    41  	machine       *state.Machine
    42  	keyupdaterApi *keyupdater.State
    43  
    44  	existingEnvKey string
    45  	existingKeys   []string
    46  }
    47  
    48  func (s *workerSuite) SetUpTest(c *gc.C) {
    49  	s.JujuConnSuite.SetUpTest(c)
    50  	// Default ssh user is currently "ubuntu".
    51  	c.Assert(authenticationworker.SSHUser, gc.Equals, "ubuntu")
    52  	// Set the ssh user to empty (the current user) as required by the test infrastructure.
    53  	s.PatchValue(&authenticationworker.SSHUser, "")
    54  
    55  	// Replace the default dummy key in the test environment with a valid one.
    56  	// This will be added to the ssh authorised keys when the agent starts.
    57  	s.setAuthorisedKeys(c, sshtesting.ValidKeyOne.Key+" firstuser@host")
    58  	// Record the existing key with its prefix for testing later.
    59  	s.existingEnvKey = sshtesting.ValidKeyOne.Key + " Juju:firstuser@host"
    60  
    61  	// Set up an existing key (which is not in the environment) in the ssh authorised_keys file.
    62  	s.existingKeys = []string{sshtesting.ValidKeyTwo.Key + " existinguser@host"}
    63  	err := ssh.AddKeys(authenticationworker.SSHUser, s.existingKeys...)
    64  	c.Assert(err, gc.IsNil)
    65  
    66  	var apiRoot *api.State
    67  	apiRoot, s.machine = s.OpenAPIAsNewMachine(c)
    68  	c.Assert(apiRoot, gc.NotNil)
    69  	s.keyupdaterApi = apiRoot.KeyUpdater()
    70  	c.Assert(s.keyupdaterApi, gc.NotNil)
    71  }
    72  
    73  func stop(c *gc.C, w worker.Worker) {
    74  	c.Assert(worker.Stop(w), gc.IsNil)
    75  }
    76  
    77  type mockConfig struct {
    78  	agent.Config
    79  	c   *gc.C
    80  	tag string
    81  }
    82  
    83  func (mock *mockConfig) Tag() string {
    84  	return mock.tag
    85  }
    86  
    87  func agentConfig(c *gc.C, tag string) *mockConfig {
    88  	return &mockConfig{c: c, tag: tag}
    89  }
    90  
    91  func (s *workerSuite) setAuthorisedKeys(c *gc.C, keys ...string) {
    92  	keyStr := strings.Join(keys, "\n")
    93  	err := testing.UpdateConfig(s.BackingState, map[string]interface{}{"authorized-keys": keyStr})
    94  	c.Assert(err, gc.IsNil)
    95  	s.BackingState.StartSync()
    96  }
    97  
    98  func (s *workerSuite) waitSSHKeys(c *gc.C, expected []string) {
    99  	timeout := time.After(worstCase)
   100  	for {
   101  		select {
   102  		case <-timeout:
   103  			c.Fatalf("timeout while waiting for authoirsed ssh keys to change")
   104  		case <-time.After(coretesting.ShortWait):
   105  			keys, err := ssh.ListKeys(authenticationworker.SSHUser, ssh.FullKeys)
   106  			c.Assert(err, gc.IsNil)
   107  			keysStr := strings.Join(keys, "\n")
   108  			expectedStr := strings.Join(expected, "\n")
   109  			if expectedStr != keysStr {
   110  				continue
   111  			}
   112  			return
   113  		}
   114  	}
   115  }
   116  
   117  func (s *workerSuite) TestKeyUpdateRetainsExisting(c *gc.C) {
   118  	authWorker := authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag()))
   119  	defer stop(c, authWorker)
   120  
   121  	newKey := sshtesting.ValidKeyThree.Key + " user@host"
   122  	s.setAuthorisedKeys(c, newKey)
   123  	newKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:user@host"
   124  	s.waitSSHKeys(c, append(s.existingKeys, newKeyWithCommentPrefix))
   125  }
   126  
   127  func (s *workerSuite) TestNewKeysInJujuAreSavedOnStartup(c *gc.C) {
   128  	newKey := sshtesting.ValidKeyThree.Key + " user@host"
   129  	s.setAuthorisedKeys(c, newKey)
   130  
   131  	authWorker := authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag()))
   132  	defer stop(c, authWorker)
   133  
   134  	newKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:user@host"
   135  	s.waitSSHKeys(c, append(s.existingKeys, newKeyWithCommentPrefix))
   136  }
   137  
   138  func (s *workerSuite) TestDeleteKey(c *gc.C) {
   139  	authWorker := authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag()))
   140  	defer stop(c, authWorker)
   141  
   142  	// Add another key
   143  	anotherKey := sshtesting.ValidKeyThree.Key + " another@host"
   144  	s.setAuthorisedKeys(c, s.existingEnvKey, anotherKey)
   145  	anotherKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:another@host"
   146  	s.waitSSHKeys(c, append(s.existingKeys, s.existingEnvKey, anotherKeyWithCommentPrefix))
   147  
   148  	// Delete the original key and check anotherKey plus the existing keys remain.
   149  	s.setAuthorisedKeys(c, anotherKey)
   150  	s.waitSSHKeys(c, append(s.existingKeys, anotherKeyWithCommentPrefix))
   151  }
   152  
   153  func (s *workerSuite) TestMultipleChanges(c *gc.C) {
   154  	authWorker := authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag()))
   155  	defer stop(c, authWorker)
   156  	s.waitSSHKeys(c, append(s.existingKeys, s.existingEnvKey))
   157  
   158  	// Perform a set to add a key and delete a key.
   159  	// added: key 3
   160  	// deleted: key 1 (existing env key)
   161  	s.setAuthorisedKeys(c, sshtesting.ValidKeyThree.Key+" yetanother@host")
   162  	yetAnotherKeyWithComment := sshtesting.ValidKeyThree.Key + " Juju:yetanother@host"
   163  	s.waitSSHKeys(c, append(s.existingKeys, yetAnotherKeyWithComment))
   164  }
   165  
   166  func (s *workerSuite) TestWorkerRestart(c *gc.C) {
   167  	authWorker := authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag()))
   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 = authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag()))
   179  	defer stop(c, authWorker)
   180  
   181  	yetAnotherKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:yetanother@host"
   182  	s.waitSSHKeys(c, append(s.existingKeys, yetAnotherKeyWithCommentPrefix))
   183  }