From d72fa0006c49591859f5faab337f59078642eef1 Mon Sep 17 00:00:00 2001 From: JoshVanL Date: Mon, 18 Jun 2018 21:54:31 +0100 Subject: [PATCH 1/3] Adds flag to use own key pair --- cmd/tarmak/cmd/root.go | 7 +++++ pkg/apis/tarmak/v1alpha1/types.go | 2 ++ pkg/tarmak/cluster/cluster.go | 14 ++++++++-- pkg/tarmak/config/config.go | 4 +++ pkg/tarmak/environment/environment.go | 9 +++++-- pkg/tarmak/interfaces/interfaces.go | 1 + pkg/tarmak/provider/amazon/amazon.go | 26 ++++++++---------- pkg/tarmak/provider/amazon/key_pair.go | 37 +++++++++++++++++++++++++- 8 files changed, 80 insertions(+), 20 deletions(-) diff --git a/cmd/tarmak/cmd/root.go b/cmd/tarmak/cmd/root.go index 53f3b0be75..06aa2e8c43 100644 --- a/cmd/tarmak/cmd/root.go +++ b/cmd/tarmak/cmd/root.go @@ -105,6 +105,13 @@ func init() { "override the current cluster set in the config", ) + RootCmd.PersistentFlags().StringVar( + &globalFlags.KeyPairName, + "key-pair", + "", + "name of an existing key pair to use", + ) + if version == "dev" { RootCmd.PersistentFlags().BoolVar( &globalFlags.WingDevMode, diff --git a/pkg/apis/tarmak/v1alpha1/types.go b/pkg/apis/tarmak/v1alpha1/types.go index a235ed67d0..851b592bbb 100644 --- a/pkg/apis/tarmak/v1alpha1/types.go +++ b/pkg/apis/tarmak/v1alpha1/types.go @@ -127,6 +127,8 @@ type Flags struct { Version string // expose tarmak's build time version WingDevMode bool // use a bundled wing version rather than a tagged release from GitHub + + KeyPairName string // use an existing key pair } // This contains the cluster specifc operation flags diff --git a/pkg/tarmak/cluster/cluster.go b/pkg/tarmak/cluster/cluster.go index 13de3b5058..ea238bea26 100644 --- a/pkg/tarmak/cluster/cluster.go +++ b/pkg/tarmak/cluster/cluster.go @@ -157,8 +157,18 @@ func (c *Cluster) validateInstancePools() (result error) { } // Verify cluster -func (c *Cluster) Verify() (result error) { - return c.VerifyInstancePools() +func (c *Cluster) Verify() error { + var result *multierror.Error + + if err := c.VerifyInstancePools(); err != nil { + result = multierror.Append(result, err) + } + + if err := c.Environment().Verify(); err != nil { + result = multierror.Append(result, err) + } + + return result.ErrorOrNil() } // Verify instance pools diff --git a/pkg/tarmak/config/config.go b/pkg/tarmak/config/config.go index 2b8eaee973..f166fd9271 100644 --- a/pkg/tarmak/config/config.go +++ b/pkg/tarmak/config/config.go @@ -351,3 +351,7 @@ func (c *Config) Project() string { func (c *Config) WingDevMode() bool { return c.flags.WingDevMode } + +func (c *Config) KeyName() string { + return c.flags.KeyPairName +} diff --git a/pkg/tarmak/environment/environment.go b/pkg/tarmak/environment/environment.go index ab95dd1b0c..0f4bb03404 100644 --- a/pkg/tarmak/environment/environment.go +++ b/pkg/tarmak/environment/environment.go @@ -301,8 +301,13 @@ func (e *Environment) ValidateAdminCIDRs() (result error) { return result } -func (e *Environment) Verify() (result error) { - return result +func (e *Environment) Verify() error { + var result *multierror.Error + if err := e.Provider().Verify(); err != nil { + result = multierror.Append(result, err) + } + + return result.ErrorOrNil() } func (e *Environment) WingTunnel() interfaces.Tunnel { diff --git a/pkg/tarmak/interfaces/interfaces.go b/pkg/tarmak/interfaces/interfaces.go index 98356d6d79..6312f71217 100644 --- a/pkg/tarmak/interfaces/interfaces.go +++ b/pkg/tarmak/interfaces/interfaces.go @@ -185,6 +185,7 @@ type Config interface { Project() string WingDevMode() bool SetCurrentCluster(string) error + KeyName() string } type Packer interface { diff --git a/pkg/tarmak/provider/amazon/amazon.go b/pkg/tarmak/provider/amazon/amazon.go index 75654ff7ca..6f3d1eb770 100644 --- a/pkg/tarmak/provider/amazon/amazon.go +++ b/pkg/tarmak/provider/amazon/amazon.go @@ -317,12 +317,11 @@ func (a *Amazon) readVaultToken() (string, error) { } func (a *Amazon) Validate() error { - var result error - var err error + var result *multierror.Error // These checks only make sense with an environment given if a.tarmak.Environment() != nil { - err = a.validateRemoteStateBucket() + err := a.validateRemoteStateBucket() if err != nil { result = multierror.Append(result, err) } @@ -337,26 +336,23 @@ func (a *Amazon) Validate() error { result = multierror.Append(result, err) } - err = a.validateAWSKeyPair() - if err != nil { - result = multierror.Append(result, err) - } - } - err = a.validatePublicZone() + err := a.validatePublicZone() if err != nil { result = multierror.Append(result, err) } - if result != nil { - return result - } - return nil - + return result.ErrorOrNil() } -func (a *Amazon) Verify() (result error) { +func (a *Amazon) Verify() error { + var result *multierror.Error + + if err := a.verifyAWSKeyPair(); err != nil { + result = multierror.Append(result, err) + } + return result } diff --git a/pkg/tarmak/provider/amazon/key_pair.go b/pkg/tarmak/provider/amazon/key_pair.go index 763b8abb2d..333f5a25c1 100644 --- a/pkg/tarmak/provider/amazon/key_pair.go +++ b/pkg/tarmak/provider/amazon/key_pair.go @@ -40,7 +40,42 @@ func fingerprintAWSStyle(signer interface{}) (string, error) { } } -func (a *Amazon) validateAWSKeyPair() error { +func (a *Amazon) getExisitingKeyPair(name string) (*ec2.KeyPairInfo, error) { + svc, err := a.EC2() + if err != nil { + return nil, err + } + + keypairs, err := svc.DescribeKeyPairs(&ec2.DescribeKeyPairsInput{ + KeyNames: []*string{aws.String(name)}, + }) + if err != nil { + return nil, fmt.Errorf("failed to get exisiting key pair: %v", err) + } + + if len(keypairs.KeyPairs) == 0 { + return nil, fmt.Errorf("key pair '%s' does not exist", name) + } + if len(keypairs.KeyPairs) != 1 { + return nil, fmt.Errorf("received an unexpected number of key pairs: %d", len(keypairs.KeyPairs)) + } + + return keypairs.KeyPairs[0], nil +} + +func (a *Amazon) verifyAWSKeyPair() error { + // if key pair has been given + if name := a.tarmak.Config().KeyName(); name != "" { + _, err := a.getExisitingKeyPair(name) + if err != nil { + return err + } + + a.conf.Amazon.KeyName = name + + return nil + } + svc, err := a.EC2() if err != nil { return err From 3b60e6ac72da471b49d0881b926cb708e88b0905 Mon Sep 17 00:00:00 2001 From: JoshVanL Date: Wed, 20 Jun 2018 20:04:06 +0100 Subject: [PATCH 2/3] Adds default description to key-pair flag --- cmd/tarmak/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/tarmak/cmd/root.go b/cmd/tarmak/cmd/root.go index 06aa2e8c43..ab6797b3ff 100644 --- a/cmd/tarmak/cmd/root.go +++ b/cmd/tarmak/cmd/root.go @@ -109,7 +109,7 @@ func init() { &globalFlags.KeyPairName, "key-pair", "", - "name of an existing key pair to use", + "name of an existing key pair to use (default to tarmak generated key pair)", ) if version == "dev" { From 41657c3e90b7caa0442b53e9b2962ad148abf2ae Mon Sep 17 00:00:00 2001 From: JoshVanL Date: Thu, 21 Jun 2018 09:27:59 +0100 Subject: [PATCH 3/3] Fixes and adds tests for aws key pair --- pkg/tarmak/provider/amazon/amazon_test.go | 3 + pkg/tarmak/provider/amazon/key_pair_test.go | 86 +++++++++++++++++++-- 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/pkg/tarmak/provider/amazon/amazon_test.go b/pkg/tarmak/provider/amazon/amazon_test.go index 6cbd6d959f..5380aa0330 100644 --- a/pkg/tarmak/provider/amazon/amazon_test.go +++ b/pkg/tarmak/provider/amazon/amazon_test.go @@ -24,6 +24,7 @@ type fakeAmazon struct { fakeEnvironment *mocks.MockEnvironment fakeCluster *mocks.MockCluster fakeTarmak *mocks.MockTarmak + fakeConfig *mocks.MockConfig } func newFakeAmazon(t *testing.T) *fakeAmazon { @@ -45,9 +46,11 @@ func newFakeAmazon(t *testing.T) *fakeAmazon { f.fakeTarmak = mocks.NewMockTarmak(f.ctrl) f.Amazon.ec2 = f.fakeEC2 f.Amazon.tarmak = f.fakeTarmak + f.fakeConfig = mocks.NewMockConfig(f.ctrl) f.fakeTarmak.EXPECT().Cluster().AnyTimes().Return(f.fakeCluster) f.fakeTarmak.EXPECT().Environment().AnyTimes().Return(f.fakeEnvironment) f.fakeCluster.EXPECT().Environment().AnyTimes().Return(f.fakeEnvironment) + f.fakeTarmak.EXPECT().Config().AnyTimes().Return(f.fakeConfig) return f } diff --git a/pkg/tarmak/provider/amazon/key_pair_test.go b/pkg/tarmak/provider/amazon/key_pair_test.go index 036bcb7889..0c44e72e8e 100644 --- a/pkg/tarmak/provider/amazon/key_pair_test.go +++ b/pkg/tarmak/provider/amazon/key_pair_test.go @@ -47,10 +47,12 @@ var fakeSSHKeyInsecureFingerprint = "c7:15:68:10:e9:39:6c:ab:99:fe:d0:8b:e8:ec:f var fakeSSHKeyInsecurePublic = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ==\n" // test the happy path, local key matches the one existing in Amazon -func TestAmazon_validateAmazonKeyPairExistingHappyPath(t *testing.T) { +func TestAmazon_verifyAmazonKeyPairExistingHappyPath(t *testing.T) { a := newFakeAmazon(t) defer a.ctrl.Finish() + a.fakeConfig.EXPECT().KeyName().Return("") + // amazon repsonds with one key a.fakeEC2.EXPECT().DescribeKeyPairs(gomock.Any()).Return( &ec2.DescribeKeyPairsOutput{ @@ -71,16 +73,18 @@ func TestAmazon_validateAmazonKeyPairExistingHappyPath(t *testing.T) { } a.fakeEnvironment.EXPECT().SSHPrivateKey().Return(signer) - err = a.Amazon.validateAWSKeyPair() + err = a.Amazon.verifyAWSKeyPair() if err != nil { t.Errorf("unexpected error: %s", err) } } -func TestAmazon_validateAmazonKeyPairExistingMismatch(t *testing.T) { +func TestAmazon_verifyAmazonKeyPairExistingMismatch(t *testing.T) { a := newFakeAmazon(t) defer a.ctrl.Finish() + a.fakeConfig.EXPECT().KeyName().Return("") + // amazon repsonds with one key a.fakeEC2.EXPECT().DescribeKeyPairs(gomock.Any()).Return( &ec2.DescribeKeyPairsOutput{ @@ -101,7 +105,7 @@ func TestAmazon_validateAmazonKeyPairExistingMismatch(t *testing.T) { } a.fakeEnvironment.EXPECT().SSHPrivateKey().Return(signer) - err = a.Amazon.validateAWSKeyPair() + err = a.Amazon.verifyAWSKeyPair() if err == nil { t.Errorf("expected an error: %s", err) } else if !strings.Contains(err.Error(), "key pair does not match") { @@ -109,10 +113,12 @@ func TestAmazon_validateAmazonKeyPairExistingMismatch(t *testing.T) { } } -func TestAmazon_validateAmazonKeyPairNotExisting(t *testing.T) { +func TestAmazon_verifyAmazonKeyPairNotExisting(t *testing.T) { a := newFakeAmazon(t) defer a.ctrl.Finish() + a.fakeConfig.EXPECT().KeyName().Return("") + // amazon reports no key a.fakeEC2.EXPECT().DescribeKeyPairs(gomock.Any()).Return( &ec2.DescribeKeyPairsOutput{ @@ -140,8 +146,76 @@ func TestAmazon_validateAmazonKeyPairNotExisting(t *testing.T) { } a.fakeEnvironment.EXPECT().SSHPrivateKey().Return(signer) - err = a.Amazon.validateAWSKeyPair() + err = a.Amazon.verifyAWSKeyPair() if err != nil { t.Errorf("unexpected error: %s", err) } } + +func TestAmazon_verifyAmazonKeyPairGiven_Exists(t *testing.T) { + a := newFakeAmazon(t) + defer a.ctrl.Finish() + + a.fakeConfig.EXPECT().KeyName().Return("foo") + + a.fakeEC2.EXPECT().DescribeKeyPairs(gomock.Any()).Return( + &ec2.DescribeKeyPairsOutput{ + KeyPairs: []*ec2.KeyPairInfo{ + &ec2.KeyPairInfo{ + KeyFingerprint: aws.String("c7:15:68:10:e9:39:6c:ab:99:fe:d0:8b:e8:ec:f5:xx"), + KeyName: aws.String("foo"), + }, + }, + }, + nil, + ) + + if err := a.Amazon.verifyAWSKeyPair(); err != nil { + t.Errorf("unexpected error: %v", err) + } +} + +func TestAmazon_verifyAmazonKeyPairGiven_NotExists(t *testing.T) { + a := newFakeAmazon(t) + defer a.ctrl.Finish() + + a.fakeConfig.EXPECT().KeyName().Return("foo") + + a.fakeEC2.EXPECT().DescribeKeyPairs(gomock.Any()).Return( + &ec2.DescribeKeyPairsOutput{ + KeyPairs: []*ec2.KeyPairInfo{}, + }, + nil, + ) + + if err := a.Amazon.verifyAWSKeyPair(); err == nil { + t.Errorf("expected error, got none.") + } +} + +func TestAmazon_verifyAmazonKeyPairGiven_MultipleReturn(t *testing.T) { + a := newFakeAmazon(t) + defer a.ctrl.Finish() + + a.fakeConfig.EXPECT().KeyName().Return("foo") + + a.fakeEC2.EXPECT().DescribeKeyPairs(gomock.Any()).Return( + &ec2.DescribeKeyPairsOutput{ + KeyPairs: []*ec2.KeyPairInfo{ + &ec2.KeyPairInfo{ + KeyFingerprint: aws.String("c7:15:68:10:e9:39:6c:ab:99:fe:d0:8b:e8:ec:f5:xx"), + KeyName: aws.String("foo"), + }, + &ec2.KeyPairInfo{ + KeyFingerprint: aws.String("c7:15:68:30:e9:39:6c:ab:99:fe:d0:8b:e8:ec:f5:xx"), + KeyName: aws.String("foo"), + }, + }, + }, + nil, + ) + + if err := a.Amazon.verifyAWSKeyPair(); err == nil { + t.Errorf("expected error, got none.") + } +}