From 77140fa36647b48b44a02d1d4009a21474b65465 Mon Sep 17 00:00:00 2001 From: Matthew Sykes Date: Mon, 28 Dec 2015 13:27:08 -0500 Subject: [PATCH] Remove cli plugin from diego-ssh The cli team has integrated the authentication and interactive shell functions into the core. --- README.md | 47 +- cf-plugin/.gitignore | 2 - cf-plugin/cmd/allow_ssh.go | 27 - cf-plugin/cmd/allow_ssh_test.go | 78 - cf-plugin/cmd/cmd_suite_test.go | 59 - cf-plugin/cmd/disable_ssh.go | 27 - cf-plugin/cmd/disable_ssh_test.go | 79 - cf-plugin/cmd/disallow_ssh.go | 27 - cf-plugin/cmd/disallow_ssh_test.go | 79 - cf-plugin/cmd/enable_ssh.go | 27 - cf-plugin/cmd/enable_ssh_test.go | 79 - cf-plugin/cmd/fakes/fake_listener_factory.go | 58 - cf-plugin/cmd/fakes/fake_secure_client.go | 181 --- cf-plugin/cmd/fakes/fake_secure_dialer.go | 60 - cf-plugin/cmd/fakes/fake_secure_session.go | 333 ----- cf-plugin/cmd/get_ssh_code.go | 28 - cf-plugin/cmd/get_ssh_code_test.go | 39 - cf-plugin/cmd/ssh.go | 474 ------ cf-plugin/cmd/ssh_allowed.go | 24 - cf-plugin/cmd/ssh_allowed_test.go | 62 - cf-plugin/cmd/ssh_enabled.go | 24 - cf-plugin/cmd/ssh_enabled_test.go | 62 - cf-plugin/cmd/ssh_test.go | 1297 ----------------- cf-plugin/main.go | 8 - cf-plugin/models/app/app.go | 73 - .../models/app/app_fakes/fake_app_factory.go | 99 -- cf-plugin/models/app/app_suite_test.go | 13 - cf-plugin/models/app/app_test.go | 95 -- cf-plugin/models/credential/credential.go | 120 -- .../fake_credential_factory.go | 45 - .../credential/credential_suite_test.go | 13 - .../models/credential/credential_test.go | 171 --- cf-plugin/models/curl.go | 45 - cf-plugin/models/curl_test.go | 79 - cf-plugin/models/info/info.go | 45 - .../info/info_fakes/fake_app_factory.go | 45 - cf-plugin/models/info/info_suite_test.go | 13 - cf-plugin/models/info/info_test.go | 79 - cf-plugin/models/models_suite_test.go | 13 - cf-plugin/models/space/space.go | 67 - .../space/space_fakes/fake_space_factory.go | 99 -- cf-plugin/models/space/space_suite_test.go | 13 - cf-plugin/models/space/space_test.go | 92 -- cf-plugin/options/multiopt/multiopt.go | 24 - .../options/multiopt/multiopt_suite_test.go | 13 - cf-plugin/options/multiopt/multiopt_test.go | 43 - cf-plugin/options/options_suite_test.go | 13 - cf-plugin/options/ssh_options.go | 208 --- cf-plugin/options/ssh_options_test.go | 369 ----- cf-plugin/scripts/build-plugins.sh | 13 - cf-plugin/sigwinch/sigwinch.go | 9 - cf-plugin/sigwinch/sigwinch_win.go | 9 - cf-plugin/ssh_plugin.go | 202 --- cf-plugin/terminal/helper.go | 47 - cf-plugin/terminal/helper_test.go | 22 - .../fake_terminal_helper.go | 257 ---- cf-plugin/terminal/terminal_suite_test.go | 13 - 57 files changed, 12 insertions(+), 5630 deletions(-) delete mode 100644 cf-plugin/.gitignore delete mode 100644 cf-plugin/cmd/allow_ssh.go delete mode 100644 cf-plugin/cmd/allow_ssh_test.go delete mode 100644 cf-plugin/cmd/cmd_suite_test.go delete mode 100644 cf-plugin/cmd/disable_ssh.go delete mode 100644 cf-plugin/cmd/disable_ssh_test.go delete mode 100644 cf-plugin/cmd/disallow_ssh.go delete mode 100644 cf-plugin/cmd/disallow_ssh_test.go delete mode 100644 cf-plugin/cmd/enable_ssh.go delete mode 100644 cf-plugin/cmd/enable_ssh_test.go delete mode 100644 cf-plugin/cmd/fakes/fake_listener_factory.go delete mode 100644 cf-plugin/cmd/fakes/fake_secure_client.go delete mode 100644 cf-plugin/cmd/fakes/fake_secure_dialer.go delete mode 100644 cf-plugin/cmd/fakes/fake_secure_session.go delete mode 100644 cf-plugin/cmd/get_ssh_code.go delete mode 100644 cf-plugin/cmd/get_ssh_code_test.go delete mode 100644 cf-plugin/cmd/ssh.go delete mode 100644 cf-plugin/cmd/ssh_allowed.go delete mode 100644 cf-plugin/cmd/ssh_allowed_test.go delete mode 100644 cf-plugin/cmd/ssh_enabled.go delete mode 100644 cf-plugin/cmd/ssh_enabled_test.go delete mode 100644 cf-plugin/cmd/ssh_test.go delete mode 100644 cf-plugin/main.go delete mode 100644 cf-plugin/models/app/app.go delete mode 100644 cf-plugin/models/app/app_fakes/fake_app_factory.go delete mode 100644 cf-plugin/models/app/app_suite_test.go delete mode 100644 cf-plugin/models/app/app_test.go delete mode 100644 cf-plugin/models/credential/credential.go delete mode 100644 cf-plugin/models/credential/credential_fakes/fake_credential_factory.go delete mode 100644 cf-plugin/models/credential/credential_suite_test.go delete mode 100644 cf-plugin/models/credential/credential_test.go delete mode 100644 cf-plugin/models/curl.go delete mode 100644 cf-plugin/models/curl_test.go delete mode 100644 cf-plugin/models/info/info.go delete mode 100644 cf-plugin/models/info/info_fakes/fake_app_factory.go delete mode 100644 cf-plugin/models/info/info_suite_test.go delete mode 100644 cf-plugin/models/info/info_test.go delete mode 100644 cf-plugin/models/models_suite_test.go delete mode 100644 cf-plugin/models/space/space.go delete mode 100644 cf-plugin/models/space/space_fakes/fake_space_factory.go delete mode 100644 cf-plugin/models/space/space_suite_test.go delete mode 100644 cf-plugin/models/space/space_test.go delete mode 100644 cf-plugin/options/multiopt/multiopt.go delete mode 100644 cf-plugin/options/multiopt/multiopt_suite_test.go delete mode 100644 cf-plugin/options/multiopt/multiopt_test.go delete mode 100644 cf-plugin/options/options_suite_test.go delete mode 100644 cf-plugin/options/ssh_options.go delete mode 100644 cf-plugin/options/ssh_options_test.go delete mode 100755 cf-plugin/scripts/build-plugins.sh delete mode 100644 cf-plugin/sigwinch/sigwinch.go delete mode 100644 cf-plugin/sigwinch/sigwinch_win.go delete mode 100644 cf-plugin/ssh_plugin.go delete mode 100644 cf-plugin/terminal/helper.go delete mode 100644 cf-plugin/terminal/helper_test.go delete mode 100644 cf-plugin/terminal/terminal_helper_fakes/fake_terminal_helper.go delete mode 100644 cf-plugin/terminal/terminal_suite_test.go diff --git a/README.md b/README.md index 7a2cf98..e86a872 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,10 @@ # diego-ssh -**The Diego-SSH CLI plugin is intended only for CLI v6.12.4 and older. -[CLI v6.13.0+](https://github.com/cloudfoundry/cli/releases) does not -require this plugin as the Diego SSH functions are now core functions -in the CLI.** - -Diego-ssh is an implementation of an ssh proxy server, a lightweight ssh -daemon, and a Cloud Foundry cli plugin. When deployed and configured -correctly, they provide a simple and scalable way to access containers -associated with Diego long running processes. +Diego-ssh is an implementation of an ssh proxy server and a lightweight ssh +daemon that supports command execution, secure file copies via `scp`, and +secure file transfer via `sftp`. When deployed and configured correctly, these +provide a simple and scalable way to access containers associated with Diego +long running processes. ## Proxy @@ -71,9 +67,9 @@ $ curl -k -v -H "Authorization: $(cf oauth-token | tail -1)" \ cut -f2 -d'=' | \ pbcopy # paste authoriztion code when prompted for password ``` -or, with the ssh plugin +or, with the Cloud Foundry `cf` [command line interface][cli]; ``` -$ cf get-ssh-code | pbcopy # paste authorization code when prompted for password +$ cf ssh-code | pbcopy # paste authorization code when prompted for password ``` The authorization code can then be used as the password: @@ -81,12 +77,14 @@ The authorization code can then be used as the password: ``` $ ssh -p 2222 cf:$(cf app app-name --guid)/0@ssh.bosh-lite.com $ scp -P 2222 -oUser=cf:$(cf app app-name --guid)/0 my-local-file.json ssh.bosh-lite.com:my-remote-file.json +$ sftp -P 2222 cf:$(cf app app-name --guid)/0@ssh.bosh-lite.com ``` -Cloud Foundry `cf` client example that uses the [ssh][ssh-plugin] plugin: +The Cloud Foundry `cf` [command line interface][cli] (v6.13 and newer) can +also be used to access an interactive shell in an application container: ``` -$ cf install-plugin https://github.com/cloudfoundry-incubator/diego-ssh/releases/download/${version}/ssh-plugin-${platform}-${arch} $ cf ssh app-name +$ cf ssh app-name -i 3 # access the container hosting index 3 of the app ``` This support is enabled with the `--enableCFAuth` flag. @@ -194,26 +192,5 @@ part of the lifecycle bundle. [bridge]: https://github.com/cloudfoundry-incubator/diego-design-notes#cc-bridge-components [cflinuxfs2]: https://github.com/cloudfoundry/stacks/tree/master/cflinuxfs2 -[ssh-plugin]: https://github.com/cloudfoundry-incubator/diego-ssh/releases - -## CF CLI Plugin - -The [cf CLI](https://github.com/cloudfoundry/cli) plugin adds the following commands: -- **ssh** - ssh to an application container instance -- **enable-ssh** - enable ssh for the application -- **disable-ssh** - disable ssh for the application -- **ssh-enabled** - reports whether SSH is enabled on an application container instance -- **allow-space-ssh** - allow SSH access for the space -- **disallow-space-ssh** - disallow SSH access for the space -- **space-ssh-allowed** - reports whether SSH is allowed in a space -- **get-ssh-code** - obtain a one-time authorization code that can be used as an ssh password - -To build and install the plugin: -``` -cd ~/workspace/diego-release/src/github.com/cloudfoundry-incubator/diego-ssh/cf-plugin -go build -cf uninstall-plugin Diego-SSH -cf install-plugin cf-plugin -``` - +[cli]: https://github.com/cloudfoundry/cli [non-standard-oauth-auth-code]: https://github.com/cloudfoundry/uaa/blob/master/docs/UAA-APIs.rst#api-authorization-requests-code-get-oauth-authorize-non-standard-oauth-authorize diff --git a/cf-plugin/.gitignore b/cf-plugin/.gitignore deleted file mode 100644 index 35d7998..0000000 --- a/cf-plugin/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -plugin -ssh-plugin-* diff --git a/cf-plugin/cmd/allow_ssh.go b/cf-plugin/cmd/allow_ssh.go deleted file mode 100644 index db2b295..0000000 --- a/cf-plugin/cmd/allow_ssh.go +++ /dev/null @@ -1,27 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space" -) - -const AllowSSHUsage = "cf allow-space-ssh SPACE_NAME" - -func AllowSSH(args []string, spaceFactory space.SpaceFactory) error { - if len(args) != 2 || args[0] != "allow-space-ssh" { - return fmt.Errorf("%s\n%s", "Invalid usage", AllowSSHUsage) - } - - space, err := spaceFactory.Get(args[1]) - if err != nil { - return err - } - - err = spaceFactory.SetBool(space, "allow_ssh", true) - if err != nil { - return err - } - - return nil -} diff --git a/cf-plugin/cmd/allow_ssh_test.go b/cf-plugin/cmd/allow_ssh_test.go deleted file mode 100644 index be425b8..0000000 --- a/cf-plugin/cmd/allow_ssh_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package cmd_test - -import ( - "errors" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space/space_fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("AllowSSH", func() { - var fakeSpaceFactory *space_fakes.FakeSpaceFactory - var mySpace space.Space - - BeforeEach(func() { - fakeSpaceFactory = &space_fakes.FakeSpaceFactory{} - mySpace = space.Space{Guid: "myguid"} - }) - - Context("validation", func() { - It("requires an space name", func() { - err := cmd.AllowSSH([]string{"allow-space-ssh"}, fakeSpaceFactory) - - Expect(err).To(MatchError("Invalid usage\n" + cmd.AllowSSHUsage)) - }) - - It("validates the command name", func() { - err := cmd.AllowSSH([]string{"bogus", "space"}, fakeSpaceFactory) - Expect(err).To(MatchError("Invalid usage\n" + cmd.AllowSSHUsage)) - }) - }) - - It("allows SSH on an space endpoint", func() { - fakeSpaceFactory.GetReturns(mySpace, nil) - - err := cmd.AllowSSH([]string{"allow-space-ssh", "myspace"}, fakeSpaceFactory) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeSpaceFactory.GetCallCount()).To(Equal(1)) - Expect(fakeSpaceFactory.GetArgsForCall(0)).To(Equal("myspace")) - - Expect(fakeSpaceFactory.SetBoolCallCount()).To(Equal(1)) - aSpace, key, val := fakeSpaceFactory.SetBoolArgsForCall(0) - Expect(aSpace).To(Equal(mySpace)) - Expect(key).To(Equal("allow_ssh")) - Expect(val).To(BeTrue()) - }) - - Context("when retrieving the Space fails", func() { - BeforeEach(func() { - fakeSpaceFactory.GetReturns(space.Space{}, errors.New("get failed")) - }) - - It("returns an err", func() { - err := cmd.AllowSSH([]string{"allow-space-ssh", "myspace"}, fakeSpaceFactory) - Expect(err).To(MatchError("get failed")) - Expect(fakeSpaceFactory.GetCallCount()).To(Equal(1)) - Expect(fakeSpaceFactory.SetBoolCallCount()).To(Equal(0)) - }) - }) - - Context("when setting the value fails", func() { - BeforeEach(func() { - fakeSpaceFactory.GetReturns(mySpace, nil) - fakeSpaceFactory.SetBoolReturns(errors.New("set failed")) - }) - - It("returns an err", func() { - err := cmd.AllowSSH([]string{"allow-space-ssh", "myspace"}, fakeSpaceFactory) - Expect(err).To(MatchError("set failed")) - Expect(fakeSpaceFactory.GetCallCount()).To(Equal(1)) - Expect(fakeSpaceFactory.SetBoolCallCount()).To(Equal(1)) - }) - }) -}) diff --git a/cf-plugin/cmd/cmd_suite_test.go b/cf-plugin/cmd/cmd_suite_test.go deleted file mode 100644 index 8227271..0000000 --- a/cf-plugin/cmd/cmd_suite_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package cmd_test - -import ( - "encoding/json" - - "github.com/cloudfoundry-incubator/diego-ssh/helpers" - "github.com/cloudfoundry-incubator/diego-ssh/keys" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "golang.org/x/crypto/ssh" - - "testing" -) - -var ( - TestHostKey ssh.Signer - TestHostKeyFingerprint string - TestPrivateKey ssh.Signer -) - -func TestCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Cmd Suite") -} - -var _ = SynchronizedBeforeSuite(func() []byte { - hostKey, err := keys.RSAKeyPairFactory.NewKeyPair(1024) - Expect(err).NotTo(HaveOccurred()) - - privateKey, err := keys.RSAKeyPairFactory.NewKeyPair(1024) - Expect(err).NotTo(HaveOccurred()) - - payload, err := json.Marshal(map[string]string{ - "host-key": hostKey.PEMEncodedPrivateKey(), - "private-key": privateKey.PEMEncodedPrivateKey(), - }) - - Expect(err).NotTo(HaveOccurred()) - - return payload - -}, func(payload []byte) { - context := map[string]string{} - - err := json.Unmarshal(payload, &context) - Expect(err).NotTo(HaveOccurred()) - - hostKeyPem := context["host-key"] - hostKey, err := ssh.ParsePrivateKey([]byte(hostKeyPem)) - Expect(err).NotTo(HaveOccurred()) - TestHostKey = hostKey - - privateKeyPem := context["private-key"] - privateKey, err := ssh.ParsePrivateKey([]byte(privateKeyPem)) - Expect(err).NotTo(HaveOccurred()) - TestPrivateKey = privateKey - - TestHostKeyFingerprint = helpers.SHA1Fingerprint(TestHostKey.PublicKey()) -}) diff --git a/cf-plugin/cmd/disable_ssh.go b/cf-plugin/cmd/disable_ssh.go deleted file mode 100644 index 4d3db93..0000000 --- a/cf-plugin/cmd/disable_ssh.go +++ /dev/null @@ -1,27 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app" -) - -const DisableSSHUsage = "cf disable-ssh APP_NAME" - -func DisableSSH(args []string, appFactory app.AppFactory) error { - if len(args) != 2 || args[0] != "disable-ssh" { - return fmt.Errorf("%s\n%s", "Invalid usage", DisableSSHUsage) - } - - app, err := appFactory.Get(args[1]) - if err != nil { - return err - } - - err = appFactory.SetBool(app, "enable_ssh", false) - if err != nil { - return err - } - - return nil -} diff --git a/cf-plugin/cmd/disable_ssh_test.go b/cf-plugin/cmd/disable_ssh_test.go deleted file mode 100644 index b9a5b5d..0000000 --- a/cf-plugin/cmd/disable_ssh_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package cmd_test - -import ( - "errors" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app/app_fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("DisableSsh", func() { - var fakeAppFactory *app_fakes.FakeAppFactory - var myApp app.App - - BeforeEach(func() { - fakeAppFactory = &app_fakes.FakeAppFactory{} - myApp = app.App{Guid: "myguid"} - }) - - Context("validation", func() { - It("requires an application name", func() { - err := cmd.DisableSSH([]string{"disable-ssh"}, fakeAppFactory) - - Expect(err).To(MatchError("Invalid usage\n" + cmd.DisableSSHUsage)) - }) - - It("validates the command name", func() { - err := cmd.DisableSSH([]string{"disable-ss", "app"}, fakeAppFactory) - - Expect(err).To(MatchError("Invalid usage\n" + cmd.DisableSSHUsage)) - }) - }) - - It("disables SSH on an app endpoint", func() { - fakeAppFactory.GetReturns(myApp, nil) - - err := cmd.DisableSSH([]string{"disable-ssh", "myapp"}, fakeAppFactory) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - Expect(fakeAppFactory.GetArgsForCall(0)).To(Equal("myapp")) - - Expect(fakeAppFactory.SetBoolCallCount()).To(Equal(1)) - anApp, key, val := fakeAppFactory.SetBoolArgsForCall(0) - Expect(anApp).To(Equal(myApp)) - Expect(key).To(Equal("enable_ssh")) - Expect(val).To(BeFalse()) - }) - - Context("when retrieving the App fails", func() { - BeforeEach(func() { - fakeAppFactory.GetReturns(app.App{}, errors.New("get failed")) - }) - - It("returns an err", func() { - err := cmd.DisableSSH([]string{"disable-ssh", "myapp"}, fakeAppFactory) - Expect(err).To(MatchError("get failed")) - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - Expect(fakeAppFactory.SetBoolCallCount()).To(Equal(0)) - }) - }) - - Context("when setting the value fails", func() { - BeforeEach(func() { - fakeAppFactory.GetReturns(myApp, nil) - fakeAppFactory.SetBoolReturns(errors.New("set failed")) - }) - - It("returns an err", func() { - err := cmd.DisableSSH([]string{"disable-ssh", "myapp"}, fakeAppFactory) - Expect(err).To(MatchError("set failed")) - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - Expect(fakeAppFactory.SetBoolCallCount()).To(Equal(1)) - }) - }) -}) diff --git a/cf-plugin/cmd/disallow_ssh.go b/cf-plugin/cmd/disallow_ssh.go deleted file mode 100644 index 9874be9..0000000 --- a/cf-plugin/cmd/disallow_ssh.go +++ /dev/null @@ -1,27 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space" -) - -const DisallowSSHUsage = "cf disallow-space-ssh SPACE_NAME" - -func DisallowSSH(args []string, spaceFactory space.SpaceFactory) error { - if len(args) != 2 || args[0] != "disallow-space-ssh" { - return fmt.Errorf("%s\n%s", "Invalid usage", DisallowSSHUsage) - } - - space, err := spaceFactory.Get(args[1]) - if err != nil { - return err - } - - err = spaceFactory.SetBool(space, "allow_ssh", false) - if err != nil { - return err - } - - return nil -} diff --git a/cf-plugin/cmd/disallow_ssh_test.go b/cf-plugin/cmd/disallow_ssh_test.go deleted file mode 100644 index c0f010d..0000000 --- a/cf-plugin/cmd/disallow_ssh_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package cmd_test - -import ( - "errors" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space/space_fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("DisallowSSH", func() { - var fakeSpaceFactory *space_fakes.FakeSpaceFactory - var mySpace space.Space - - BeforeEach(func() { - fakeSpaceFactory = &space_fakes.FakeSpaceFactory{} - mySpace = space.Space{Guid: "myguid"} - }) - - Context("validation", func() { - It("requires an space name", func() { - err := cmd.DisallowSSH([]string{"disallow-space-ssh"}, fakeSpaceFactory) - - Expect(err).To(MatchError("Invalid usage\n" + cmd.DisallowSSHUsage)) - }) - - It("validates the command name", func() { - err := cmd.DisallowSSH([]string{"bogus", "space"}, fakeSpaceFactory) - - Expect(err).To(MatchError("Invalid usage\n" + cmd.DisallowSSHUsage)) - }) - }) - - It("disallows SSH on an space endpoint", func() { - fakeSpaceFactory.GetReturns(mySpace, nil) - - err := cmd.DisallowSSH([]string{"disallow-space-ssh", "myspace"}, fakeSpaceFactory) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeSpaceFactory.GetCallCount()).To(Equal(1)) - Expect(fakeSpaceFactory.GetArgsForCall(0)).To(Equal("myspace")) - - Expect(fakeSpaceFactory.SetBoolCallCount()).To(Equal(1)) - aSpace, key, val := fakeSpaceFactory.SetBoolArgsForCall(0) - Expect(aSpace).To(Equal(mySpace)) - Expect(key).To(Equal("allow_ssh")) - Expect(val).To(BeFalse()) - }) - - Context("when retrieving the Space fails", func() { - BeforeEach(func() { - fakeSpaceFactory.GetReturns(space.Space{}, errors.New("get failed")) - }) - - It("returns an err", func() { - err := cmd.DisallowSSH([]string{"disallow-space-ssh", "myspace"}, fakeSpaceFactory) - Expect(err).To(MatchError("get failed")) - Expect(fakeSpaceFactory.GetCallCount()).To(Equal(1)) - Expect(fakeSpaceFactory.SetBoolCallCount()).To(Equal(0)) - }) - }) - - Context("when setting the value fails", func() { - BeforeEach(func() { - fakeSpaceFactory.GetReturns(mySpace, nil) - fakeSpaceFactory.SetBoolReturns(errors.New("set failed")) - }) - - It("returns an err", func() { - err := cmd.DisallowSSH([]string{"disallow-space-ssh", "myspace"}, fakeSpaceFactory) - Expect(err).To(MatchError("set failed")) - Expect(fakeSpaceFactory.GetCallCount()).To(Equal(1)) - Expect(fakeSpaceFactory.SetBoolCallCount()).To(Equal(1)) - }) - }) -}) diff --git a/cf-plugin/cmd/enable_ssh.go b/cf-plugin/cmd/enable_ssh.go deleted file mode 100644 index c7816a7..0000000 --- a/cf-plugin/cmd/enable_ssh.go +++ /dev/null @@ -1,27 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app" -) - -const EnableSSHUsage = "cf enable-ssh APP_NAME" - -func EnableSSH(args []string, appFactory app.AppFactory) error { - if len(args) != 2 || args[0] != "enable-ssh" { - return fmt.Errorf("%s\n%s", "Invalid usage", EnableSSHUsage) - } - - app, err := appFactory.Get(args[1]) - if err != nil { - return err - } - - err = appFactory.SetBool(app, "enable_ssh", true) - if err != nil { - return err - } - - return nil -} diff --git a/cf-plugin/cmd/enable_ssh_test.go b/cf-plugin/cmd/enable_ssh_test.go deleted file mode 100644 index a92f627..0000000 --- a/cf-plugin/cmd/enable_ssh_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package cmd_test - -import ( - "errors" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app/app_fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("EnableSsh", func() { - var fakeAppFactory *app_fakes.FakeAppFactory - var myApp app.App - - BeforeEach(func() { - fakeAppFactory = &app_fakes.FakeAppFactory{} - myApp = app.App{Guid: "myguid"} - }) - - Context("validation", func() { - It("requires an application name", func() { - err := cmd.EnableSSH([]string{"enable-ssh"}, fakeAppFactory) - - Expect(err).To(MatchError("Invalid usage\n" + cmd.EnableSSHUsage)) - }) - - It("validates the command name", func() { - err := cmd.EnableSSH([]string{"enable-ss", "app"}, fakeAppFactory) - - Expect(err).To(MatchError("Invalid usage\n" + cmd.EnableSSHUsage)) - }) - }) - - It("enables SSH on an app endpoint", func() { - fakeAppFactory.GetReturns(myApp, nil) - - err := cmd.EnableSSH([]string{"enable-ssh", "myapp"}, fakeAppFactory) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - Expect(fakeAppFactory.GetArgsForCall(0)).To(Equal("myapp")) - - Expect(fakeAppFactory.SetBoolCallCount()).To(Equal(1)) - anApp, key, val := fakeAppFactory.SetBoolArgsForCall(0) - Expect(anApp).To(Equal(myApp)) - Expect(key).To(Equal("enable_ssh")) - Expect(val).To(BeTrue()) - }) - - Context("when retrieving the App fails", func() { - BeforeEach(func() { - fakeAppFactory.GetReturns(app.App{}, errors.New("get failed")) - }) - - It("returns an err", func() { - err := cmd.EnableSSH([]string{"enable-ssh", "myapp"}, fakeAppFactory) - Expect(err).To(MatchError("get failed")) - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - Expect(fakeAppFactory.SetBoolCallCount()).To(Equal(0)) - }) - }) - - Context("when setting the value fails", func() { - BeforeEach(func() { - fakeAppFactory.GetReturns(myApp, nil) - fakeAppFactory.SetBoolReturns(errors.New("set failed")) - }) - - It("returns an err", func() { - err := cmd.EnableSSH([]string{"enable-ssh", "myapp"}, fakeAppFactory) - Expect(err).To(MatchError("set failed")) - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - Expect(fakeAppFactory.SetBoolCallCount()).To(Equal(1)) - }) - }) -}) diff --git a/cf-plugin/cmd/fakes/fake_listener_factory.go b/cf-plugin/cmd/fakes/fake_listener_factory.go deleted file mode 100644 index ee77e6e..0000000 --- a/cf-plugin/cmd/fakes/fake_listener_factory.go +++ /dev/null @@ -1,58 +0,0 @@ -// This file was generated by counterfeiter -package fakes - -import ( - "net" - "sync" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" -) - -type FakeListenerFactory struct { - ListenStub func(network, address string) (net.Listener, error) - listenMutex sync.RWMutex - listenArgsForCall []struct { - network string - address string - } - listenReturns struct { - result1 net.Listener - result2 error - } -} - -func (fake *FakeListenerFactory) Listen(network string, address string) (net.Listener, error) { - fake.listenMutex.Lock() - fake.listenArgsForCall = append(fake.listenArgsForCall, struct { - network string - address string - }{network, address}) - fake.listenMutex.Unlock() - if fake.ListenStub != nil { - return fake.ListenStub(network, address) - } else { - return fake.listenReturns.result1, fake.listenReturns.result2 - } -} - -func (fake *FakeListenerFactory) ListenCallCount() int { - fake.listenMutex.RLock() - defer fake.listenMutex.RUnlock() - return len(fake.listenArgsForCall) -} - -func (fake *FakeListenerFactory) ListenArgsForCall(i int) (string, string) { - fake.listenMutex.RLock() - defer fake.listenMutex.RUnlock() - return fake.listenArgsForCall[i].network, fake.listenArgsForCall[i].address -} - -func (fake *FakeListenerFactory) ListenReturns(result1 net.Listener, result2 error) { - fake.ListenStub = nil - fake.listenReturns = struct { - result1 net.Listener - result2 error - }{result1, result2} -} - -var _ cmd.ListenerFactory = new(FakeListenerFactory) diff --git a/cf-plugin/cmd/fakes/fake_secure_client.go b/cf-plugin/cmd/fakes/fake_secure_client.go deleted file mode 100644 index b57db47..0000000 --- a/cf-plugin/cmd/fakes/fake_secure_client.go +++ /dev/null @@ -1,181 +0,0 @@ -// This file was generated by counterfeiter -package fakes - -import ( - "net" - "sync" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "golang.org/x/crypto/ssh" -) - -type FakeSecureClient struct { - NewSessionStub func() (cmd.SecureSession, error) - newSessionMutex sync.RWMutex - newSessionArgsForCall []struct{} - newSessionReturns struct { - result1 cmd.SecureSession - result2 error - } - ConnStub func() ssh.Conn - connMutex sync.RWMutex - connArgsForCall []struct{} - connReturns struct { - result1 ssh.Conn - } - DialStub func(network, address string) (net.Conn, error) - dialMutex sync.RWMutex - dialArgsForCall []struct { - network string - address string - } - dialReturns struct { - result1 net.Conn - result2 error - } - WaitStub func() error - waitMutex sync.RWMutex - waitArgsForCall []struct{} - waitReturns struct { - result1 error - } - CloseStub func() error - closeMutex sync.RWMutex - closeArgsForCall []struct{} - closeReturns struct { - result1 error - } -} - -func (fake *FakeSecureClient) NewSession() (cmd.SecureSession, error) { - fake.newSessionMutex.Lock() - fake.newSessionArgsForCall = append(fake.newSessionArgsForCall, struct{}{}) - fake.newSessionMutex.Unlock() - if fake.NewSessionStub != nil { - return fake.NewSessionStub() - } else { - return fake.newSessionReturns.result1, fake.newSessionReturns.result2 - } -} - -func (fake *FakeSecureClient) NewSessionCallCount() int { - fake.newSessionMutex.RLock() - defer fake.newSessionMutex.RUnlock() - return len(fake.newSessionArgsForCall) -} - -func (fake *FakeSecureClient) NewSessionReturns(result1 cmd.SecureSession, result2 error) { - fake.NewSessionStub = nil - fake.newSessionReturns = struct { - result1 cmd.SecureSession - result2 error - }{result1, result2} -} - -func (fake *FakeSecureClient) Conn() ssh.Conn { - fake.connMutex.Lock() - fake.connArgsForCall = append(fake.connArgsForCall, struct{}{}) - fake.connMutex.Unlock() - if fake.ConnStub != nil { - return fake.ConnStub() - } else { - return fake.connReturns.result1 - } -} - -func (fake *FakeSecureClient) ConnCallCount() int { - fake.connMutex.RLock() - defer fake.connMutex.RUnlock() - return len(fake.connArgsForCall) -} - -func (fake *FakeSecureClient) ConnReturns(result1 ssh.Conn) { - fake.ConnStub = nil - fake.connReturns = struct { - result1 ssh.Conn - }{result1} -} - -func (fake *FakeSecureClient) Dial(network string, address string) (net.Conn, error) { - fake.dialMutex.Lock() - fake.dialArgsForCall = append(fake.dialArgsForCall, struct { - network string - address string - }{network, address}) - fake.dialMutex.Unlock() - if fake.DialStub != nil { - return fake.DialStub(network, address) - } else { - return fake.dialReturns.result1, fake.dialReturns.result2 - } -} - -func (fake *FakeSecureClient) DialCallCount() int { - fake.dialMutex.RLock() - defer fake.dialMutex.RUnlock() - return len(fake.dialArgsForCall) -} - -func (fake *FakeSecureClient) DialArgsForCall(i int) (string, string) { - fake.dialMutex.RLock() - defer fake.dialMutex.RUnlock() - return fake.dialArgsForCall[i].network, fake.dialArgsForCall[i].address -} - -func (fake *FakeSecureClient) DialReturns(result1 net.Conn, result2 error) { - fake.DialStub = nil - fake.dialReturns = struct { - result1 net.Conn - result2 error - }{result1, result2} -} - -func (fake *FakeSecureClient) Wait() error { - fake.waitMutex.Lock() - fake.waitArgsForCall = append(fake.waitArgsForCall, struct{}{}) - fake.waitMutex.Unlock() - if fake.WaitStub != nil { - return fake.WaitStub() - } else { - return fake.waitReturns.result1 - } -} - -func (fake *FakeSecureClient) WaitCallCount() int { - fake.waitMutex.RLock() - defer fake.waitMutex.RUnlock() - return len(fake.waitArgsForCall) -} - -func (fake *FakeSecureClient) WaitReturns(result1 error) { - fake.WaitStub = nil - fake.waitReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeSecureClient) Close() error { - fake.closeMutex.Lock() - fake.closeArgsForCall = append(fake.closeArgsForCall, struct{}{}) - fake.closeMutex.Unlock() - if fake.CloseStub != nil { - return fake.CloseStub() - } else { - return fake.closeReturns.result1 - } -} - -func (fake *FakeSecureClient) CloseCallCount() int { - fake.closeMutex.RLock() - defer fake.closeMutex.RUnlock() - return len(fake.closeArgsForCall) -} - -func (fake *FakeSecureClient) CloseReturns(result1 error) { - fake.CloseStub = nil - fake.closeReturns = struct { - result1 error - }{result1} -} - -var _ cmd.SecureClient = new(FakeSecureClient) diff --git a/cf-plugin/cmd/fakes/fake_secure_dialer.go b/cf-plugin/cmd/fakes/fake_secure_dialer.go deleted file mode 100644 index 3c3333b..0000000 --- a/cf-plugin/cmd/fakes/fake_secure_dialer.go +++ /dev/null @@ -1,60 +0,0 @@ -// This file was generated by counterfeiter -package fakes - -import ( - "sync" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "golang.org/x/crypto/ssh" -) - -type FakeSecureDialer struct { - DialStub func(network, address string, config *ssh.ClientConfig) (cmd.SecureClient, error) - dialMutex sync.RWMutex - dialArgsForCall []struct { - network string - address string - config *ssh.ClientConfig - } - dialReturns struct { - result1 cmd.SecureClient - result2 error - } -} - -func (fake *FakeSecureDialer) Dial(network string, address string, config *ssh.ClientConfig) (cmd.SecureClient, error) { - fake.dialMutex.Lock() - fake.dialArgsForCall = append(fake.dialArgsForCall, struct { - network string - address string - config *ssh.ClientConfig - }{network, address, config}) - fake.dialMutex.Unlock() - if fake.DialStub != nil { - return fake.DialStub(network, address, config) - } else { - return fake.dialReturns.result1, fake.dialReturns.result2 - } -} - -func (fake *FakeSecureDialer) DialCallCount() int { - fake.dialMutex.RLock() - defer fake.dialMutex.RUnlock() - return len(fake.dialArgsForCall) -} - -func (fake *FakeSecureDialer) DialArgsForCall(i int) (string, string, *ssh.ClientConfig) { - fake.dialMutex.RLock() - defer fake.dialMutex.RUnlock() - return fake.dialArgsForCall[i].network, fake.dialArgsForCall[i].address, fake.dialArgsForCall[i].config -} - -func (fake *FakeSecureDialer) DialReturns(result1 cmd.SecureClient, result2 error) { - fake.DialStub = nil - fake.dialReturns = struct { - result1 cmd.SecureClient - result2 error - }{result1, result2} -} - -var _ cmd.SecureDialer = new(FakeSecureDialer) diff --git a/cf-plugin/cmd/fakes/fake_secure_session.go b/cf-plugin/cmd/fakes/fake_secure_session.go deleted file mode 100644 index 9a08c18..0000000 --- a/cf-plugin/cmd/fakes/fake_secure_session.go +++ /dev/null @@ -1,333 +0,0 @@ -// This file was generated by counterfeiter -package fakes - -import ( - "io" - "sync" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "golang.org/x/crypto/ssh" -) - -type FakeSecureSession struct { - RequestPtyStub func(term string, height, width int, termModes ssh.TerminalModes) error - requestPtyMutex sync.RWMutex - requestPtyArgsForCall []struct { - term string - height int - width int - termModes ssh.TerminalModes - } - requestPtyReturns struct { - result1 error - } - SendRequestStub func(name string, wantReply bool, payload []byte) (bool, error) - sendRequestMutex sync.RWMutex - sendRequestArgsForCall []struct { - name string - wantReply bool - payload []byte - } - sendRequestReturns struct { - result1 bool - result2 error - } - StdinPipeStub func() (io.WriteCloser, error) - stdinPipeMutex sync.RWMutex - stdinPipeArgsForCall []struct{} - stdinPipeReturns struct { - result1 io.WriteCloser - result2 error - } - StdoutPipeStub func() (io.Reader, error) - stdoutPipeMutex sync.RWMutex - stdoutPipeArgsForCall []struct{} - stdoutPipeReturns struct { - result1 io.Reader - result2 error - } - StderrPipeStub func() (io.Reader, error) - stderrPipeMutex sync.RWMutex - stderrPipeArgsForCall []struct{} - stderrPipeReturns struct { - result1 io.Reader - result2 error - } - StartStub func(command string) error - startMutex sync.RWMutex - startArgsForCall []struct { - command string - } - startReturns struct { - result1 error - } - ShellStub func() error - shellMutex sync.RWMutex - shellArgsForCall []struct{} - shellReturns struct { - result1 error - } - WaitStub func() error - waitMutex sync.RWMutex - waitArgsForCall []struct{} - waitReturns struct { - result1 error - } - CloseStub func() error - closeMutex sync.RWMutex - closeArgsForCall []struct{} - closeReturns struct { - result1 error - } -} - -func (fake *FakeSecureSession) RequestPty(term string, height int, width int, termModes ssh.TerminalModes) error { - fake.requestPtyMutex.Lock() - fake.requestPtyArgsForCall = append(fake.requestPtyArgsForCall, struct { - term string - height int - width int - termModes ssh.TerminalModes - }{term, height, width, termModes}) - fake.requestPtyMutex.Unlock() - if fake.RequestPtyStub != nil { - return fake.RequestPtyStub(term, height, width, termModes) - } else { - return fake.requestPtyReturns.result1 - } -} - -func (fake *FakeSecureSession) RequestPtyCallCount() int { - fake.requestPtyMutex.RLock() - defer fake.requestPtyMutex.RUnlock() - return len(fake.requestPtyArgsForCall) -} - -func (fake *FakeSecureSession) RequestPtyArgsForCall(i int) (string, int, int, ssh.TerminalModes) { - fake.requestPtyMutex.RLock() - defer fake.requestPtyMutex.RUnlock() - return fake.requestPtyArgsForCall[i].term, fake.requestPtyArgsForCall[i].height, fake.requestPtyArgsForCall[i].width, fake.requestPtyArgsForCall[i].termModes -} - -func (fake *FakeSecureSession) RequestPtyReturns(result1 error) { - fake.RequestPtyStub = nil - fake.requestPtyReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeSecureSession) SendRequest(name string, wantReply bool, payload []byte) (bool, error) { - fake.sendRequestMutex.Lock() - fake.sendRequestArgsForCall = append(fake.sendRequestArgsForCall, struct { - name string - wantReply bool - payload []byte - }{name, wantReply, payload}) - fake.sendRequestMutex.Unlock() - if fake.SendRequestStub != nil { - return fake.SendRequestStub(name, wantReply, payload) - } else { - return fake.sendRequestReturns.result1, fake.sendRequestReturns.result2 - } -} - -func (fake *FakeSecureSession) SendRequestCallCount() int { - fake.sendRequestMutex.RLock() - defer fake.sendRequestMutex.RUnlock() - return len(fake.sendRequestArgsForCall) -} - -func (fake *FakeSecureSession) SendRequestArgsForCall(i int) (string, bool, []byte) { - fake.sendRequestMutex.RLock() - defer fake.sendRequestMutex.RUnlock() - return fake.sendRequestArgsForCall[i].name, fake.sendRequestArgsForCall[i].wantReply, fake.sendRequestArgsForCall[i].payload -} - -func (fake *FakeSecureSession) SendRequestReturns(result1 bool, result2 error) { - fake.SendRequestStub = nil - fake.sendRequestReturns = struct { - result1 bool - result2 error - }{result1, result2} -} - -func (fake *FakeSecureSession) StdinPipe() (io.WriteCloser, error) { - fake.stdinPipeMutex.Lock() - fake.stdinPipeArgsForCall = append(fake.stdinPipeArgsForCall, struct{}{}) - fake.stdinPipeMutex.Unlock() - if fake.StdinPipeStub != nil { - return fake.StdinPipeStub() - } else { - return fake.stdinPipeReturns.result1, fake.stdinPipeReturns.result2 - } -} - -func (fake *FakeSecureSession) StdinPipeCallCount() int { - fake.stdinPipeMutex.RLock() - defer fake.stdinPipeMutex.RUnlock() - return len(fake.stdinPipeArgsForCall) -} - -func (fake *FakeSecureSession) StdinPipeReturns(result1 io.WriteCloser, result2 error) { - fake.StdinPipeStub = nil - fake.stdinPipeReturns = struct { - result1 io.WriteCloser - result2 error - }{result1, result2} -} - -func (fake *FakeSecureSession) StdoutPipe() (io.Reader, error) { - fake.stdoutPipeMutex.Lock() - fake.stdoutPipeArgsForCall = append(fake.stdoutPipeArgsForCall, struct{}{}) - fake.stdoutPipeMutex.Unlock() - if fake.StdoutPipeStub != nil { - return fake.StdoutPipeStub() - } else { - return fake.stdoutPipeReturns.result1, fake.stdoutPipeReturns.result2 - } -} - -func (fake *FakeSecureSession) StdoutPipeCallCount() int { - fake.stdoutPipeMutex.RLock() - defer fake.stdoutPipeMutex.RUnlock() - return len(fake.stdoutPipeArgsForCall) -} - -func (fake *FakeSecureSession) StdoutPipeReturns(result1 io.Reader, result2 error) { - fake.StdoutPipeStub = nil - fake.stdoutPipeReturns = struct { - result1 io.Reader - result2 error - }{result1, result2} -} - -func (fake *FakeSecureSession) StderrPipe() (io.Reader, error) { - fake.stderrPipeMutex.Lock() - fake.stderrPipeArgsForCall = append(fake.stderrPipeArgsForCall, struct{}{}) - fake.stderrPipeMutex.Unlock() - if fake.StderrPipeStub != nil { - return fake.StderrPipeStub() - } else { - return fake.stderrPipeReturns.result1, fake.stderrPipeReturns.result2 - } -} - -func (fake *FakeSecureSession) StderrPipeCallCount() int { - fake.stderrPipeMutex.RLock() - defer fake.stderrPipeMutex.RUnlock() - return len(fake.stderrPipeArgsForCall) -} - -func (fake *FakeSecureSession) StderrPipeReturns(result1 io.Reader, result2 error) { - fake.StderrPipeStub = nil - fake.stderrPipeReturns = struct { - result1 io.Reader - result2 error - }{result1, result2} -} - -func (fake *FakeSecureSession) Start(command string) error { - fake.startMutex.Lock() - fake.startArgsForCall = append(fake.startArgsForCall, struct { - command string - }{command}) - fake.startMutex.Unlock() - if fake.StartStub != nil { - return fake.StartStub(command) - } else { - return fake.startReturns.result1 - } -} - -func (fake *FakeSecureSession) StartCallCount() int { - fake.startMutex.RLock() - defer fake.startMutex.RUnlock() - return len(fake.startArgsForCall) -} - -func (fake *FakeSecureSession) StartArgsForCall(i int) string { - fake.startMutex.RLock() - defer fake.startMutex.RUnlock() - return fake.startArgsForCall[i].command -} - -func (fake *FakeSecureSession) StartReturns(result1 error) { - fake.StartStub = nil - fake.startReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeSecureSession) Shell() error { - fake.shellMutex.Lock() - fake.shellArgsForCall = append(fake.shellArgsForCall, struct{}{}) - fake.shellMutex.Unlock() - if fake.ShellStub != nil { - return fake.ShellStub() - } else { - return fake.shellReturns.result1 - } -} - -func (fake *FakeSecureSession) ShellCallCount() int { - fake.shellMutex.RLock() - defer fake.shellMutex.RUnlock() - return len(fake.shellArgsForCall) -} - -func (fake *FakeSecureSession) ShellReturns(result1 error) { - fake.ShellStub = nil - fake.shellReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeSecureSession) Wait() error { - fake.waitMutex.Lock() - fake.waitArgsForCall = append(fake.waitArgsForCall, struct{}{}) - fake.waitMutex.Unlock() - if fake.WaitStub != nil { - return fake.WaitStub() - } else { - return fake.waitReturns.result1 - } -} - -func (fake *FakeSecureSession) WaitCallCount() int { - fake.waitMutex.RLock() - defer fake.waitMutex.RUnlock() - return len(fake.waitArgsForCall) -} - -func (fake *FakeSecureSession) WaitReturns(result1 error) { - fake.WaitStub = nil - fake.waitReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeSecureSession) Close() error { - fake.closeMutex.Lock() - fake.closeArgsForCall = append(fake.closeArgsForCall, struct{}{}) - fake.closeMutex.Unlock() - if fake.CloseStub != nil { - return fake.CloseStub() - } else { - return fake.closeReturns.result1 - } -} - -func (fake *FakeSecureSession) CloseCallCount() int { - fake.closeMutex.RLock() - defer fake.closeMutex.RUnlock() - return len(fake.closeArgsForCall) -} - -func (fake *FakeSecureSession) CloseReturns(result1 error) { - fake.CloseStub = nil - fake.closeReturns = struct { - result1 error - }{result1} -} - -var _ cmd.SecureSession = new(FakeSecureSession) diff --git a/cf-plugin/cmd/get_ssh_code.go b/cf-plugin/cmd/get_ssh_code.go deleted file mode 100644 index 836e4ba..0000000 --- a/cf-plugin/cmd/get_ssh_code.go +++ /dev/null @@ -1,28 +0,0 @@ -package cmd - -import ( - "fmt" - "io" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/credential" -) - -const GetSSHCodeUsage = "cf get-ssh-code" - -func GetSSHCode( - args []string, - credFactory credential.CredentialFactory, - output io.Writer, -) error { - if len(args) != 1 || args[0] != "get-ssh-code" { - return fmt.Errorf("%s\n%s", "Invalid usage", GetSSHCodeUsage) - } - - code, err := credFactory.AuthorizationCode() - if err != nil { - return err - } - - fmt.Fprintf(output, "%s\n", code) - return nil -} diff --git a/cf-plugin/cmd/get_ssh_code_test.go b/cf-plugin/cmd/get_ssh_code_test.go deleted file mode 100644 index c853614..0000000 --- a/cf-plugin/cmd/get_ssh_code_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package cmd_test - -import ( - "bytes" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/credential/credential_fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("GetSSHCode", func() { - var fakeCredFactory *credential_fakes.FakeCredentialFactory - - BeforeEach(func() { - fakeCredFactory = &credential_fakes.FakeCredentialFactory{} - }) - - It("validates the command name", func() { - err := cmd.GetSSHCode([]string{"bogus-name"}, fakeCredFactory, nil) - Expect(err).To(MatchError("Invalid usage\n" + cmd.GetSSHCodeUsage)) - }) - - It("does not accept any arugments", func() { - err := cmd.GetSSHCode([]string{"get-ssh-code", "bogus-argument"}, fakeCredFactory, nil) - Expect(err).To(MatchError("Invalid usage\n" + cmd.GetSSHCodeUsage)) - }) - - It("gets the authorization code from the credential factory", func() { - fakeCredFactory.AuthorizationCodeReturns("xyxpdq", nil) - - writer := &bytes.Buffer{} - err := cmd.GetSSHCode([]string{"get-ssh-code"}, fakeCredFactory, writer) - Expect(err).NotTo(HaveOccurred()) - - Expect(writer.String()).To(Equal("xyxpdq\n")) - }) -}) diff --git a/cf-plugin/cmd/ssh.go b/cf-plugin/cmd/ssh.go deleted file mode 100644 index f379c95..0000000 --- a/cf-plugin/cmd/ssh.go +++ /dev/null @@ -1,474 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "io" - "net" - "os" - "os/signal" - "runtime" - "strings" - "sync" - "syscall" - "time" - - "golang.org/x/crypto/ssh" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/credential" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/info" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/options" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/sigwinch" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/terminal" - "github.com/cloudfoundry-incubator/diego-ssh/helpers" - "github.com/docker/docker/pkg/term" -) - -type SecureShell interface { - Connect(opts *options.SSHOptions) error - InteractiveSession() error - LocalPortForward() error - Wait() error - Close() error -} - -//go:generate counterfeiter -o fakes/fake_secure_dialer.go . SecureDialer -type SecureDialer interface { - Dial(network, address string, config *ssh.ClientConfig) (SecureClient, error) -} - -//go:generate counterfeiter -o fakes/fake_secure_client.go . SecureClient -type SecureClient interface { - NewSession() (SecureSession, error) - Conn() ssh.Conn - Dial(network, address string) (net.Conn, error) - Wait() error - Close() error -} - -//go:generate counterfeiter -o fakes/fake_listener_factory.go . ListenerFactory -type ListenerFactory interface { - Listen(network, address string) (net.Listener, error) -} - -//go:generate counterfeiter -o fakes/fake_secure_session.go . SecureSession -type SecureSession interface { - RequestPty(term string, height, width int, termModes ssh.TerminalModes) error - SendRequest(name string, wantReply bool, payload []byte) (bool, error) - StdinPipe() (io.WriteCloser, error) - StdoutPipe() (io.Reader, error) - StderrPipe() (io.Reader, error) - Start(command string) error - Shell() error - Wait() error - Close() error -} - -type secureShell struct { - secureDialer SecureDialer - terminalHelper terminal.TerminalHelper - listenerFactory ListenerFactory - keepAliveInterval time.Duration - appFactory app.AppFactory - infoFactory info.InfoFactory - credFactory credential.CredentialFactory - secureClient SecureClient - opts *options.SSHOptions - - localListeners []net.Listener -} - -func NewSecureShell( - secureDialer SecureDialer, - terminalHelper terminal.TerminalHelper, - listenerFactory ListenerFactory, - keepAliveInterval time.Duration, - appFactory app.AppFactory, - infoFactory info.InfoFactory, - credFactory credential.CredentialFactory, -) SecureShell { - return &secureShell{ - secureDialer: secureDialer, - terminalHelper: terminalHelper, - listenerFactory: listenerFactory, - keepAliveInterval: keepAliveInterval, - appFactory: appFactory, - infoFactory: infoFactory, - credFactory: credFactory, - localListeners: []net.Listener{}, - } -} - -func (c *secureShell) Connect(opts *options.SSHOptions) error { - app, err := c.appFactory.Get(opts.AppName) - if err != nil { - return err - } - - info, err := c.infoFactory.Get() - if err != nil { - return err - } - - cred, err := c.credFactory.AuthorizationCode() - if err != nil { - return err - } - - err = c.validateTarget(app, opts) - if err != nil { - return err - } - - clientConfig := &ssh.ClientConfig{ - User: fmt.Sprintf("cf:%s/%d", app.Guid, opts.Index), - Auth: []ssh.AuthMethod{ - ssh.Password(cred), - }, - HostKeyCallback: fingerprintCallback(opts, info.SSHEndpointFingerprint), - } - - secureClient, err := c.secureDialer.Dial("tcp", info.SSHEndpoint, clientConfig) - if err != nil { - return err - } - - c.secureClient = secureClient - c.opts = opts - return nil -} - -func (c *secureShell) Close() error { - for _, listener := range c.localListeners { - listener.Close() - } - return c.secureClient.Close() -} - -func (c *secureShell) LocalPortForward() error { - for _, forwardSpec := range c.opts.ForwardSpecs { - listener, err := c.listenerFactory.Listen("tcp", forwardSpec.ListenAddress) - if err != nil { - return err - } - c.localListeners = append(c.localListeners, listener) - - go c.localForwardAcceptLoop(listener, forwardSpec.ConnectAddress) - } - - return nil -} - -func (c *secureShell) localForwardAcceptLoop(listener net.Listener, addr string) { - defer listener.Close() - - for { - conn, err := listener.Accept() - if err != nil { - if netErr, ok := err.(net.Error); ok && netErr.Temporary() { - time.Sleep(100 * time.Millisecond) - continue - } - return - } - - go c.handleForwardConnection(conn, addr) - } -} - -func (c *secureShell) handleForwardConnection(conn net.Conn, targetAddr string) { - defer conn.Close() - - target, err := c.secureClient.Dial("tcp", targetAddr) - if err != nil { - fmt.Printf("connect to %s failed: %s\n", targetAddr, err.Error()) - return - } - defer target.Close() - - wg := &sync.WaitGroup{} - wg.Add(2) - - go copyAndClose(wg, conn, target) - go copyAndClose(wg, target, conn) - wg.Wait() -} - -func copyAndClose(wg *sync.WaitGroup, dest io.WriteCloser, src io.Reader) { - io.Copy(dest, src) - dest.Close() - if wg != nil { - wg.Done() - } -} - -func copyAndDone(wg *sync.WaitGroup, dest io.Writer, src io.Reader) { - io.Copy(dest, src) - wg.Done() -} - -func (c *secureShell) InteractiveSession() error { - secureClient := c.secureClient - opts := c.opts - - session, err := secureClient.NewSession() - if err != nil { - return fmt.Errorf("SSH session allocation failed: %s", err.Error()) - } - defer session.Close() - - stdin, stdout, stderr := c.terminalHelper.StdStreams() - - inPipe, err := session.StdinPipe() - if err != nil { - return err - } - - outPipe, err := session.StdoutPipe() - if err != nil { - return err - } - - errPipe, err := session.StderrPipe() - if err != nil { - return err - } - - stdinFd, stdinIsTerminal := c.terminalHelper.GetFdInfo(stdin) - stdoutFd, stdoutIsTerminal := c.terminalHelper.GetFdInfo(stdout) - - if c.shouldAllocateTerminal(opts, stdinIsTerminal) { - modes := ssh.TerminalModes{ - ssh.ECHO: 1, - ssh.TTY_OP_ISPEED: 115200, - ssh.TTY_OP_OSPEED: 115200, - } - - width, height := c.getWindowDimensions(stdoutFd) - - err = session.RequestPty(c.terminalType(), height, width, modes) - if err != nil { - return err - } - - state, err := c.terminalHelper.SetRawTerminal(stdinFd) - if err == nil { - defer c.terminalHelper.RestoreTerminal(stdinFd, state) - } - } - - if len(opts.Command) != 0 { - cmd := strings.Join(opts.Command, " ") - err = session.Start(cmd) - if err != nil { - return err - } - } else { - err = session.Shell() - if err != nil { - return err - } - } - - wg := &sync.WaitGroup{} - wg.Add(2) - - go copyAndClose(nil, inPipe, stdin) - go copyAndDone(wg, stdout, outPipe) - go copyAndDone(wg, stderr, errPipe) - - if stdoutIsTerminal { - resized := make(chan os.Signal, 16) - - if runtime.GOOS == "windows" { - ticker := time.NewTicker(250 * time.Millisecond) - defer ticker.Stop() - - go func() { - for _ = range ticker.C { - resized <- syscall.Signal(-1) - } - close(resized) - }() - } else { - signal.Notify(resized, sigwinch.SIGWINCH()) - defer func() { signal.Stop(resized); close(resized) }() - } - - go c.resize(resized, session, stdoutFd) - } - - keepaliveStopCh := make(chan struct{}) - defer close(keepaliveStopCh) - - go keepalive(secureClient.Conn(), time.NewTicker(c.keepAliveInterval), keepaliveStopCh) - - result := session.Wait() - wg.Wait() - - return result -} - -func (c *secureShell) Wait() error { - keepaliveStopCh := make(chan struct{}) - defer close(keepaliveStopCh) - - go keepalive(c.secureClient.Conn(), time.NewTicker(c.keepAliveInterval), keepaliveStopCh) - - return c.secureClient.Wait() -} - -func (c *secureShell) validateTarget(app app.App, opts *options.SSHOptions) error { - if app.State != "STARTED" { - return fmt.Errorf("Application %q is not in the STARTED state", opts.AppName) - } - - if !app.Diego { - return fmt.Errorf("Application %q is not running on Diego", opts.AppName) - } - - return nil -} - -type hostKeyCallback func(hostname string, remote net.Addr, key ssh.PublicKey) error - -func fingerprintCallback(opts *options.SSHOptions, expectedFingerprint string) hostKeyCallback { - if opts.SkipHostValidation { - return nil - } - - return func(hostname string, remote net.Addr, key ssh.PublicKey) error { - switch len(expectedFingerprint) { - case helpers.SHA1_FINGERPRINT_LENGTH: - fingerprint := helpers.SHA1Fingerprint(key) - if fingerprint != expectedFingerprint { - return fmt.Errorf("Host key verification failed.\n\nThe fingerprint of the received key was %q.", fingerprint) - } - case helpers.MD5_FINGERPRINT_LENGTH: - fingerprint := helpers.MD5Fingerprint(key) - if fingerprint != expectedFingerprint { - return fmt.Errorf("Host key verification failed.\n\nThe fingerprint of the received key was %q.", fingerprint) - } - case 0: - fingerprint := helpers.MD5Fingerprint(key) - return fmt.Errorf("Unable to verify identity of host.\n\nThe fingerprint of the received key was %q.", fingerprint) - default: - return errors.New("Unsupported host key fingerprint format") - } - return nil - } -} - -func (c *secureShell) shouldAllocateTerminal(opts *options.SSHOptions, stdinIsTerminal bool) bool { - switch opts.TerminalRequest { - case options.REQUEST_TTY_FORCE: - return true - case options.REQUEST_TTY_NO: - return false - case options.REQUEST_TTY_YES: - return stdinIsTerminal - case options.REQUEST_TTY_AUTO: - return len(opts.Command) == 0 && stdinIsTerminal - default: - return false - } -} - -func (c *secureShell) resize(resized <-chan os.Signal, session SecureSession, terminalFd uintptr) { - type resizeMessage struct { - Width uint32 - Height uint32 - PixelWidth uint32 - PixelHeight uint32 - } - - var previousWidth, previousHeight int - - for _ = range resized { - width, height := c.getWindowDimensions(terminalFd) - - if width == previousWidth && height == previousHeight { - continue - } - - message := resizeMessage{ - Width: uint32(width), - Height: uint32(height), - } - - session.SendRequest("window-change", false, ssh.Marshal(message)) - - previousWidth = width - previousHeight = height - } -} - -func keepalive(conn ssh.Conn, ticker *time.Ticker, stopCh chan struct{}) { - for { - select { - case <-ticker.C: - conn.SendRequest("keepalive@cloudfoundry.org", true, nil) - case <-stopCh: - ticker.Stop() - return - } - } -} - -func (c *secureShell) terminalType() string { - term := os.Getenv("TERM") - if term == "" { - term = "xterm" - } - return term -} - -func (c *secureShell) getWindowDimensions(terminalFd uintptr) (width int, height int) { - winSize, err := c.terminalHelper.GetWinsize(terminalFd) - if err != nil { - winSize = &term.Winsize{ - Width: 80, - Height: 43, - } - } - - return int(winSize.Width), int(winSize.Height) -} - -type secureDialer struct{} - -func (d *secureDialer) Dial(network string, address string, config *ssh.ClientConfig) (SecureClient, error) { - client, err := ssh.Dial(network, address, config) - if err != nil { - return nil, err - } - - return &secureClient{client: client}, nil -} - -func DefaultSecureDialer() SecureDialer { - return &secureDialer{} -} - -type secureClient struct{ client *ssh.Client } - -func (sc *secureClient) Close() error { return sc.client.Close() } -func (sc *secureClient) Conn() ssh.Conn { return sc.client.Conn } -func (sc *secureClient) Wait() error { return sc.client.Wait() } -func (sc *secureClient) Dial(n, addr string) (net.Conn, error) { - return sc.client.Dial(n, addr) -} -func (sc *secureClient) NewSession() (SecureSession, error) { - return sc.client.NewSession() -} - -type listenerFactory struct{} - -func (lf *listenerFactory) Listen(network, address string) (net.Listener, error) { - return net.Listen(network, address) -} - -func DefaultListenerFactory() ListenerFactory { - return &listenerFactory{} -} diff --git a/cf-plugin/cmd/ssh_allowed.go b/cf-plugin/cmd/ssh_allowed.go deleted file mode 100644 index 7ad8dff..0000000 --- a/cf-plugin/cmd/ssh_allowed.go +++ /dev/null @@ -1,24 +0,0 @@ -package cmd - -import ( - "fmt" - "io" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space" -) - -const SSHAllowedUsage = "cf space-ssh-allowed SPACE_NAME" - -func SSHAllowed(args []string, spaceFactory space.SpaceFactory, output io.Writer) error { - if len(args) != 2 || args[0] != "space-ssh-allowed" { - return fmt.Errorf("%s\n%s", "Invalid usage", SSHAllowedUsage) - } - - space, err := spaceFactory.Get(args[1]) - if err != nil { - return err - } - - fmt.Fprintf(output, "%t", space.AllowSSH) - return nil -} diff --git a/cf-plugin/cmd/ssh_allowed_test.go b/cf-plugin/cmd/ssh_allowed_test.go deleted file mode 100644 index 15aa923..0000000 --- a/cf-plugin/cmd/ssh_allowed_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package cmd_test - -import ( - "bytes" - "errors" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space/space_fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("SSHAllowed", func() { - var fakeSpaceFactory *space_fakes.FakeSpaceFactory - var mySpace space.Space - - BeforeEach(func() { - fakeSpaceFactory = &space_fakes.FakeSpaceFactory{} - mySpace = space.Space{Guid: "myguid"} - }) - - Context("validation", func() { - It("requires an spacelication name", func() { - err := cmd.SSHAllowed([]string{"space-ssh-allowed"}, fakeSpaceFactory, nil) - - Expect(err).To(MatchError("Invalid usage\n" + cmd.SSHAllowedUsage)) - }) - - It("validates the command name", func() { - err := cmd.SSHAllowed([]string{"bogus", "space"}, fakeSpaceFactory, nil) - - Expect(err).To(MatchError("Invalid usage\n" + cmd.SSHAllowedUsage)) - }) - }) - - It("returns the value", func() { - mySpace.AllowSSH = true - fakeSpaceFactory.GetReturns(mySpace, nil) - writer := bytes.NewBuffer(nil) - - err := cmd.SSHAllowed([]string{"space-ssh-allowed", "myspace"}, fakeSpaceFactory, writer) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeSpaceFactory.GetCallCount()).To(Equal(1)) - Expect(fakeSpaceFactory.GetArgsForCall(0)).To(Equal("myspace")) - Expect(writer.String()).To(Equal("true")) - }) - - Context("when retrieving the Space fails", func() { - BeforeEach(func() { - fakeSpaceFactory.GetReturns(space.Space{}, errors.New("get failed")) - }) - - It("returns an err", func() { - err := cmd.SSHAllowed([]string{"space-ssh-allowed", "myspace"}, fakeSpaceFactory, nil) - Expect(err).To(MatchError("get failed")) - Expect(fakeSpaceFactory.GetCallCount()).To(Equal(1)) - }) - }) -}) diff --git a/cf-plugin/cmd/ssh_enabled.go b/cf-plugin/cmd/ssh_enabled.go deleted file mode 100644 index 39a9419..0000000 --- a/cf-plugin/cmd/ssh_enabled.go +++ /dev/null @@ -1,24 +0,0 @@ -package cmd - -import ( - "fmt" - "io" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app" -) - -const SSHEnabledUsage = "cf ssh-enabled APP_NAME" - -func SSHEnabled(args []string, appFactory app.AppFactory, output io.Writer) error { - if len(args) != 2 || args[0] != "ssh-enabled" { - return fmt.Errorf("%s\n%s", "Invalid usage", SSHEnabledUsage) - } - - app, err := appFactory.Get(args[1]) - if err != nil { - return err - } - - fmt.Fprintf(output, "%t", app.EnableSSH) - return nil -} diff --git a/cf-plugin/cmd/ssh_enabled_test.go b/cf-plugin/cmd/ssh_enabled_test.go deleted file mode 100644 index 05c0a8a..0000000 --- a/cf-plugin/cmd/ssh_enabled_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package cmd_test - -import ( - "bytes" - "errors" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app/app_fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("SSHEnabled", func() { - var fakeAppFactory *app_fakes.FakeAppFactory - var myApp app.App - - BeforeEach(func() { - fakeAppFactory = &app_fakes.FakeAppFactory{} - myApp = app.App{Guid: "myguid"} - }) - - Context("validation", func() { - It("requires an application name", func() { - err := cmd.SSHEnabled([]string{"ssh-enabled"}, fakeAppFactory, nil) - - Expect(err).To(MatchError("Invalid usage\n" + cmd.SSHEnabledUsage)) - }) - - It("validates the command name", func() { - err := cmd.SSHEnabled([]string{"ssh-enable", "app"}, fakeAppFactory, nil) - - Expect(err).To(MatchError("Invalid usage\n" + cmd.SSHEnabledUsage)) - }) - }) - - It("returns the value", func() { - myApp.EnableSSH = true - fakeAppFactory.GetReturns(myApp, nil) - writer := bytes.NewBuffer(nil) - - err := cmd.SSHEnabled([]string{"ssh-enabled", "myapp"}, fakeAppFactory, writer) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - Expect(fakeAppFactory.GetArgsForCall(0)).To(Equal("myapp")) - Expect(writer.String()).To(Equal("true")) - }) - - Context("when retrieving the App fails", func() { - BeforeEach(func() { - fakeAppFactory.GetReturns(app.App{}, errors.New("get failed")) - }) - - It("returns an err", func() { - err := cmd.SSHEnabled([]string{"ssh-enabled", "myapp"}, fakeAppFactory, nil) - Expect(err).To(MatchError("get failed")) - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - }) - }) -}) diff --git a/cf-plugin/cmd/ssh_test.go b/cf-plugin/cmd/ssh_test.go deleted file mode 100644 index 47ed5ba..0000000 --- a/cf-plugin/cmd/ssh_test.go +++ /dev/null @@ -1,1297 +0,0 @@ -// +build !windows - -package cmd_test - -import ( - "errors" - "fmt" - "io" - "net" - "os" - "syscall" - "time" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd/fakes" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app/app_fakes" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/credential/credential_fakes" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/info" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/info/info_fakes" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/options" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/terminal" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/terminal/terminal_helper_fakes" - "github.com/cloudfoundry-incubator/diego-ssh/server" - fake_server "github.com/cloudfoundry-incubator/diego-ssh/server/fakes" - "github.com/cloudfoundry-incubator/diego-ssh/test_helpers" - "github.com/cloudfoundry-incubator/diego-ssh/test_helpers/fake_io" - "github.com/cloudfoundry-incubator/diego-ssh/test_helpers/fake_net" - "github.com/cloudfoundry-incubator/diego-ssh/test_helpers/fake_ssh" - "github.com/docker/docker/pkg/term" - "github.com/kr/pty" - "github.com/pivotal-golang/lager/lagertest" - "golang.org/x/crypto/ssh" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Diego SSH Plugin", func() { - var ( - fakeTerminalHelper *terminal_helper_fakes.FakeTerminalHelper - fakeListenerFactory *fakes.FakeListenerFactory - fakeAppFactory *app_fakes.FakeAppFactory - fakeInfoFactory *info_fakes.FakeInfoFactory - fakeCredFactory *credential_fakes.FakeCredentialFactory - - fakeConnection *fake_ssh.FakeConn - fakeSecureClient *fakes.FakeSecureClient - fakeSecureDialer *fakes.FakeSecureDialer - fakeSecureSession *fakes.FakeSecureSession - - terminalHelper terminal.TerminalHelper - keepAliveDuration time.Duration - secureShell cmd.SecureShell - - stdinPipe *fake_io.FakeWriteCloser - ) - - BeforeEach(func() { - fakeTerminalHelper = &terminal_helper_fakes.FakeTerminalHelper{} - terminalHelper = terminal.DefaultHelper() - - fakeListenerFactory = &fakes.FakeListenerFactory{} - fakeListenerFactory.ListenStub = net.Listen - - keepAliveDuration = 30 * time.Second - - fakeAppFactory = &app_fakes.FakeAppFactory{} - fakeInfoFactory = &info_fakes.FakeInfoFactory{} - fakeCredFactory = &credential_fakes.FakeCredentialFactory{} - - fakeConnection = &fake_ssh.FakeConn{} - fakeSecureClient = &fakes.FakeSecureClient{} - fakeSecureDialer = &fakes.FakeSecureDialer{} - fakeSecureSession = &fakes.FakeSecureSession{} - - fakeSecureDialer.DialReturns(fakeSecureClient, nil) - fakeSecureClient.NewSessionReturns(fakeSecureSession, nil) - fakeSecureClient.ConnReturns(fakeConnection) - - stdinPipe = &fake_io.FakeWriteCloser{} - stdinPipe.WriteStub = func(p []byte) (int, error) { - return len(p), nil - } - - stdoutPipe := &fake_io.FakeReader{} - stdoutPipe.ReadStub = func(p []byte) (int, error) { - return 0, io.EOF - } - - stderrPipe := &fake_io.FakeReader{} - stderrPipe.ReadStub = func(p []byte) (int, error) { - return 0, io.EOF - } - - fakeSecureSession.StdinPipeReturns(stdinPipe, nil) - fakeSecureSession.StdoutPipeReturns(stdoutPipe, nil) - fakeSecureSession.StderrPipeReturns(stderrPipe, nil) - }) - - JustBeforeEach(func() { - secureShell = cmd.NewSecureShell( - fakeSecureDialer, - terminalHelper, - fakeListenerFactory, - keepAliveDuration, - fakeAppFactory, - fakeInfoFactory, - fakeCredFactory, - ) - }) - - Describe("Validation", func() { - var connectErr error - var opts *options.SSHOptions - - BeforeEach(func() { - opts = &options.SSHOptions{ - AppName: "app-1", - } - }) - - JustBeforeEach(func() { - connectErr = secureShell.Connect(opts) - }) - - Context("when there is an error getting the app model", func() { - BeforeEach(func() { - fakeAppFactory.GetReturns(app.App{}, errors.New("woops")) - }) - - It("returns the error", func() { - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - Expect(connectErr).To(Equal(errors.New("woops"))) - }) - - It("does not attempt to acquire endpoint info", func() { - Expect(fakeInfoFactory.GetCallCount()).To(Equal(0)) - }) - }) - - Context("when the app model is successfully acquired", func() { - BeforeEach(func() { - fakeAppFactory.GetReturns(app.App{}, nil) - fakeInfoFactory.GetReturns(info.Info{}, nil) - }) - - It("gets the ssh endpoint information", func() { - Expect(fakeInfoFactory.GetCallCount()).To(Equal(1)) - }) - - Context("when getting the endpoint info fails", func() { - BeforeEach(func() { - fakeInfoFactory.GetReturns(info.Info{}, errors.New("woops")) - }) - - It("returns the error", func() { - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - Expect(connectErr).To(Equal(errors.New("woops"))) - }) - }) - }) - - Context("when the app model and endpoint info are successfully acquired", func() { - BeforeEach(func() { - fakeAppFactory.GetReturns(app.App{ - State: "STARTED", - Diego: true, - }, nil) - fakeInfoFactory.GetReturns(info.Info{}, nil) - fakeCredFactory.AuthorizationCodeReturns("", nil) - }) - - It("gets the current oauth token credential", func() { - Expect(fakeCredFactory.AuthorizationCodeCallCount()).To(Equal(1)) - }) - - Context("when getting the credential fails", func() { - BeforeEach(func() { - fakeCredFactory.AuthorizationCodeReturns("", errors.New("woops")) - }) - - It("returns the error", func() { - Expect(fakeCredFactory.AuthorizationCodeCallCount()).To(Equal(1)) - Expect(connectErr).To(Equal(errors.New("woops"))) - }) - }) - - Context("when the app is not in the 'STARTED' state", func() { - BeforeEach(func() { - stoppedApp := app.App{ - State: "STOPPED", - } - fakeAppFactory.GetReturns(stoppedApp, nil) - }) - - It("returns an error", func() { - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - Expect(connectErr).To(MatchError(MatchRegexp("Application.*not in the STARTED state"))) - }) - }) - - Context("when the app is not a Diego app", func() { - BeforeEach(func() { - deaApp := app.App{ - State: "STARTED", - Diego: false, - } - fakeAppFactory.GetReturns(deaApp, nil) - }) - - It("returns an error", func() { - Expect(fakeAppFactory.GetCallCount()).To(Equal(1)) - Expect(connectErr).To(MatchError(MatchRegexp("Application.*not running on Diego"))) - }) - }) - - Context("when dialing fails", func() { - var dialError = errors.New("woops") - - BeforeEach(func() { - fakeSecureDialer.DialReturns(nil, dialError) - }) - - It("returns the dial error", func() { - Expect(connectErr).To(Equal(dialError)) - Expect(fakeSecureDialer.DialCallCount()).To(Equal(1)) - }) - }) - }) - }) - - Describe("InteractiveSession", func() { - var opts *options.SSHOptions - var sessionError error - var interactiveSessionInvoker func(secureShell cmd.SecureShell) - - BeforeEach(func() { - sshInfo := info.Info{ - SSHEndpoint: "ssh.example.com:22", - SSHEndpointFingerprint: TestHostKeyFingerprint, - } - - app := app.App{ - Guid: "app-guid", - EnableSSH: true, - Diego: true, - State: "STARTED", - } - - opts = &options.SSHOptions{ - AppName: "app-name", - Index: 2, - } - - fakeAppFactory.GetReturns(app, nil) - fakeCredFactory.AuthorizationCodeReturns("abc123", nil) - fakeInfoFactory.GetReturns(sshInfo, nil) - - interactiveSessionInvoker = func(secureShell cmd.SecureShell) { - sessionError = secureShell.InteractiveSession() - } - }) - - JustBeforeEach(func() { - connectErr := secureShell.Connect(opts) - Expect(connectErr).NotTo(HaveOccurred()) - interactiveSessionInvoker(secureShell) - }) - - It("dials the correct endpoint as the correct user", func() { - Expect(fakeSecureDialer.DialCallCount()).To(Equal(1)) - - network, address, config := fakeSecureDialer.DialArgsForCall(0) - Expect(network).To(Equal("tcp")) - Expect(address).To(Equal("ssh.example.com:22")) - Expect(config.Auth).NotTo(BeEmpty()) - Expect(config.User).To(Equal("cf:app-guid/2")) - Expect(config.HostKeyCallback).NotTo(BeNil()) - }) - - Context("when host key validation is enabled", func() { - var callback func(hostname string, remote net.Addr, key ssh.PublicKey) error - var addr net.Addr - - JustBeforeEach(func() { - Expect(fakeSecureDialer.DialCallCount()).To(Equal(1)) - _, _, config := fakeSecureDialer.DialArgsForCall(0) - callback = config.HostKeyCallback - - listener, err := net.Listen("tcp", "localhost:0") - Expect(err).NotTo(HaveOccurred()) - - addr = listener.Addr() - listener.Close() - }) - - Context("when the SHA1 fingerprint does not match", func() { - BeforeEach(func() { - info := info.Info{ - SSHEndpointFingerprint: "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00", - } - fakeInfoFactory.GetReturns(info, nil) - }) - - It("returns an error'", func() { - err := callback("", addr, TestHostKey.PublicKey()) - Expect(err).To(MatchError(MatchRegexp("Host key verification failed\\."))) - Expect(err).To(MatchError(MatchRegexp("The fingerprint of the received key was \".*\""))) - }) - }) - - Context("when the MD5 fingerprint does not match", func() { - BeforeEach(func() { - info := info.Info{ - SSHEndpointFingerprint: "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00", - } - fakeInfoFactory.GetReturns(info, nil) - }) - - It("returns an error'", func() { - err := callback("", addr, TestHostKey.PublicKey()) - Expect(err).To(MatchError(MatchRegexp("Host key verification failed\\."))) - Expect(err).To(MatchError(MatchRegexp("The fingerprint of the received key was \".*\""))) - }) - }) - - Context("when no fingerprint is present in endpoint info", func() { - BeforeEach(func() { - info := info.Info{} - fakeInfoFactory.GetReturns(info, nil) - }) - - It("returns an error'", func() { - err := callback("", addr, TestHostKey.PublicKey()) - Expect(err).To(MatchError(MatchRegexp("Unable to verify identity of host\\."))) - Expect(err).To(MatchError(MatchRegexp("The fingerprint of the received key was \".*\""))) - }) - }) - - Context("when the fingerprint length doesn't make sense", func() { - BeforeEach(func() { - info := info.Info{ - SSHEndpointFingerprint: "garbage", - } - fakeInfoFactory.GetReturns(info, nil) - }) - - It("returns an error", func() { - err := callback("", addr, TestHostKey.PublicKey()) - Eventually(err).Should(MatchError(MatchRegexp("Unsupported host key fingerprint format"))) - }) - }) - }) - - Context("when the skip host validation flag is set", func() { - BeforeEach(func() { - opts.SkipHostValidation = true - }) - - It("removes the HostKeyCallback from the client config", func() { - Expect(fakeSecureDialer.DialCallCount()).To(Equal(1)) - - _, _, config := fakeSecureDialer.DialArgsForCall(0) - Expect(config.HostKeyCallback).To(BeNil()) - }) - }) - - Context("when dialing is successful", func() { - BeforeEach(func() { - fakeTerminalHelper.StdStreamsStub = terminalHelper.StdStreams - terminalHelper = fakeTerminalHelper - }) - - It("creates a new secure shell session", func() { - Expect(fakeSecureClient.NewSessionCallCount()).To(Equal(1)) - }) - - It("closes the session", func() { - Expect(fakeSecureSession.CloseCallCount()).To(Equal(1)) - }) - - It("allocates standard streams", func() { - Expect(fakeTerminalHelper.StdStreamsCallCount()).To(Equal(1)) - }) - - It("gets a stdin pipe for the session", func() { - Expect(fakeSecureSession.StdinPipeCallCount()).To(Equal(1)) - }) - - Context("when getting the stdin pipe fails", func() { - BeforeEach(func() { - fakeSecureSession.StdinPipeReturns(nil, errors.New("woops")) - }) - - It("returns the error", func() { - Expect(sessionError).Should(MatchError("woops")) - }) - }) - - It("gets a stdout pipe for the session", func() { - Expect(fakeSecureSession.StdoutPipeCallCount()).To(Equal(1)) - }) - - Context("when getting the stdout pipe fails", func() { - BeforeEach(func() { - fakeSecureSession.StdoutPipeReturns(nil, errors.New("woops")) - }) - - It("returns the error", func() { - Expect(sessionError).Should(MatchError("woops")) - }) - }) - - It("gets a stderr pipe for the session", func() { - Expect(fakeSecureSession.StderrPipeCallCount()).To(Equal(1)) - }) - - Context("when getting the stderr pipe fails", func() { - BeforeEach(func() { - fakeSecureSession.StderrPipeReturns(nil, errors.New("woops")) - }) - - It("returns the error", func() { - Expect(sessionError).Should(MatchError("woops")) - }) - }) - }) - - Context("when stdin is a terminal", func() { - var master, slave *os.File - - BeforeEach(func() { - _, stdout, stderr := terminalHelper.StdStreams() - - var err error - master, slave, err = pty.Open() - Expect(err).NotTo(HaveOccurred()) - - fakeTerminalHelper.IsTerminalStub = terminalHelper.IsTerminal - fakeTerminalHelper.GetFdInfoStub = terminalHelper.GetFdInfo - fakeTerminalHelper.GetWinsizeStub = terminalHelper.GetWinsize - fakeTerminalHelper.StdStreamsReturns(slave, stdout, stderr) - terminalHelper = fakeTerminalHelper - }) - - AfterEach(func() { - master.Close() - // slave.Close() // race - }) - - Context("when a command is not specified", func() { - var terminalType string - - BeforeEach(func() { - terminalType = os.Getenv("TERM") - os.Setenv("TERM", "test-terminal-type") - - winsize := &term.Winsize{Width: 1024, Height: 256} - fakeTerminalHelper.GetWinsizeReturns(winsize, nil) - - fakeSecureSession.ShellStub = func() error { - Expect(fakeTerminalHelper.SetRawTerminalCallCount()).To(Equal(1)) - Expect(fakeTerminalHelper.RestoreTerminalCallCount()).To(Equal(0)) - return nil - } - }) - - AfterEach(func() { - os.Setenv("TERM", terminalType) - }) - - It("requests a pty with the correct terminal type, window size, and modes", func() { - Expect(fakeSecureSession.RequestPtyCallCount()).To(Equal(1)) - Expect(fakeTerminalHelper.GetWinsizeCallCount()).To(Equal(1)) - - termType, height, width, modes := fakeSecureSession.RequestPtyArgsForCall(0) - Expect(termType).To(Equal("test-terminal-type")) - Expect(height).To(Equal(256)) - Expect(width).To(Equal(1024)) - - expectedModes := ssh.TerminalModes{ - ssh.ECHO: 1, - ssh.TTY_OP_ISPEED: 115200, - ssh.TTY_OP_OSPEED: 115200, - } - Expect(modes).To(Equal(expectedModes)) - }) - - Context("when the TERM environment variable is not set", func() { - BeforeEach(func() { - os.Unsetenv("TERM") - }) - - It("requests a pty with the default terminal type", func() { - Expect(fakeSecureSession.RequestPtyCallCount()).To(Equal(1)) - - termType, _, _, _ := fakeSecureSession.RequestPtyArgsForCall(0) - Expect(termType).To(Equal("xterm")) - }) - }) - - It("puts the terminal into raw mode and restores it after running the shell", func() { - Expect(fakeSecureSession.ShellCallCount()).To(Equal(1)) - Expect(fakeTerminalHelper.SetRawTerminalCallCount()).To(Equal(1)) - Expect(fakeTerminalHelper.RestoreTerminalCallCount()).To(Equal(1)) - }) - - Context("when the pty allocation fails", func() { - var ptyError error - - BeforeEach(func() { - ptyError = errors.New("pty allocation error") - fakeSecureSession.RequestPtyReturns(ptyError) - }) - - It("returns the error", func() { - Expect(sessionError).To(Equal(ptyError)) - }) - }) - - Context("when placing the terminal into raw mode fails", func() { - BeforeEach(func() { - fakeTerminalHelper.SetRawTerminalReturns(nil, errors.New("woops")) - }) - - It("keeps calm and carries on", func() { - Expect(fakeSecureSession.ShellCallCount()).To(Equal(1)) - }) - - It("does not not restore the terminal", func() { - Expect(fakeSecureSession.ShellCallCount()).To(Equal(1)) - Expect(fakeTerminalHelper.SetRawTerminalCallCount()).To(Equal(1)) - Expect(fakeTerminalHelper.RestoreTerminalCallCount()).To(Equal(0)) - }) - }) - }) - - Context("when a command is specified", func() { - BeforeEach(func() { - opts.Command = []string{"echo", "-n", "hello"} - }) - - Context("when a terminal is requested", func() { - BeforeEach(func() { - opts.TerminalRequest = options.REQUEST_TTY_YES - }) - - It("requests a pty", func() { - Expect(fakeSecureSession.RequestPtyCallCount()).To(Equal(1)) - }) - }) - - Context("when a terminal is not explicitly requested", func() { - It("does not request a pty", func() { - Expect(fakeSecureSession.RequestPtyCallCount()).To(Equal(0)) - }) - }) - }) - }) - - Context("when stdin is not a terminal", func() { - BeforeEach(func() { - _, stdout, stderr := terminalHelper.StdStreams() - - stdin := &fake_io.FakeReadCloser{} - stdin.ReadStub = func(p []byte) (int, error) { - return 0, io.EOF - } - - fakeTerminalHelper.IsTerminalStub = terminalHelper.IsTerminal - fakeTerminalHelper.GetFdInfoStub = terminalHelper.GetFdInfo - fakeTerminalHelper.GetWinsizeStub = terminalHelper.GetWinsize - fakeTerminalHelper.StdStreamsReturns(stdin, stdout, stderr) - terminalHelper = fakeTerminalHelper - }) - - Context("when a terminal is not requested", func() { - It("does not request a pty", func() { - Expect(fakeSecureSession.RequestPtyCallCount()).To(Equal(0)) - }) - }) - - Context("when a terminal is requested", func() { - BeforeEach(func() { - opts.TerminalRequest = options.REQUEST_TTY_YES - }) - - It("does not request a pty", func() { - Expect(fakeSecureSession.RequestPtyCallCount()).To(Equal(0)) - }) - }) - }) - - Context("when a terminal is forced", func() { - BeforeEach(func() { - opts.TerminalRequest = options.REQUEST_TTY_FORCE - }) - - It("requests a pty", func() { - Expect(fakeSecureSession.RequestPtyCallCount()).To(Equal(1)) - }) - }) - - Context("when a terminal is disabled", func() { - BeforeEach(func() { - opts.TerminalRequest = options.REQUEST_TTY_NO - }) - - It("does not request a pty", func() { - Expect(fakeSecureSession.RequestPtyCallCount()).To(Equal(0)) - }) - }) - - Context("when a command is not specified", func() { - It("requests an interactive shell", func() { - Expect(fakeSecureSession.ShellCallCount()).To(Equal(1)) - }) - - Context("when the shell request returns an error", func() { - BeforeEach(func() { - fakeSecureSession.ShellReturns(errors.New("oh bother")) - }) - - It("returns the error", func() { - Expect(sessionError).To(MatchError("oh bother")) - }) - }) - }) - - Context("when a command is specifed", func() { - BeforeEach(func() { - opts.Command = []string{"echo", "-n", "hello"} - }) - - It("starts the command", func() { - Expect(fakeSecureSession.StartCallCount()).To(Equal(1)) - Expect(fakeSecureSession.StartArgsForCall(0)).To(Equal("echo -n hello")) - }) - - Context("when the command fails to start", func() { - BeforeEach(func() { - fakeSecureSession.StartReturns(errors.New("oh well")) - }) - - It("returns the error", func() { - Expect(sessionError).To(MatchError("oh well")) - }) - }) - }) - - Context("when the shell or command has started", func() { - var ( - stdin *fake_io.FakeReadCloser - stdout, stderr *fake_io.FakeWriter - stdinPipe *fake_io.FakeWriteCloser - stdoutPipe, stderrPipe *fake_io.FakeReader - ) - - BeforeEach(func() { - stdin = &fake_io.FakeReadCloser{} - stdin.ReadStub = func(p []byte) (int, error) { - p[0] = 0 - return 1, io.EOF - } - stdinPipe = &fake_io.FakeWriteCloser{} - stdinPipe.WriteStub = func(p []byte) (int, error) { - defer GinkgoRecover() - Expect(p[0]).To(Equal(byte(0))) - return 1, nil - } - - stdoutPipe = &fake_io.FakeReader{} - stdoutPipe.ReadStub = func(p []byte) (int, error) { - p[0] = 1 - return 1, io.EOF - } - stdout = &fake_io.FakeWriter{} - stdout.WriteStub = func(p []byte) (int, error) { - defer GinkgoRecover() - Expect(p[0]).To(Equal(byte(1))) - return 1, nil - } - - stderrPipe = &fake_io.FakeReader{} - stderrPipe.ReadStub = func(p []byte) (int, error) { - p[0] = 2 - return 1, io.EOF - } - stderr = &fake_io.FakeWriter{} - stderr.WriteStub = func(p []byte) (int, error) { - defer GinkgoRecover() - Expect(p[0]).To(Equal(byte(2))) - return 1, nil - } - - fakeTerminalHelper.StdStreamsReturns(stdin, stdout, stderr) - terminalHelper = fakeTerminalHelper - - fakeSecureSession.StdinPipeReturns(stdinPipe, nil) - fakeSecureSession.StdoutPipeReturns(stdoutPipe, nil) - fakeSecureSession.StderrPipeReturns(stderrPipe, nil) - - fakeSecureSession.WaitReturns(errors.New("error result")) - }) - - It("copies data from the stdin stream to the session stdin pipe", func() { - Eventually(stdin.ReadCallCount).Should(Equal(1)) - Eventually(stdinPipe.WriteCallCount).Should(Equal(1)) - }) - - It("copies data from the session stdout pipe to the stdout stream", func() { - Eventually(stdoutPipe.ReadCallCount).Should(Equal(1)) - Eventually(stdout.WriteCallCount).Should(Equal(1)) - }) - - It("copies data from the session stderr pipe to the stderr stream", func() { - Eventually(stderrPipe.ReadCallCount).Should(Equal(1)) - Eventually(stderr.WriteCallCount).Should(Equal(1)) - }) - - It("waits for the session to end", func() { - Expect(fakeSecureSession.WaitCallCount()).To(Equal(1)) - }) - - It("returns the result from wait", func() { - Expect(sessionError).To(MatchError("error result")) - }) - - Context("when the session terminates before stream copies complete", func() { - var sessionErrorCh chan error - - BeforeEach(func() { - sessionErrorCh = make(chan error, 1) - - interactiveSessionInvoker = func(secureShell cmd.SecureShell) { - go func() { sessionErrorCh <- secureShell.InteractiveSession() }() - } - - stdoutPipe.ReadStub = func(p []byte) (int, error) { - defer GinkgoRecover() - Eventually(fakeSecureSession.WaitCallCount).Should(Equal(1)) - Consistently(sessionErrorCh).ShouldNot(Receive()) - - p[0] = 1 - return 1, io.EOF - } - - stderrPipe.ReadStub = func(p []byte) (int, error) { - defer GinkgoRecover() - Eventually(fakeSecureSession.WaitCallCount).Should(Equal(1)) - Consistently(sessionErrorCh).ShouldNot(Receive()) - - p[0] = 2 - return 1, io.EOF - } - }) - - It("waits for the copies to complete", func() { - Eventually(sessionErrorCh).Should(Receive()) - Expect(stdoutPipe.ReadCallCount()).To(Equal(1)) - Expect(stderrPipe.ReadCallCount()).To(Equal(1)) - }) - }) - - Context("when stdin is closed", func() { - BeforeEach(func() { - stdin.ReadStub = func(p []byte) (int, error) { - defer GinkgoRecover() - Consistently(stdinPipe.CloseCallCount).Should(Equal(0)) - p[0] = 0 - return 1, io.EOF - } - }) - - It("closes the stdinPipe", func() { - Eventually(stdinPipe.CloseCallCount).Should(Equal(1)) - }) - }) - }) - - Context("when stdout is a terminal and a window size change occurs", func() { - var master, slave *os.File - - BeforeEach(func() { - stdin, _, stderr := terminalHelper.StdStreams() - - var err error - master, slave, err = pty.Open() - Expect(err).NotTo(HaveOccurred()) - - fakeTerminalHelper.IsTerminalStub = terminalHelper.IsTerminal - fakeTerminalHelper.GetFdInfoStub = terminalHelper.GetFdInfo - fakeTerminalHelper.GetWinsizeStub = terminalHelper.GetWinsize - fakeTerminalHelper.StdStreamsReturns(stdin, slave, stderr) - terminalHelper = fakeTerminalHelper - - winsize := &term.Winsize{Height: 100, Width: 100} - err = term.SetWinsize(slave.Fd(), winsize) - Expect(err).NotTo(HaveOccurred()) - - fakeSecureSession.WaitStub = func() error { - fakeSecureSession.SendRequestCallCount() - Expect(fakeSecureSession.SendRequestCallCount()).To(Equal(0)) - - // No dimension change - for i := 0; i < 3; i++ { - winsize := &term.Winsize{Height: 100, Width: 100} - err = term.SetWinsize(slave.Fd(), winsize) - Expect(err).NotTo(HaveOccurred()) - } - - winsize := &term.Winsize{Height: 100, Width: 200} - err = term.SetWinsize(slave.Fd(), winsize) - Expect(err).NotTo(HaveOccurred()) - - err = syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) - Expect(err).NotTo(HaveOccurred()) - - Eventually(fakeSecureSession.SendRequestCallCount).Should(Equal(1)) - return nil - } - }) - - AfterEach(func() { - master.Close() - slave.Close() - }) - - It("sends window change events when the window dimensions change", func() { - Expect(fakeSecureSession.SendRequestCallCount()).To(Equal(1)) - - requestType, wantReply, message := fakeSecureSession.SendRequestArgsForCall(0) - Expect(requestType).To(Equal("window-change")) - Expect(wantReply).To(BeFalse()) - - type resizeMessage struct { - Width uint32 - Height uint32 - PixelWidth uint32 - PixelHeight uint32 - } - var resizeMsg resizeMessage - - err := ssh.Unmarshal(message, &resizeMsg) - Expect(err).NotTo(HaveOccurred()) - - Expect(resizeMsg).To(Equal(resizeMessage{Height: 100, Width: 200})) - }) - }) - - Describe("keep alive messages", func() { - var times []time.Time - var timesCh chan []time.Time - var done chan struct{} - - BeforeEach(func() { - keepAliveDuration = 100 * time.Millisecond - - times = []time.Time{} - timesCh = make(chan []time.Time, 1) - done = make(chan struct{}, 1) - - fakeConnection.SendRequestStub = func(reqName string, wantReply bool, message []byte) (bool, []byte, error) { - Expect(reqName).To(Equal("keepalive@cloudfoundry.org")) - Expect(wantReply).To(BeTrue()) - Expect(message).To(BeNil()) - - times = append(times, time.Now()) - if len(times) == 3 { - timesCh <- times - close(done) - } - return true, nil, nil - } - - fakeSecureSession.WaitStub = func() error { - Eventually(done).Should(BeClosed()) - return nil - } - }) - - It("sends keep alive messages at the expected interval", func() { - times := <-timesCh - Expect(times[2]).To(BeTemporally("~", times[0].Add(200*time.Millisecond), 100*time.Millisecond)) - }) - }) - }) - - Describe("LocalPortForward", func() { - var ( - opts *options.SSHOptions - localForwardError error - - echoAddress string - echoListener *fake_net.FakeListener - echoHandler *fake_server.FakeConnectionHandler - echoServer *server.Server - - localAddress string - - realLocalListener net.Listener - fakeLocalListener *fake_net.FakeListener - ) - - BeforeEach(func() { - logger := lagertest.NewTestLogger("test") - - var err error - realLocalListener, err = net.Listen("tcp", "127.0.0.1:0") - Expect(err).NotTo(HaveOccurred()) - - localAddress = realLocalListener.Addr().String() - fakeListenerFactory.ListenReturns(realLocalListener, nil) - - echoHandler = &fake_server.FakeConnectionHandler{} - echoHandler.HandleConnectionStub = func(conn net.Conn) { - io.Copy(conn, conn) - conn.Close() - } - - realListener, err := net.Listen("tcp", "127.0.0.1:0") - Expect(err).NotTo(HaveOccurred()) - echoAddress = realListener.Addr().String() - - echoListener = &fake_net.FakeListener{} - echoListener.AcceptStub = realListener.Accept - echoListener.CloseStub = realListener.Close - echoListener.AddrStub = realListener.Addr - - fakeLocalListener = &fake_net.FakeListener{} - fakeLocalListener.AcceptReturns(nil, errors.New("Not Accepting Connections")) - - echoServer = server.NewServer(logger.Session("echo"), "", echoHandler) - echoServer.SetListener(echoListener) - go echoServer.Serve() - - opts = &options.SSHOptions{ - AppName: "app-1", - ForwardSpecs: []options.ForwardSpec{{ - ListenAddress: localAddress, - ConnectAddress: echoAddress, - }}, - } - - fakeAppFactory.GetReturns(app.App{ - State: "STARTED", - Diego: true, - }, nil) - fakeInfoFactory.GetReturns(info.Info{}, nil) - fakeCredFactory.AuthorizationCodeReturns("", nil) - - fakeSecureClient.DialStub = net.Dial - }) - - JustBeforeEach(func() { - connectErr := secureShell.Connect(opts) - Expect(connectErr).NotTo(HaveOccurred()) - - localForwardError = secureShell.LocalPortForward() - }) - - AfterEach(func() { - err := secureShell.Close() - Expect(err).NotTo(HaveOccurred()) - echoServer.Shutdown() - - realLocalListener.Close() - }) - - validateConnectivity := func(addr string) { - conn, err := net.Dial("tcp", addr) - Expect(err).NotTo(HaveOccurred()) - - msg := fmt.Sprintf("Hello from %s\n", addr) - n, err := conn.Write([]byte(msg)) - Expect(err).NotTo(HaveOccurred()) - Expect(n).To(Equal(len(msg))) - - response := make([]byte, len(msg)) - n, err = conn.Read(response) - Expect(err).NotTo(HaveOccurred()) - Expect(n).To(Equal(len(msg))) - - err = conn.Close() - Expect(err).NotTo(HaveOccurred()) - - Expect(response).To(Equal([]byte(msg))) - } - - It("dials the connect address when a local connection is made", func() { - Expect(localForwardError).NotTo(HaveOccurred()) - - conn, err := net.Dial("tcp", localAddress) - Expect(err).NotTo(HaveOccurred()) - - Eventually(echoListener.AcceptCallCount).Should(BeNumerically(">=", 1)) - Eventually(fakeSecureClient.DialCallCount).Should(Equal(1)) - - network, addr := fakeSecureClient.DialArgsForCall(0) - Expect(network).To(Equal("tcp")) - Expect(addr).To(Equal(echoAddress)) - - Expect(conn.Close()).NotTo(HaveOccurred()) - }) - - It("copies data between the local and remote connections", func() { - validateConnectivity(localAddress) - }) - - Context("when a local connection is already open", func() { - var ( - conn net.Conn - err error - ) - - JustBeforeEach(func() { - conn, err = net.Dial("tcp", localAddress) - Expect(err).NotTo(HaveOccurred()) - }) - - AfterEach(func() { - err = conn.Close() - Expect(err).NotTo(HaveOccurred()) - }) - - It("allows for new incoming connections as well", func() { - validateConnectivity(localAddress) - }) - }) - - Context("when there are multiple port forward specs", func() { - var realLocalListener2 net.Listener - var localAddress2 string - - BeforeEach(func() { - var err error - realLocalListener2, err = net.Listen("tcp", "127.0.0.1:0") - Expect(err).NotTo(HaveOccurred()) - - localAddress2 = realLocalListener2.Addr().String() - - fakeListenerFactory.ListenStub = func(network, addr string) (net.Listener, error) { - if addr == localAddress { - return realLocalListener, nil - } - - if addr == localAddress2 { - return realLocalListener2, nil - } - - return nil, errors.New("unexpected address") - } - - opts = &options.SSHOptions{ - AppName: "app-1", - ForwardSpecs: []options.ForwardSpec{{ - ListenAddress: localAddress, - ConnectAddress: echoAddress, - }, { - ListenAddress: localAddress2, - ConnectAddress: echoAddress, - }}, - } - }) - - AfterEach(func() { - realLocalListener2.Close() - }) - - It("listens to all the things", func() { - Eventually(fakeListenerFactory.ListenCallCount).Should(Equal(2)) - - network, addr := fakeListenerFactory.ListenArgsForCall(0) - Expect(network).To(Equal("tcp")) - Expect(addr).To(Equal(localAddress)) - - network, addr = fakeListenerFactory.ListenArgsForCall(1) - Expect(network).To(Equal("tcp")) - Expect(addr).To(Equal(localAddress2)) - }) - - It("forwards to the correct target", func() { - validateConnectivity(localAddress) - validateConnectivity(localAddress2) - }) - - Context("when the secure client is closed", func() { - BeforeEach(func() { - fakeListenerFactory.ListenReturns(fakeLocalListener, nil) - fakeLocalListener.AcceptReturns(nil, errors.New("not accepting connections")) - }) - - It("closes the listeners ", func() { - Eventually(fakeListenerFactory.ListenCallCount).Should(Equal(2)) - Eventually(fakeLocalListener.AcceptCallCount).Should(Equal(2)) - - originalCloseCount := fakeLocalListener.CloseCallCount() - err := secureShell.Close() - Expect(err).NotTo(HaveOccurred()) - Expect(fakeLocalListener.CloseCallCount()).Should(Equal(originalCloseCount + 2)) - }) - }) - }) - - Context("when listen fails", func() { - BeforeEach(func() { - fakeListenerFactory.ListenReturns(nil, errors.New("failure is an option")) - }) - - It("returns the error", func() { - Expect(localForwardError).To(MatchError("failure is an option")) - }) - }) - - Context("when the client it closed", func() { - BeforeEach(func() { - fakeListenerFactory.ListenReturns(fakeLocalListener, nil) - fakeLocalListener.AcceptReturns(nil, errors.New("not accepting and connections")) - }) - - It("closes the listener when the client is closed", func() { - Eventually(fakeListenerFactory.ListenCallCount).Should(Equal(1)) - Eventually(fakeLocalListener.AcceptCallCount).Should(Equal(1)) - - originalCloseCount := fakeLocalListener.CloseCallCount() - err := secureShell.Close() - Expect(err).NotTo(HaveOccurred()) - Expect(fakeLocalListener.CloseCallCount()).Should(Equal(originalCloseCount + 1)) - }) - }) - - Context("when accept fails", func() { - var fakeConn *fake_net.FakeConn - BeforeEach(func() { - fakeConn = &fake_net.FakeConn{} - fakeConn.ReadReturns(0, io.EOF) - - fakeListenerFactory.ListenReturns(fakeLocalListener, nil) - }) - - Context("with a permanent error", func() { - BeforeEach(func() { - fakeLocalListener.AcceptReturns(nil, errors.New("boom")) - }) - - It("stops trying to accept connections", func() { - Eventually(fakeLocalListener.AcceptCallCount).Should(Equal(1)) - Consistently(fakeLocalListener.AcceptCallCount).Should(Equal(1)) - Expect(fakeLocalListener.CloseCallCount()).To(Equal(1)) - }) - }) - - Context("with a temporary error", func() { - var timeCh chan time.Time - - BeforeEach(func() { - timeCh = make(chan time.Time, 3) - - fakeLocalListener.AcceptStub = func() (net.Conn, error) { - timeCh := timeCh - if fakeLocalListener.AcceptCallCount() > 3 { - close(timeCh) - return nil, test_helpers.NewTestNetError(false, false) - } else { - timeCh <- time.Now() - return nil, test_helpers.NewTestNetError(false, true) - } - } - }) - - It("retries connecting after a short delay", func() { - Eventually(fakeLocalListener.AcceptCallCount).Should(Equal(3)) - Expect(timeCh).To(HaveLen(3)) - - times := make([]time.Time, 0) - for t := range timeCh { - times = append(times, t) - } - - Expect(times[1]).To(BeTemporally("~", times[0].Add(115*time.Millisecond), 30*time.Millisecond)) - Expect(times[2]).To(BeTemporally("~", times[1].Add(115*time.Millisecond), 30*time.Millisecond)) - }) - }) - }) - - Context("when dialing the connect address fails", func() { - var fakeTarget *fake_net.FakeConn - - BeforeEach(func() { - fakeTarget = &fake_net.FakeConn{} - fakeSecureClient.DialReturns(fakeTarget, errors.New("boom")) - }) - - It("does not call close on the target connection", func() { - Consistently(fakeTarget.CloseCallCount).Should(Equal(0)) - }) - }) - }) - - Describe("Wait", func() { - var opts *options.SSHOptions - var waitErr error - - BeforeEach(func() { - opts = &options.SSHOptions{ - AppName: "app-1", - } - - fakeAppFactory.GetReturns(app.App{ - State: "STARTED", - Diego: true, - }, nil) - fakeInfoFactory.GetReturns(info.Info{}, nil) - fakeCredFactory.AuthorizationCodeReturns("", nil) - }) - - JustBeforeEach(func() { - connectErr := secureShell.Connect(opts) - Expect(connectErr).NotTo(HaveOccurred()) - - waitErr = secureShell.Wait() - }) - - It("calls wait on the secureClient", func() { - Expect(waitErr).NotTo(HaveOccurred()) - Expect(fakeSecureClient.WaitCallCount()).To(Equal(1)) - }) - - Describe("keep alive messages", func() { - var times []time.Time - var timesCh chan []time.Time - var done chan struct{} - - BeforeEach(func() { - keepAliveDuration = 100 * time.Millisecond - - times = []time.Time{} - timesCh = make(chan []time.Time, 1) - done = make(chan struct{}, 1) - - fakeConnection.SendRequestStub = func(reqName string, wantReply bool, message []byte) (bool, []byte, error) { - Expect(reqName).To(Equal("keepalive@cloudfoundry.org")) - Expect(wantReply).To(BeTrue()) - Expect(message).To(BeNil()) - - times = append(times, time.Now()) - if len(times) == 3 { - timesCh <- times - close(done) - } - return true, nil, nil - } - - fakeSecureClient.WaitStub = func() error { - Eventually(done).Should(BeClosed()) - return nil - } - }) - - It("sends keep alive messages at the expected interval", func() { - Expect(waitErr).NotTo(HaveOccurred()) - times := <-timesCh - Expect(times[2]).To(BeTemporally("~", times[0].Add(200*time.Millisecond), 100*time.Millisecond)) - }) - }) - }) - - Describe("Close", func() { - var opts *options.SSHOptions - - BeforeEach(func() { - opts = &options.SSHOptions{ - AppName: "app-1", - } - - fakeAppFactory.GetReturns(app.App{ - State: "STARTED", - Diego: true, - }, nil) - fakeInfoFactory.GetReturns(info.Info{}, nil) - fakeCredFactory.AuthorizationCodeReturns("", nil) - }) - - JustBeforeEach(func() { - connectErr := secureShell.Connect(opts) - Expect(connectErr).NotTo(HaveOccurred()) - }) - - It("calls close on the secureClient", func() { - err := secureShell.Close() - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeSecureClient.CloseCallCount()).To(Equal(1)) - }) - }) -}) diff --git a/cf-plugin/main.go b/cf-plugin/main.go deleted file mode 100644 index 940c8eb..0000000 --- a/cf-plugin/main.go +++ /dev/null @@ -1,8 +0,0 @@ -package main - -import "github.com/cloudfoundry/cli/plugin" - -func main() { - sshPlugin := &SSHPlugin{} - plugin.Start(sshPlugin) -} diff --git a/cf-plugin/models/app/app.go b/cf-plugin/models/app/app.go deleted file mode 100644 index af3f054..0000000 --- a/cf-plugin/models/app/app.go +++ /dev/null @@ -1,73 +0,0 @@ -package app - -import ( - "errors" - "strconv" - "strings" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models" - "github.com/cloudfoundry/cli/plugin" -) - -//go:generate counterfeiter -o app_fakes/fake_app_factory.go . AppFactory -type AppFactory interface { - Get(string) (App, error) - SetBool(anApp App, key string, value bool) error -} - -type appFactory struct { - cli plugin.CliConnection - curl models.Curler -} - -func NewAppFactory(cli plugin.CliConnection, curl models.Curler) AppFactory { - return &appFactory{cli: cli, curl: curl} -} - -type App struct { - Guid string - EnableSSH bool - Diego bool - State string -} - -type metadata struct { - Guid string `json:"guid"` -} - -type entity struct { - EnableSSH bool `json:"enable_ssh"` - Diego bool `json:"diego"` - State string `json:"state"` -} - -type CFApp struct { - Metadata metadata `json:"metadata"` - Entity entity `json:"entity"` -} - -func (af *appFactory) Get(appName string) (App, error) { - output, err := af.cli.CliCommandWithoutTerminalOutput("app", appName, "--guid") - if err != nil { - return App{}, errors.New(output[len(output)-1]) - } - - guid := strings.TrimSpace(output[0]) - - app := CFApp{} - err = af.curl(af.cli, &app, "/v2/apps/"+guid) - if err != nil { - return App{}, errors.New("Failed to acquire " + appName + " info") - } - - return App{ - Guid: app.Metadata.Guid, - EnableSSH: app.Entity.EnableSSH, - Diego: app.Entity.Diego, - State: app.Entity.State, - }, nil -} - -func (af *appFactory) SetBool(anApp App, key string, value bool) error { - return af.curl(af.cli, nil, "/v2/apps/"+anApp.Guid, "-X", "PUT", "-d", `{"`+key+`":`+strconv.FormatBool(value)+`}`) -} diff --git a/cf-plugin/models/app/app_fakes/fake_app_factory.go b/cf-plugin/models/app/app_fakes/fake_app_factory.go deleted file mode 100644 index d03d24f..0000000 --- a/cf-plugin/models/app/app_fakes/fake_app_factory.go +++ /dev/null @@ -1,99 +0,0 @@ -// This file was generated by counterfeiter -package app_fakes - -import ( - "sync" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app" -) - -type FakeAppFactory struct { - GetStub func(string) (app.App, error) - getMutex sync.RWMutex - getArgsForCall []struct { - arg1 string - } - getReturns struct { - result1 app.App - result2 error - } - SetBoolStub func(anApp app.App, key string, value bool) error - setBoolMutex sync.RWMutex - setBoolArgsForCall []struct { - anApp app.App - key string - value bool - } - setBoolReturns struct { - result1 error - } -} - -func (fake *FakeAppFactory) Get(arg1 string) (app.App, error) { - fake.getMutex.Lock() - fake.getArgsForCall = append(fake.getArgsForCall, struct { - arg1 string - }{arg1}) - fake.getMutex.Unlock() - if fake.GetStub != nil { - return fake.GetStub(arg1) - } else { - return fake.getReturns.result1, fake.getReturns.result2 - } -} - -func (fake *FakeAppFactory) GetCallCount() int { - fake.getMutex.RLock() - defer fake.getMutex.RUnlock() - return len(fake.getArgsForCall) -} - -func (fake *FakeAppFactory) GetArgsForCall(i int) string { - fake.getMutex.RLock() - defer fake.getMutex.RUnlock() - return fake.getArgsForCall[i].arg1 -} - -func (fake *FakeAppFactory) GetReturns(result1 app.App, result2 error) { - fake.GetStub = nil - fake.getReturns = struct { - result1 app.App - result2 error - }{result1, result2} -} - -func (fake *FakeAppFactory) SetBool(anApp app.App, key string, value bool) error { - fake.setBoolMutex.Lock() - fake.setBoolArgsForCall = append(fake.setBoolArgsForCall, struct { - anApp app.App - key string - value bool - }{anApp, key, value}) - fake.setBoolMutex.Unlock() - if fake.SetBoolStub != nil { - return fake.SetBoolStub(anApp, key, value) - } else { - return fake.setBoolReturns.result1 - } -} - -func (fake *FakeAppFactory) SetBoolCallCount() int { - fake.setBoolMutex.RLock() - defer fake.setBoolMutex.RUnlock() - return len(fake.setBoolArgsForCall) -} - -func (fake *FakeAppFactory) SetBoolArgsForCall(i int) (app.App, string, bool) { - fake.setBoolMutex.RLock() - defer fake.setBoolMutex.RUnlock() - return fake.setBoolArgsForCall[i].anApp, fake.setBoolArgsForCall[i].key, fake.setBoolArgsForCall[i].value -} - -func (fake *FakeAppFactory) SetBoolReturns(result1 error) { - fake.SetBoolStub = nil - fake.setBoolReturns = struct { - result1 error - }{result1} -} - -var _ app.AppFactory = new(FakeAppFactory) diff --git a/cf-plugin/models/app/app_suite_test.go b/cf-plugin/models/app/app_suite_test.go deleted file mode 100644 index 9bee804..0000000 --- a/cf-plugin/models/app/app_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package app_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestApp(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "App Suite") -} diff --git a/cf-plugin/models/app/app_test.go b/cf-plugin/models/app/app_test.go deleted file mode 100644 index 25bbb13..0000000 --- a/cf-plugin/models/app/app_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package app_test - -import ( - "errors" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app" - "github.com/cloudfoundry/cli/plugin" - "github.com/cloudfoundry/cli/plugin/fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("App", func() { - var ( - fakeCliConnection *fakes.FakeCliConnection - curler models.Curler - af app.AppFactory - ) - - BeforeEach(func() { - fakeCliConnection = &fakes.FakeCliConnection{} - }) - - JustBeforeEach(func() { - af = app.NewAppFactory(fakeCliConnection, curler) - }) - - Describe("Get", func() { - Context("when CC returns a valid app guid", func() { - BeforeEach(func() { - fakeCliConnection.CliCommandWithoutTerminalOutputStub = func(args ...string) ([]string, error) { - Expect(args).To(ConsistOf("app", "app1", "--guid")) - return []string{"app1-guid\n"}, nil - } - }) - - Context("when an App is returned", func() { - BeforeEach(func() { - curler = func(cli plugin.CliConnection, result interface{}, args ...string) error { - a, ok := result.(*app.CFApp) - Expect(ok).To(BeTrue()) - a.Metadata.Guid = "app1-guid" - a.Entity.EnableSSH = true - a.Entity.Diego = true - a.Entity.State = "STARTED" - return nil - } - }) - - It("returns a populated App model", func() { - model, err := af.Get("app1") - - Expect(err).NotTo(HaveOccurred()) - Expect(model.Guid).To(Equal("app1-guid")) - Expect(model.EnableSSH).To(BeTrue()) - Expect(model.Diego).To(BeTrue()) - Expect(model.State).To(Equal("STARTED")) - }) - }) - - Context("when curling the App fails", func() { - BeforeEach(func() { - curler = func(cli plugin.CliConnection, result interface{}, args ...string) error { - return errors.New("not good") - } - }) - - It("returns an error", func() { - _, err := af.Get("app1") - Expect(err).To(MatchError("Failed to acquire app1 info")) - }) - }) - }) - - Context("when the app does not exist", func() { - BeforeEach(func() { - fakeCliConnection.CliCommandWithoutTerminalOutputReturns( - []string{"FAILED", "App app1 is not found"}, - errors.New("Error executing cli core command"), - ) - }) - - It("returns 'App not found' error", func() { - _, err := af.Get("app1") - Expect(err).To(MatchError("App app1 is not found")) - - Expect(fakeCliConnection.CliCommandWithoutTerminalOutputCallCount()).To(Equal(1)) - args := fakeCliConnection.CliCommandWithoutTerminalOutputArgsForCall(0) - Expect(args).To(ConsistOf("app", "app1", "--guid")) - }) - }) - }) -}) diff --git a/cf-plugin/models/credential/credential.go b/cf-plugin/models/credential/credential.go deleted file mode 100644 index 57cb93e..0000000 --- a/cf-plugin/models/credential/credential.go +++ /dev/null @@ -1,120 +0,0 @@ -package credential - -import ( - "crypto/tls" - "errors" - "net/http" - "net/url" - "time" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/info" - "github.com/cloudfoundry/cli/plugin" -) - -//go:generate counterfeiter -o credential_fakes/fake_credential_factory.go . CredentialFactory -type CredentialFactory interface { - AuthorizationCode() (string, error) -} - -type credFactory struct { - cli plugin.CliConnection - infoFactory info.InfoFactory -} - -var NoRedirectsErr = errors.New("No redirects") - -func NewCredentialFactory(cli plugin.CliConnection, infoFactory info.InfoFactory) CredentialFactory { - return &credFactory{ - cli: cli, - infoFactory: infoFactory, - } -} - -func (c *credFactory) AuthorizationCode() (string, error) { - authzToken, err := c.authorizationToken() - if err != nil { - return "", err - } - - skipCertificateVerify, err := c.cli.IsSSLDisabled() - if err != nil { - return "", err - } - - httpClient := &http.Client{ - CheckRedirect: func(req *http.Request, _ []*http.Request) error { - return NoRedirectsErr - }, - Timeout: 30 * time.Second, - Transport: &http.Transport{ - DisableKeepAlives: true, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: skipCertificateVerify, - }, - TLSHandshakeTimeout: 10 * time.Second, - }, - } - - authorizeURL, err := c.authorizeURL() - if err != nil { - return "", err - } - - authorizeReq, err := http.NewRequest("GET", authorizeURL, nil) - if err != nil { - return "", err - } - authorizeReq.Header.Add("authorization", authzToken) - - resp, err := httpClient.Do(authorizeReq) - if err == nil { - return "", errors.New("Authorization server did not redirect with one time code") - } - - if netErr, ok := err.(*url.Error); !ok || netErr.Err != NoRedirectsErr { - return "", err - } - - loc, err := resp.Location() - if err != nil { - return "", err - } - - codes := loc.Query()["code"] - if len(codes) != 1 { - return "", errors.New("Unable to acquire one time code from authorization response") - } - - return codes[0], nil -} - -func (c *credFactory) authorizeURL() (string, error) { - v2Info, err := c.infoFactory.Get() - if err != nil { - return "", err - } - - authorizeURL, err := url.Parse(v2Info.TokenEndpoint) - if err != nil { - return "", err - } - - values := url.Values{} - values.Set("response_type", "code") - values.Set("grant_type", "authorization_code") - values.Set("client_id", v2Info.SSHOAuthClient) - - authorizeURL.Path = "/oauth/authorize" - authorizeURL.RawQuery = values.Encode() - - return authorizeURL.String(), nil -} - -func (c *credFactory) authorizationToken() (string, error) { - _, err := c.cli.CliCommandWithoutTerminalOutput("oauth-token") - if err != nil { - return "", err - } - - return c.cli.AccessToken() -} diff --git a/cf-plugin/models/credential/credential_fakes/fake_credential_factory.go b/cf-plugin/models/credential/credential_fakes/fake_credential_factory.go deleted file mode 100644 index b40041c..0000000 --- a/cf-plugin/models/credential/credential_fakes/fake_credential_factory.go +++ /dev/null @@ -1,45 +0,0 @@ -// This file was generated by counterfeiter -package credential_fakes - -import ( - "sync" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/credential" -) - -type FakeCredentialFactory struct { - AuthorizationCodeStub func() (string, error) - authorizationCodeMutex sync.RWMutex - authorizationCodeArgsForCall []struct{} - authorizationCodeReturns struct { - result1 string - result2 error - } -} - -func (fake *FakeCredentialFactory) AuthorizationCode() (string, error) { - fake.authorizationCodeMutex.Lock() - fake.authorizationCodeArgsForCall = append(fake.authorizationCodeArgsForCall, struct{}{}) - fake.authorizationCodeMutex.Unlock() - if fake.AuthorizationCodeStub != nil { - return fake.AuthorizationCodeStub() - } else { - return fake.authorizationCodeReturns.result1, fake.authorizationCodeReturns.result2 - } -} - -func (fake *FakeCredentialFactory) AuthorizationCodeCallCount() int { - fake.authorizationCodeMutex.RLock() - defer fake.authorizationCodeMutex.RUnlock() - return len(fake.authorizationCodeArgsForCall) -} - -func (fake *FakeCredentialFactory) AuthorizationCodeReturns(result1 string, result2 error) { - fake.AuthorizationCodeStub = nil - fake.authorizationCodeReturns = struct { - result1 string - result2 error - }{result1, result2} -} - -var _ credential.CredentialFactory = new(FakeCredentialFactory) diff --git a/cf-plugin/models/credential/credential_suite_test.go b/cf-plugin/models/credential/credential_suite_test.go deleted file mode 100644 index 67b9856..0000000 --- a/cf-plugin/models/credential/credential_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package credential_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestCredential(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Credential Suite") -} diff --git a/cf-plugin/models/credential/credential_test.go b/cf-plugin/models/credential/credential_test.go deleted file mode 100644 index 8b6e20d..0000000 --- a/cf-plugin/models/credential/credential_test.go +++ /dev/null @@ -1,171 +0,0 @@ -package credential_test - -import ( - "errors" - "net/http" - "net/url" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/credential" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/info" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/info/info_fakes" - "github.com/cloudfoundry/cli/plugin/fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/onsi/gomega/ghttp" -) - -var _ = Describe("Credential", func() { - var ( - fakeCliConnection *fakes.FakeCliConnection - fakeInfoFactory *info_fakes.FakeInfoFactory - credFactory credential.CredentialFactory - ) - - BeforeEach(func() { - fakeCliConnection = &fakes.FakeCliConnection{} - fakeCliConnection.IsSSLDisabledReturns(true, nil) - fakeInfoFactory = &info_fakes.FakeInfoFactory{} - }) - - JustBeforeEach(func() { - credFactory = credential.NewCredentialFactory(fakeCliConnection, fakeInfoFactory) - }) - - Describe("AuthorizationCode", func() { - var v2Info info.Info - var fakeUAA *ghttp.Server - - BeforeEach(func() { - fakeCliConnection.AccessTokenReturns("bearer client-bearer-token", nil) - - fakeUAA = ghttp.NewTLSServer() - v2Info = info.Info{ - SSHOAuthClient: "ssh-oauth-client-id", - TokenEndpoint: fakeUAA.URL(), - } - fakeInfoFactory.GetReturns(v2Info, nil) - - fakeUAA.RouteToHandler("GET", "/oauth/authorize", ghttp.CombineHandlers( - ghttp.VerifyRequest("GET", "/oauth/authorize"), - ghttp.VerifyFormKV("response_type", "code"), - ghttp.VerifyFormKV("client_id", "ssh-oauth-client-id"), - ghttp.VerifyFormKV("grant_type", "authorization_code"), - ghttp.VerifyHeaderKV("authorization", "bearer client-bearer-token"), - ghttp.RespondWith(http.StatusFound, "", http.Header{ - "Location": []string{"https://uaa.example.com/login?code=abc123"}, - }), - )) - }) - - It("forces the cli to refresh the access token used to acquire the access code", func() { - _, err := credFactory.AuthorizationCode() - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeCliConnection.CliCommandWithoutTerminalOutputCallCount()).To(Equal(1)) - Expect(fakeCliConnection.CliCommandWithoutTerminalOutputArgsForCall(0)).To(ConsistOf("oauth-token")) - Expect(fakeCliConnection.AccessTokenCallCount()).To(Equal(1)) - }) - - It("gets the access code from the token endpoint", func() { - code, err := credFactory.AuthorizationCode() - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeUAA.ReceivedRequests()).To(HaveLen(1)) - Expect(code).To(Equal("abc123")) - }) - - It("returns an error when the uaa certificate is not valid and certificate validation is enabled", func() { - fakeCliConnection.IsSSLDisabledReturns(false, nil) - - _, err := credFactory.AuthorizationCode() - Expect(err).To(HaveOccurred()) - - urlErr, ok := err.(*url.Error) - Expect(ok).To(BeTrue()) - Expect(urlErr.Err).To(MatchError(ContainSubstring("signed by unknown authority"))) - - Expect(fakeUAA.ReceivedRequests()).To(HaveLen(0)) - }) - - It("returns the error from the cli when refreshing the access token fails", func() { - fakeCliConnection.CliCommandWithoutTerminalOutputReturns([]string{}, errors.New("woops")) - - _, err := credFactory.AuthorizationCode() - Expect(err).To(MatchError("woops")) - - Expect(fakeCliConnection.CliCommandWithoutTerminalOutputCallCount()).To(Equal(1)) - Expect(fakeCliConnection.AccessTokenCallCount()).To(Equal(0)) - }) - - It("returns the error from the cli when getting the access token fails", func() { - fakeCliConnection.AccessTokenReturns("", errors.New("woops")) - - _, err := credFactory.AuthorizationCode() - Expect(err).To(MatchError("woops")) - - Expect(fakeCliConnection.AccessTokenCallCount()).To(Equal(1)) - }) - - It("returns the error from the info factory when getting /v2/info fails", func() { - var expectedErr = errors.New("boom") - fakeInfoFactory.GetReturns(info.Info{}, expectedErr) - - _, err := credFactory.AuthorizationCode() - Expect(err).To(Equal(expectedErr)) - }) - - It("returns the error from the cli plugin when getting the access token fails", func() { - var expectedErr = errors.New("boom") - fakeCliConnection.AccessTokenReturns("", expectedErr) - - _, err := credFactory.AuthorizationCode() - Expect(err).To(Equal(expectedErr)) - }) - - It("returns an error when the endpoint url cannot be parsed", func() { - fakeInfoFactory.GetReturns(info.Info{ - SSHOAuthClient: "ssh-oauth-client-id", - TokenEndpoint: ":goober#swallow?yak", - }, nil) - - _, err := credFactory.AuthorizationCode() - Expect(err).To(HaveOccurred()) - Expect(fakeUAA.ReceivedRequests()).To(HaveLen(0)) - }) - - It("returns an error when the request to the authorization server fails", func() { - fakeInfoFactory.GetReturns(info.Info{ - SSHOAuthClient: "ssh-oauth-client-id", - TokenEndpoint: "http://0.0.0.0", // invalid address - }, nil) - - _, err := credFactory.AuthorizationCode() - Expect(err).To(HaveOccurred()) - }) - - It("returns an error when the authorization server does not redirect", func() { - fakeUAA.RouteToHandler("GET", "/oauth/authorize", ghttp.CombineHandlers( - ghttp.VerifyRequest("GET", "/oauth/authorize"), - ghttp.RespondWith(http.StatusOK, ""), - )) - - _, err := credFactory.AuthorizationCode() - Expect(err).To(MatchError("Authorization server did not redirect with one time code")) - Expect(fakeUAA.ReceivedRequests()).To(HaveLen(1)) - }) - - It("returns an error when the redirect URL does not contain a code", func() { - fakeUAA.RouteToHandler("GET", "/oauth/authorize", ghttp.CombineHandlers( - ghttp.VerifyRequest("GET", "/oauth/authorize"), - ghttp.RespondWith(http.StatusFound, "", http.Header{ - "Location": []string{"https://uaa.example.com/login"}, - }), - )) - - _, err := credFactory.AuthorizationCode() - Expect(err).To(MatchError("Unable to acquire one time code from authorization response")) - Expect(fakeUAA.ReceivedRequests()).To(HaveLen(1)) - }) - }) -}) diff --git a/cf-plugin/models/curl.go b/cf-plugin/models/curl.go deleted file mode 100644 index f84fd26..0000000 --- a/cf-plugin/models/curl.go +++ /dev/null @@ -1,45 +0,0 @@ -package models - -import ( - "encoding/json" - "errors" - "strings" - - "github.com/cloudfoundry/cli/plugin" -) - -type curlError struct { - Code int `json:"code"` - Description string `json:"description"` - ErrorCode string `json:"error_code"` -} - -type Curler func(cli plugin.CliConnection, result interface{}, args ...string) error - -func Curl(cli plugin.CliConnection, result interface{}, args ...string) error { - output, err := cli.CliCommandWithoutTerminalOutput(append([]string{"curl"}, args...)...) - if err != nil { - return err - } - - buf := []byte(strings.Join(output, "\n")) - - var errorResponse curlError - err = json.Unmarshal(buf, &errorResponse) - if err != nil { - return err - } - - if errorResponse.Code != 0 { - return errors.New(errorResponse.Description) - } - - if result != nil { - err = json.Unmarshal(buf, result) - if err != nil { - return err - } - } - - return nil -} diff --git a/cf-plugin/models/curl_test.go b/cf-plugin/models/curl_test.go deleted file mode 100644 index 0b8ab46..0000000 --- a/cf-plugin/models/curl_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package models_test - -import ( - "encoding/json" - "errors" - - . "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models" - "github.com/cloudfoundry/cli/plugin/fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Curl", func() { - var fakeCliConnection *fakes.FakeCliConnection - - BeforeEach(func() { - fakeCliConnection = &fakes.FakeCliConnection{} - }) - - Context("with a valid response", func() { - type MyStruct struct { - SomeString string - } - - BeforeEach(func() { - input := []string{"{", `"somestring": "foo"`, "}"} - fakeCliConnection.CliCommandWithoutTerminalOutputReturns(input, nil) - }) - - It("unmarshals a successful response", func() { - var result MyStruct - err := Curl(fakeCliConnection, &result, "a", "b", "c") - Expect(err).NotTo(HaveOccurred()) - Expect(result.SomeString).To(Equal("foo")) - - Expect(fakeCliConnection.CliCommandWithoutTerminalOutputCallCount()).To(Equal(1)) - Expect(fakeCliConnection.CliCommandWithoutTerminalOutputArgsForCall(0)).To(Equal([]string{"curl", "a", "b", "c"})) - }) - - It("succeeds with no response object", func() { - err := Curl(fakeCliConnection, nil, "a", "b", "c") - Expect(err).NotTo(HaveOccurred()) - }) - }) - - Context("when the cli errors", func() { - BeforeEach(func() { - fakeCliConnection.CliCommandWithoutTerminalOutputReturns(nil, errors.New("an error")) - }) - - It("returns an error", func() { - err := Curl(fakeCliConnection, nil, "a", "b", "c") - Expect(err).To(MatchError("an error")) - }) - }) - - Context("when the response cannot be unmarshalled", func() { - BeforeEach(func() { - fakeCliConnection.CliCommandWithoutTerminalOutputReturns([]string{"abcd"}, nil) - }) - - It("returns an error", func() { - err := Curl(fakeCliConnection, nil, "a", "b", "c") - Expect(err).To(BeAssignableToTypeOf(&json.SyntaxError{})) - }) - }) - - Context("when the response is a CF Error", func() { - BeforeEach(func() { - fakeCliConnection.CliCommandWithoutTerminalOutputReturns([]string{"{", `"code": 1234,`, `"description":"another error"`, "}"}, nil) - }) - - It("returns an error", func() { - err := Curl(fakeCliConnection, nil, "a", "b", "c") - Expect(err).To(MatchError("another error")) - }) - }) -}) diff --git a/cf-plugin/models/info/info.go b/cf-plugin/models/info/info.go deleted file mode 100644 index 4be6d69..0000000 --- a/cf-plugin/models/info/info.go +++ /dev/null @@ -1,45 +0,0 @@ -package info - -import ( - "encoding/json" - "errors" - - "github.com/cloudfoundry/cli/plugin" -) - -type InfoFactory interface { - Get() (Info, error) -} - -type infoFactory struct { - cli plugin.CliConnection -} - -func NewInfoFactory(cli plugin.CliConnection) InfoFactory { - return &infoFactory{cli: cli} -} - -type Info struct { - SSHEndpoint string `json:"app_ssh_endpoint"` - SSHEndpointFingerprint string `json:"app_ssh_host_key_fingerprint"` - SSHOAuthClient string `json:"app_ssh_oauth_client"` - TokenEndpoint string `json:"token_endpoint"` -} - -func (ifactory *infoFactory) Get() (Info, error) { - var info Info - - output, err := ifactory.cli.CliCommandWithoutTerminalOutput("curl", "/v2/info") - if err != nil { - return info, errors.New("Failed to acquire SSH endpoint info") - } - - response := []byte(output[0]) - - err = json.Unmarshal(response, &info) - if err != nil { - return info, errors.New("Failed to acquire SSH endpoint info") - } - - return info, nil -} diff --git a/cf-plugin/models/info/info_fakes/fake_app_factory.go b/cf-plugin/models/info/info_fakes/fake_app_factory.go deleted file mode 100644 index ef54852..0000000 --- a/cf-plugin/models/info/info_fakes/fake_app_factory.go +++ /dev/null @@ -1,45 +0,0 @@ -// This file was generated by counterfeiter -package info_fakes - -import ( - "sync" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/info" -) - -type FakeInfoFactory struct { - GetStub func() (info.Info, error) - getMutex sync.RWMutex - getArgsForCall []struct{} - getReturns struct { - result1 info.Info - result2 error - } -} - -func (fake *FakeInfoFactory) Get() (info.Info, error) { - fake.getMutex.Lock() - fake.getArgsForCall = append(fake.getArgsForCall, struct{}{}) - fake.getMutex.Unlock() - if fake.GetStub != nil { - return fake.GetStub() - } else { - return fake.getReturns.result1, fake.getReturns.result2 - } -} - -func (fake *FakeInfoFactory) GetCallCount() int { - fake.getMutex.RLock() - defer fake.getMutex.RUnlock() - return len(fake.getArgsForCall) -} - -func (fake *FakeInfoFactory) GetReturns(result1 info.Info, result2 error) { - fake.GetStub = nil - fake.getReturns = struct { - result1 info.Info - result2 error - }{result1, result2} -} - -var _ info.InfoFactory = new(FakeInfoFactory) diff --git a/cf-plugin/models/info/info_suite_test.go b/cf-plugin/models/info/info_suite_test.go deleted file mode 100644 index 289f47c..0000000 --- a/cf-plugin/models/info/info_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package info_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestInfo(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Info Suite") -} diff --git a/cf-plugin/models/info/info_test.go b/cf-plugin/models/info/info_test.go deleted file mode 100644 index 87ca420..0000000 --- a/cf-plugin/models/info/info_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package info_test - -import ( - "errors" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/info" - "github.com/cloudfoundry/cli/plugin/fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Info", func() { - var ( - fakeCliConnection *fakes.FakeCliConnection - infoFactory info.InfoFactory - ) - - BeforeEach(func() { - fakeCliConnection = &fakes.FakeCliConnection{} - infoFactory = info.NewInfoFactory(fakeCliConnection) - }) - - Describe("Get", func() { - var expectedJson string - - JustBeforeEach(func() { - fakeCliConnection.CliCommandWithoutTerminalOutputReturns([]string{expectedJson}, nil) - }) - - Context("when retrieving /v2/info is successful", func() { - BeforeEach(func() { - expectedJson = `{ - "app_ssh_endpoint": "ssh.example.com:1234", - "app_ssh_host_key_fingerprint": "00:11:22:33:44:55:66:77:88", - "app_ssh_oauth_client": "ssh-proxy", - "token_endpoint": "https://uaa.example.com" - }` - - fakeCliConnection.CliCommandWithoutTerminalOutputReturns([]string{expectedJson}, nil) - }) - - It("returns a populated Info model", func() { - model, err := infoFactory.Get() - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeCliConnection.CliCommandWithoutTerminalOutputCallCount()).To(Equal(1)) - Expect(fakeCliConnection.CliCommandWithoutTerminalOutputArgsForCall(0)).To(ConsistOf("curl", "/v2/info")) - - Expect(model.SSHEndpoint).To(Equal("ssh.example.com:1234")) - Expect(model.SSHEndpointFingerprint).To(Equal("00:11:22:33:44:55:66:77:88")) - Expect(model.SSHOAuthClient).To(Equal("ssh-proxy")) - Expect(model.TokenEndpoint).To(Equal("https://uaa.example.com")) - }) - }) - - Context("when retrieving /v2/info fails", func() { - JustBeforeEach(func() { - fakeCliConnection.CliCommandWithoutTerminalOutputReturns(nil, errors.New("woops")) - }) - - It("fails with an error", func() { - _, err := infoFactory.Get() - Expect(err).To(MatchError("Failed to acquire SSH endpoint info")) - }) - }) - - Context("when the json response fails to unmarshal", func() { - BeforeEach(func() { - expectedJson = `soo, this is bad #json` - }) - - It("fails with an error", func() { - _, err := infoFactory.Get() - Expect(err).To(MatchError("Failed to acquire SSH endpoint info")) - }) - }) - }) -}) diff --git a/cf-plugin/models/models_suite_test.go b/cf-plugin/models/models_suite_test.go deleted file mode 100644 index 2d4c6de..0000000 --- a/cf-plugin/models/models_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package models_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestModels(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Models Suite") -} diff --git a/cf-plugin/models/space/space.go b/cf-plugin/models/space/space.go deleted file mode 100644 index ee5c9d7..0000000 --- a/cf-plugin/models/space/space.go +++ /dev/null @@ -1,67 +0,0 @@ -package space - -import ( - "errors" - "strconv" - "strings" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models" - "github.com/cloudfoundry/cli/plugin" -) - -//go:generate counterfeiter -o space_fakes/fake_space_factory.go . SpaceFactory -type SpaceFactory interface { - Get(string) (Space, error) - SetBool(aSpace Space, key string, value bool) error -} - -type spaceFactory struct { - cli plugin.CliConnection - curl models.Curler -} - -func NewSpaceFactory(cli plugin.CliConnection, curl models.Curler) SpaceFactory { - return &spaceFactory{cli: cli, curl: curl} -} - -type Space struct { - Guid string - AllowSSH bool -} - -type metadata struct { - Guid string `json:"guid"` -} - -type entity struct { - AllowSSH bool `json:"allow_ssh"` -} - -type CFSpace struct { - Metadata metadata `json:"metadata"` - Entity entity `json:"entity"` -} - -func (sf *spaceFactory) Get(spaceName string) (Space, error) { - output, err := sf.cli.CliCommandWithoutTerminalOutput("space", spaceName, "--guid") - if err != nil { - return Space{}, errors.New(output[len(output)-1]) - } - - guid := strings.TrimSpace(output[0]) - space := CFSpace{} - err = sf.curl(sf.cli, &space, "/v2/spaces/"+guid) - - if err != nil { - return Space{}, errors.New("Failed to acquire " + spaceName + " info") - } - - return Space{ - Guid: space.Metadata.Guid, - AllowSSH: space.Entity.AllowSSH, - }, nil -} - -func (sf *spaceFactory) SetBool(aSpace Space, key string, value bool) error { - return sf.curl(sf.cli, nil, "/v2/spaces/"+aSpace.Guid, "-X", "PUT", "-d", `{"`+key+`":`+strconv.FormatBool(value)+`}`) -} diff --git a/cf-plugin/models/space/space_fakes/fake_space_factory.go b/cf-plugin/models/space/space_fakes/fake_space_factory.go deleted file mode 100644 index 42f8f81..0000000 --- a/cf-plugin/models/space/space_fakes/fake_space_factory.go +++ /dev/null @@ -1,99 +0,0 @@ -// This file was generated by counterfeiter -package space_fakes - -import ( - "sync" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space" -) - -type FakeSpaceFactory struct { - GetStub func(string) (space.Space, error) - getMutex sync.RWMutex - getArgsForCall []struct { - arg1 string - } - getReturns struct { - result1 space.Space - result2 error - } - SetBoolStub func(aSpace space.Space, key string, value bool) error - setBoolMutex sync.RWMutex - setBoolArgsForCall []struct { - aSpace space.Space - key string - value bool - } - setBoolReturns struct { - result1 error - } -} - -func (fake *FakeSpaceFactory) Get(arg1 string) (space.Space, error) { - fake.getMutex.Lock() - fake.getArgsForCall = append(fake.getArgsForCall, struct { - arg1 string - }{arg1}) - fake.getMutex.Unlock() - if fake.GetStub != nil { - return fake.GetStub(arg1) - } else { - return fake.getReturns.result1, fake.getReturns.result2 - } -} - -func (fake *FakeSpaceFactory) GetCallCount() int { - fake.getMutex.RLock() - defer fake.getMutex.RUnlock() - return len(fake.getArgsForCall) -} - -func (fake *FakeSpaceFactory) GetArgsForCall(i int) string { - fake.getMutex.RLock() - defer fake.getMutex.RUnlock() - return fake.getArgsForCall[i].arg1 -} - -func (fake *FakeSpaceFactory) GetReturns(result1 space.Space, result2 error) { - fake.GetStub = nil - fake.getReturns = struct { - result1 space.Space - result2 error - }{result1, result2} -} - -func (fake *FakeSpaceFactory) SetBool(aSpace space.Space, key string, value bool) error { - fake.setBoolMutex.Lock() - fake.setBoolArgsForCall = append(fake.setBoolArgsForCall, struct { - aSpace space.Space - key string - value bool - }{aSpace, key, value}) - fake.setBoolMutex.Unlock() - if fake.SetBoolStub != nil { - return fake.SetBoolStub(aSpace, key, value) - } else { - return fake.setBoolReturns.result1 - } -} - -func (fake *FakeSpaceFactory) SetBoolCallCount() int { - fake.setBoolMutex.RLock() - defer fake.setBoolMutex.RUnlock() - return len(fake.setBoolArgsForCall) -} - -func (fake *FakeSpaceFactory) SetBoolArgsForCall(i int) (space.Space, string, bool) { - fake.setBoolMutex.RLock() - defer fake.setBoolMutex.RUnlock() - return fake.setBoolArgsForCall[i].aSpace, fake.setBoolArgsForCall[i].key, fake.setBoolArgsForCall[i].value -} - -func (fake *FakeSpaceFactory) SetBoolReturns(result1 error) { - fake.SetBoolStub = nil - fake.setBoolReturns = struct { - result1 error - }{result1} -} - -var _ space.SpaceFactory = new(FakeSpaceFactory) diff --git a/cf-plugin/models/space/space_suite_test.go b/cf-plugin/models/space/space_suite_test.go deleted file mode 100644 index 16cb824..0000000 --- a/cf-plugin/models/space/space_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package space_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestSpace(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Space Suite") -} diff --git a/cf-plugin/models/space/space_test.go b/cf-plugin/models/space/space_test.go deleted file mode 100644 index 20b6709..0000000 --- a/cf-plugin/models/space/space_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package space_test - -import ( - "errors" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space" - "github.com/cloudfoundry/cli/plugin" - "github.com/cloudfoundry/cli/plugin/fakes" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Space", func() { - var ( - fakeCliConnection *fakes.FakeCliConnection - curler models.Curler - sf space.SpaceFactory - ) - - BeforeEach(func() { - fakeCliConnection = &fakes.FakeCliConnection{} - - }) - - JustBeforeEach(func() { - sf = space.NewSpaceFactory(fakeCliConnection, curler) - }) - - Describe("Get", func() { - Context("when CC returns a valid space guid", func() { - BeforeEach(func() { - fakeCliConnection.CliCommandWithoutTerminalOutputStub = func(args ...string) ([]string, error) { - Expect(args).To(ConsistOf("space", "space1", "--guid")) - return []string{"space1-guid\n"}, nil - } - }) - - Context("when a Space is returned", func() { - BeforeEach(func() { - curler = func(cli plugin.CliConnection, result interface{}, args ...string) error { - s, ok := result.(*space.CFSpace) - Expect(ok).To(BeTrue()) - s.Metadata.Guid = "space1-guid" - s.Entity.AllowSSH = true - return nil - } - }) - - It("returns a populated Space model", func() { - model, err := sf.Get("space1") - - Expect(err).NotTo(HaveOccurred()) - Expect(model.Guid).To(Equal("space1-guid")) - Expect(model.AllowSSH).To(BeTrue()) - }) - }) - - Context("when curling the Space fails", func() { - BeforeEach(func() { - curler = func(cli plugin.CliConnection, result interface{}, args ...string) error { - return errors.New("not good") - } - }) - - It("returns an error", func() { - _, err := sf.Get("space1") - Expect(err).To(MatchError("Failed to acquire space1 info")) - }) - }) - }) - - Context("when the space does not exist", func() { - BeforeEach(func() { - fakeCliConnection.CliCommandWithoutTerminalOutputReturns( - []string{"FAILED", "Space space1 is not found"}, - errors.New("Error executing cli core command"), - ) - }) - - It("returns 'Space not found' error", func() { - _, err := sf.Get("space1") - Expect(err).To(MatchError("Space space1 is not found")) - - Expect(fakeCliConnection.CliCommandWithoutTerminalOutputCallCount()).To(Equal(1)) - args := fakeCliConnection.CliCommandWithoutTerminalOutputArgsForCall(0) - Expect(args).To(ConsistOf("space", "space1", "--guid")) - }) - }) - }) -}) diff --git a/cf-plugin/options/multiopt/multiopt.go b/cf-plugin/options/multiopt/multiopt.go deleted file mode 100644 index e571239..0000000 --- a/cf-plugin/options/multiopt/multiopt.go +++ /dev/null @@ -1,24 +0,0 @@ -package multiopt - -import ( - "strings" - - "github.com/pborman/getopt" -) - -type MultiValue struct { - values []string -} - -func (mv *MultiValue) Set(value string, option getopt.Option) error { - mv.values = append(mv.values, value) - return nil -} - -func (mv *MultiValue) String() string { - return strings.Join(mv.values, ",") -} - -func (mv *MultiValue) Values() []string { - return mv.values -} diff --git a/cf-plugin/options/multiopt/multiopt_suite_test.go b/cf-plugin/options/multiopt/multiopt_suite_test.go deleted file mode 100644 index 1636f12..0000000 --- a/cf-plugin/options/multiopt/multiopt_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package multiopt_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestMultiopt(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Multiopt Suite") -} diff --git a/cf-plugin/options/multiopt/multiopt_test.go b/cf-plugin/options/multiopt/multiopt_test.go deleted file mode 100644 index 5f87994..0000000 --- a/cf-plugin/options/multiopt/multiopt_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package multiopt_test - -import ( - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/options/multiopt" - "github.com/pborman/getopt" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Multiopt", func() { - var ( - opts *getopt.Set - mv *multiopt.MultiValue - args []string - ) - - BeforeEach(func() { - opts = getopt.New() - mv = &multiopt.MultiValue{} - - opts.Var(mv, 'L', "help string") - - args = []string{"ssh", "-L8080:example.com:80", "-L9443:example.com:443"} - }) - - JustBeforeEach(func() { - err := opts.Getopt(args, nil) - Expect(err).NotTo(HaveOccurred()) - }) - - Describe("Values", func() { - It("aggregates values for an option", func() { - Expect(mv.Values()).To(ConsistOf("8080:example.com:80", "9443:example.com:443")) - }) - }) - - Describe("String", func() { - It("returns all arguments separated by a comma", func() { - Expect(mv.String()).To(Equal("8080:example.com:80,9443:example.com:443")) - }) - }) -}) diff --git a/cf-plugin/options/options_suite_test.go b/cf-plugin/options/options_suite_test.go deleted file mode 100644 index a928cbe..0000000 --- a/cf-plugin/options/options_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package options_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestOptions(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Options Suite") -} diff --git a/cf-plugin/options/ssh_options.go b/cf-plugin/options/ssh_options.go deleted file mode 100644 index b291c62..0000000 --- a/cf-plugin/options/ssh_options.go +++ /dev/null @@ -1,208 +0,0 @@ -package options - -import ( - "bytes" - "errors" - "fmt" - "strings" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/options/multiopt" - "github.com/pborman/getopt" -) - -type TTYRequest int - -const ( - REQUEST_TTY_AUTO TTYRequest = iota - REQUEST_TTY_NO - REQUEST_TTY_YES - REQUEST_TTY_FORCE -) - -type ForwardSpec struct { - ListenAddress string - ConnectAddress string -} - -type SSHOptions struct { - AppName string - Command []string - Index uint - SkipHostValidation bool - SkipRemoteExecution bool - TerminalRequest TTYRequest - ForwardSpecs []ForwardSpec - - getoptSet *getopt.Set - indexOption getopt.Option - skipHostValidationOption getopt.Option - skipRemoteExecutionOption getopt.Option - disableTerminalAllocationOption getopt.Option - forceTerminalAllocationOption getopt.Option - localForwardingOption getopt.Option - - localForwardMultiVal *multiopt.MultiValue -} - -var UsageError = errors.New("Invalid usage") - -func NewSSHOptions() *SSHOptions { - sshOptions := &SSHOptions{} - - opts := getopt.New() - - sshOptions.indexOption = opts.UintVarLong( - &sshOptions.Index, - "index", - 'i', - "application instance index", - "app-instance-index", - ) - - sshOptions.skipHostValidationOption = opts.BoolVarLong( - &sshOptions.SkipHostValidation, - "skip-host-validation", - 'k', - "skip host key validation", - ).SetFlag() - - sshOptions.skipRemoteExecutionOption = opts.BoolVar( - &sshOptions.SkipRemoteExecution, - 'N', - "do not execute a remote command", - ).SetFlag() - - var force, disable bool - sshOptions.forceTerminalAllocationOption = opts.BoolVar(&force, 't', "force pseudo-tty allocation").SetFlag() - sshOptions.disableTerminalAllocationOption = opts.BoolVar(&disable, 'T', "disable pseudo-tty allocation").SetFlag() - - sshOptions.localForwardMultiVal = &multiopt.MultiValue{} - sshOptions.localForwardingOption = opts.Var( - sshOptions.localForwardMultiVal, - 'L', - "local port forward specification", - "[bind_address:]port:host:hostport", - ) - - sshOptions.getoptSet = opts - - return sshOptions -} - -func (o *SSHOptions) Parse(args []string) error { - opts := o.getoptSet - err := opts.Getopt(args, nil) - if err != nil { - return err - } - - if len(args) == 0 || args[0] != "ssh" { - return UsageError - } - - if opts.NArgs() == 0 { - return UsageError - } - - o.AppName = opts.Arg(0) - - if opts.NArgs() > 0 { - err = opts.Getopt(opts.Args(), nil) - if err != nil { - return err - } - - o.Command = opts.Args() - } - - if o.localForwardingOption.Seen() { - for _, arg := range o.localForwardMultiVal.Values() { - forwardSpec, err := o.parseLocalForwardingSpec(arg) - if err != nil { - return err - } - o.ForwardSpecs = append(o.ForwardSpecs, *forwardSpec) - } - } - - if o.forceTerminalAllocationOption.Count() == 1 { - o.TerminalRequest = REQUEST_TTY_YES - } else if o.forceTerminalAllocationOption.Count() > 1 { - o.TerminalRequest = REQUEST_TTY_FORCE - } - - if o.disableTerminalAllocationOption.Count() != 0 { - o.TerminalRequest = REQUEST_TTY_NO - } - - return nil -} - -func (o *SSHOptions) parseLocalForwardingSpec(arg string) (*ForwardSpec, error) { - arg = strings.TrimSpace(arg) - - parts := []string{} - for remainder := arg; remainder != ""; { - part, r, err := tokenizeForward(remainder) - if err != nil { - return nil, err - } - - parts = append(parts, part) - remainder = r - } - - forwardSpec := &ForwardSpec{} - switch len(parts) { - case 4: - if parts[0] == "*" { - parts[0] = "" - } - forwardSpec.ListenAddress = fmt.Sprintf("%s:%s", parts[0], parts[1]) - forwardSpec.ConnectAddress = fmt.Sprintf("%s:%s", parts[2], parts[3]) - case 3: - forwardSpec.ListenAddress = fmt.Sprintf("localhost:%s", parts[0]) - forwardSpec.ConnectAddress = fmt.Sprintf("%s:%s", parts[1], parts[2]) - default: - return nil, fmt.Errorf("Unable to parse local forwarding argument: %q", arg) - } - - return forwardSpec, nil -} - -func tokenizeForward(arg string) (string, string, error) { - switch arg[0] { - case ':': - return "", arg[1:], nil - - case '[': - parts := strings.SplitAfterN(arg, "]", 2) - if len(parts) != 2 { - return "", "", fmt.Errorf("Argument missing closing bracket: %q", arg) - } - - if parts[1][0] == ':' { - return parts[0], parts[1][1:], nil - } - - return "", "", fmt.Errorf("Unexpected token: %q", parts[1]) - - default: - parts := strings.SplitN(arg, ":", 2) - if len(parts) < 2 { - return parts[0], "", nil - } - return parts[0], parts[1], nil - } -} - -func SSHUsage() string { - b := &bytes.Buffer{} - - o := NewSSHOptions() - o.getoptSet.SetProgram("ssh") - o.getoptSet.SetParameters("app-name [command]") - o.getoptSet.PrintUsage(b) - - return b.String() -} diff --git a/cf-plugin/options/ssh_options_test.go b/cf-plugin/options/ssh_options_test.go deleted file mode 100644 index a38a48e..0000000 --- a/cf-plugin/options/ssh_options_test.go +++ /dev/null @@ -1,369 +0,0 @@ -package options_test - -import ( - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/options" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("SSHOptions", func() { - var ( - opts *options.SSHOptions - args []string - parseError error - ) - - Describe("Parse", func() { - BeforeEach(func() { - opts = options.NewSSHOptions() - args = []string{"ssh"} - parseError = nil - }) - - JustBeforeEach(func() { - parseError = opts.Parse(args) - }) - - Context("when the command name is missing", func() { - BeforeEach(func() { - args = []string{} - }) - - It("returns a UsageError", func() { - Expect(parseError).To(Equal(options.UsageError)) - }) - }) - - Context("when the wrong command name is present", func() { - BeforeEach(func() { - args = []string{"scp"} - }) - - It("returns a UsageError", func() { - Expect(parseError).To(Equal(options.UsageError)) - }) - }) - - Context("when no arguments are specified", func() { - It("returns a UsageError", func() { - Expect(parseError).To(Equal(options.UsageError)) - }) - }) - - Context("when no app name is provided", func() { - BeforeEach(func() { - args = append(args, "-i", "3") - }) - - It("returns a UsageError", func() { - Expect(parseError).To(Equal(options.UsageError)) - }) - }) - - Context("when an app name is provided", func() { - Context("as the only argument", func() { - BeforeEach(func() { - args = append(args, "app-1") - }) - - It("populates the AppName field", func() { - Expect(parseError).NotTo(HaveOccurred()) - Expect(opts.AppName).To(Equal("app-1")) - }) - }) - - Context("as the last argument", func() { - BeforeEach(func() { - args = append(args, "-i", "3", "app-1") - }) - - It("populates the AppName field", func() { - Expect(parseError).NotTo(HaveOccurred()) - Expect(opts.AppName).To(Equal("app-1")) - }) - }) - }) - - Context("when --skip-host-validation is set", func() { - BeforeEach(func() { - args = append(args, "app-name", "--skip-host-validation") - }) - - It("disables host key validation", func() { - Expect(parseError).ToNot(HaveOccurred()) - Expect(opts.SkipHostValidation).To(BeTrue()) - Expect(opts.AppName).To(Equal("app-name")) - }) - }) - - Context("when -k is set", func() { - BeforeEach(func() { - args = append(args, "app-name", "-k") - }) - - It("disables host key validation", func() { - Expect(parseError).ToNot(HaveOccurred()) - Expect(opts.SkipHostValidation).To(BeTrue()) - Expect(opts.AppName).To(Equal("app-name")) - }) - }) - - Context("when an -i flag is provided", func() { - BeforeEach(func() { - args = append(args, "app-name") - }) - - Context("without an argument", func() { - BeforeEach(func() { - args = append(args, "-i") - }) - - It("returns an error", func() { - Expect(parseError).To(MatchError("missing parameter for -i")) - }) - }) - - Context("with a positive integer argument", func() { - BeforeEach(func() { - args = append(args, "-i", "3") - }) - - It("populates the Index field", func() { - Expect(parseError).NotTo(HaveOccurred()) - Expect(opts.Index).To(BeEquivalentTo(3)) - }) - }) - - Context("with a negative integer argument", func() { - BeforeEach(func() { - args = append(args, "-i", "-3") - }) - - It("returns an error", func() { - Expect(parseError).To(MatchError("not a valid number: -3")) - }) - }) - - Context("with a non-numeric argument", func() { - BeforeEach(func() { - args = append(args, "-i", "three") - }) - - It("returns an error", func() { - Expect(parseError).To(MatchError("not a valid number: three")) - }) - }) - }) - - Context("when the -t and -T flags are not used", func() { - BeforeEach(func() { - args = append(args, "app-name") - }) - - It("requests auto tty allocation", func() { - Expect(opts.TerminalRequest).To(Equal(options.REQUEST_TTY_AUTO)) - }) - }) - - Context("when the -T flag is provided", func() { - BeforeEach(func() { - args = append(args, "app-name", "-T") - }) - - It("disables tty allocation", func() { - Expect(opts.TerminalRequest).To(Equal(options.REQUEST_TTY_NO)) - }) - }) - - Context("when the -t flag is used once", func() { - BeforeEach(func() { - args = append(args, "app-name", "-t") - }) - - It("requests tty allocation", func() { - Expect(opts.TerminalRequest).To(Equal(options.REQUEST_TTY_YES)) - }) - }) - - Context("when the -t flag is used more than once", func() { - BeforeEach(func() { - args = append(args, "app-name", "-tt") - }) - It("foces tty allocation", func() { - Expect(opts.TerminalRequest).To(Equal(options.REQUEST_TTY_FORCE)) - }) - }) - - Context("when -t and -T are both specified", func() { - BeforeEach(func() { - args = append(args, "app-name", "-tTt") - }) - - It("disables tty allocation", func() { - Expect(opts.TerminalRequest).To(Equal(options.REQUEST_TTY_NO)) - }) - }) - - Context("when a command is specified", func() { - Context("without flags", func() { - BeforeEach(func() { - args = append(args, "app-name", "-k", "-t", "true") - }) - - It("handles the app and command correctly", func() { - Expect(opts.SkipHostValidation).To(BeTrue()) - Expect(opts.TerminalRequest).To(Equal(options.REQUEST_TTY_YES)) - Expect(opts.AppName).To(Equal("app-name")) - Expect(opts.Command).To(ConsistOf("true")) - }) - }) - - Context("with flags", func() { - BeforeEach(func() { - args = append(args, "-k", "app-name", "-t", "echo", "-n", "hello!") - }) - - It("handles the app and command correctly", func() { - Expect(opts.SkipHostValidation).To(BeTrue()) - Expect(opts.TerminalRequest).To(Equal(options.REQUEST_TTY_YES)) - Expect(opts.AppName).To(Equal("app-name")) - Expect(opts.Command).To(ConsistOf("echo", "-n", "hello!")) - }) - }) - }) - - Context("when local port forwarding is requested", func() { - BeforeEach(func() { - args = append(args, "app-name") - }) - - Context("without an explicit bind address", func() { - BeforeEach(func() { - args = append(args, "-L", "9999:remote:8888") - }) - - It("sets the forward spec", func() { - Expect(parseError).NotTo(HaveOccurred()) - Expect(opts.ForwardSpecs).To(ConsistOf(options.ForwardSpec{ListenAddress: "localhost:9999", ConnectAddress: "remote:8888"})) - }) - }) - - Context("with an explit bind address", func() { - BeforeEach(func() { - args = append(args, "-L", "explicit:9999:remote:8888") - }) - - It("sets the forward spec", func() { - Expect(parseError).NotTo(HaveOccurred()) - Expect(opts.ForwardSpecs).To(ConsistOf(options.ForwardSpec{ListenAddress: "explicit:9999", ConnectAddress: "remote:8888"})) - }) - }) - - Context("with an explicit ipv6 bind address", func() { - BeforeEach(func() { - args = append(args, "-L", "[::]:9999:remote:8888") - }) - - It("sets the forward spec", func() { - Expect(parseError).NotTo(HaveOccurred()) - Expect(opts.ForwardSpecs).To(ConsistOf(options.ForwardSpec{ListenAddress: "[::]:9999", ConnectAddress: "remote:8888"})) - }) - }) - - Context("with an empty bind address", func() { - BeforeEach(func() { - args = append(args, "-L", ":9999:remote:8888") - }) - - It("sets the forward spec", func() { - Expect(parseError).NotTo(HaveOccurred()) - Expect(opts.ForwardSpecs).To(ConsistOf(options.ForwardSpec{ListenAddress: ":9999", ConnectAddress: "remote:8888"})) - }) - }) - - Context("with * as the bind address", func() { - BeforeEach(func() { - args = append(args, "-L", "*:9999:remote:8888") - }) - - It("sets the forward spec", func() { - Expect(parseError).NotTo(HaveOccurred()) - Expect(opts.ForwardSpecs).To(ConsistOf(options.ForwardSpec{ListenAddress: ":9999", ConnectAddress: "remote:8888"})) - }) - }) - - Context("with an explicit ipv6 connect address", func() { - BeforeEach(func() { - args = append(args, "-L", "[::]:9999:[2001:db8::1]:8888") - }) - - It("sets the forward spec", func() { - Expect(parseError).NotTo(HaveOccurred()) - Expect(opts.ForwardSpecs).To(ConsistOf(options.ForwardSpec{ListenAddress: "[::]:9999", ConnectAddress: "[2001:db8::1]:8888"})) - }) - }) - - Context("with a missing bracket", func() { - BeforeEach(func() { - args = append(args, "-L", "localhost:9999:[example.com:8888") - }) - - It("returns an error", func() { - Expect(parseError).To(MatchError(`Argument missing closing bracket: "[example.com:8888"`)) - }) - }) - - Context("when a closing bracket is not followed by a colon", func() { - BeforeEach(func() { - args = append(args, "-L", "localhost:9999:[example.com]8888") - }) - - It("returns an error", func() { - Expect(parseError).To(MatchError(`Unexpected token: "8888"`)) - }) - }) - - Context("when multiple local port forward options are specified", func() { - BeforeEach(func() { - args = append(args, "-L", "9999:remote:8888") - args = append(args, "-L8080:remote:80") - }) - - It("sets the forward specs", func() { - Expect(parseError).NotTo(HaveOccurred()) - Expect(opts.ForwardSpecs).To(ConsistOf( - options.ForwardSpec{ListenAddress: "localhost:9999", ConnectAddress: "remote:8888"}, - options.ForwardSpec{ListenAddress: "localhost:8080", ConnectAddress: "remote:80"}, - )) - }) - }) - }) - - Context("when -N is specified", func() { - BeforeEach(func() { - args = append(args, "app-name", "-N") - }) - - It("indicates that no remote command should be run", func() { - Expect(parseError).ToNot(HaveOccurred()) - Expect(opts.SkipRemoteExecution).To(BeTrue()) - Expect(opts.AppName).To(Equal("app-name")) - }) - }) - }) - - Describe("SSHUsage", func() { - It("prints usage information", func() { - usage := options.SSHUsage() - - Expect(usage).To(ContainSubstring("Usage: ssh [-kNTt] [-i app-instance-index] [-L [bind_address:]port:host:hostport] app-name [command]")) - Expect(usage).To(ContainSubstring("-i, --index=app-instance-index")) - Expect(usage).To(ContainSubstring("-k, --skip-host-validation")) - Expect(usage).To(ContainSubstring("-L [bind_address:]port:host:hostport")) - Expect(usage).To(ContainSubstring("-N do not execute a remote command")) - Expect(usage).To(ContainSubstring("-T disable pseudo-tty allocation")) - Expect(usage).To(ContainSubstring("-t force pseudo-tty allocation")) - }) - }) -}) diff --git a/cf-plugin/scripts/build-plugins.sh b/cf-plugin/scripts/build-plugins.sh deleted file mode 100755 index 95fc721..0000000 --- a/cf-plugin/scripts/build-plugins.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -x - -go get github.com/Sirupsen/logrus - -GOOS=linux GOARCH=386 go build -o ssh-plugin-linux-386 . -GOOS=linux GOARCH=amd64 go build -o ssh-plugin-linux-amd64 . -GOOS=windows GOARCH=386 go build -o ssh-plugin-win32.exe . -GOOS=windows GOARCH=amd64 go build -o ssh-plugin-win64.exe . -go build -o ssh-plugin-darwin-amd64 . - -shasum -a1 ssh-plugin-* diff --git a/cf-plugin/sigwinch/sigwinch.go b/cf-plugin/sigwinch/sigwinch.go deleted file mode 100644 index ace01f9..0000000 --- a/cf-plugin/sigwinch/sigwinch.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build !windows - -package sigwinch - -import "syscall" - -func SIGWINCH() syscall.Signal { - return syscall.SIGWINCH -} diff --git a/cf-plugin/sigwinch/sigwinch_win.go b/cf-plugin/sigwinch/sigwinch_win.go deleted file mode 100644 index eac5f1e..0000000 --- a/cf-plugin/sigwinch/sigwinch_win.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build windows - -package sigwinch - -import "syscall" - -func SIGWINCH() syscall.Signal { - panic("Not supported on windows") -} diff --git a/cf-plugin/ssh_plugin.go b/cf-plugin/ssh_plugin.go deleted file mode 100644 index 01a730c..0000000 --- a/cf-plugin/ssh_plugin.go +++ /dev/null @@ -1,202 +0,0 @@ -package main - -import ( - "fmt" - "io" - "os" - "time" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/cmd" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/app" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/credential" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/info" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/models/space" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/options" - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/terminal" - "github.com/cloudfoundry/cli/plugin" - "golang.org/x/crypto/ssh" -) - -type SSHPlugin struct { - OutputWriter io.Writer -} - -func (p *SSHPlugin) GetMetadata() plugin.PluginMetadata { - return plugin.PluginMetadata{ - Name: "Diego-SSH", - Version: plugin.VersionType{ - Major: 0, - Minor: 2, - Build: 1, - }, - Commands: []plugin.Command{ - { - Name: "ssh", - HelpText: "ssh to an application container instance", - UsageDetails: plugin.Usage{ - Usage: options.SSHUsage(), - }, - }, - { - Name: "enable-ssh", - HelpText: "enable ssh for the application", - UsageDetails: plugin.Usage{ - Usage: cmd.EnableSSHUsage, - }, - }, - { - Name: "disable-ssh", - HelpText: "disable ssh for the application", - UsageDetails: plugin.Usage{ - Usage: cmd.DisableSSHUsage, - }, - }, - { - Name: "ssh-enabled", - HelpText: "reports whether SSH is enabled on an application container instance", - UsageDetails: plugin.Usage{ - Usage: cmd.SSHEnabledUsage, - }, - }, - { - Name: "allow-space-ssh", - HelpText: "allow SSH access for the space", - UsageDetails: plugin.Usage{ - Usage: cmd.AllowSSHUsage, - }, - }, - { - Name: "disallow-space-ssh", - HelpText: "disallow SSH access for the space", - UsageDetails: plugin.Usage{ - Usage: cmd.DisallowSSHUsage, - }, - }, - { - Name: "space-ssh-allowed", - HelpText: "reports whether SSH is allowed in a space", - UsageDetails: plugin.Usage{ - Usage: cmd.SSHAllowedUsage, - }, - }, - { - Name: "get-ssh-code", - HelpText: "get a one time password for ssh clients", - UsageDetails: plugin.Usage{ - Usage: cmd.GetSSHCodeUsage, - }, - }, - }, - } -} - -func (p *SSHPlugin) Run(cli plugin.CliConnection, args []string) { - p.OutputWriter = os.Stdout - appFactory := app.NewAppFactory(cli, models.Curl) - infoFactory := info.NewInfoFactory(cli) - credFactory := credential.NewCredentialFactory(cli, infoFactory) - spaceFactory := space.NewSpaceFactory(cli, models.Curl) - - switch args[0] { - case "CLI-MESSAGE-UNINSTALL": - return - case "enable-ssh": - err := cmd.EnableSSH(args, appFactory) - if err != nil { - p.Fatal(err) - } - case "disable-ssh": - err := cmd.DisableSSH(args, appFactory) - if err != nil { - p.Fatal(err) - } - case "ssh-enabled": - err := cmd.SSHEnabled(args, appFactory, p.OutputWriter) - if err != nil { - p.Fatal(err) - } - case "allow-space-ssh": - err := cmd.AllowSSH(args, spaceFactory) - if err != nil { - p.Fatal(err) - } - case "disallow-space-ssh": - err := cmd.DisallowSSH(args, spaceFactory) - if err != nil { - p.Fatal(err) - } - case "space-ssh-allowed": - err := cmd.SSHAllowed(args, spaceFactory, p.OutputWriter) - if err != nil { - p.Fatal(err) - } - case "get-ssh-code": - err := cmd.GetSSHCode(args, credFactory, p.OutputWriter) - if err != nil { - p.Fatal(err) - } - case "ssh": - opts := options.NewSSHOptions() - err := opts.Parse(args) - if err != nil { - p.Fail(err.Error()) - fmt.Fprintf(p.OutputWriter, options.SSHUsage()) - return - } - - secureShell := cmd.NewSecureShell( - cmd.DefaultSecureDialer(), - terminal.DefaultHelper(), - cmd.DefaultListenerFactory(), - 30*time.Second, - appFactory, - infoFactory, - credFactory, - ) - - err = secureShell.Connect(opts) - if err != nil { - p.Fail(err.Error()) - return - } - defer secureShell.Close() - - err = secureShell.LocalPortForward() - if err != nil { - return - } - - if opts.SkipRemoteExecution { - err = secureShell.Wait() - } else { - err = secureShell.InteractiveSession() - } - - if err == nil { - return - } - - if exitError, ok := err.(*ssh.ExitError); ok { - exitStatus := exitError.ExitStatus() - if sig := exitError.Signal(); sig != "" { - fmt.Printf("Process terminated by signal: %s. Exited with %d.\n", sig, exitStatus) - } - os.Exit(exitStatus) - } else { - p.Fail(err.Error()) - } - - default: - p.Fail("Invalid command") - } -} - -func (p *SSHPlugin) Fatal(err error) { - p.Fail(err.Error()) - os.Exit(1) -} - -func (p *SSHPlugin) Fail(message string) { - fmt.Fprintf(p.OutputWriter, "FAILED\n\n%s\n", message) -} diff --git a/cf-plugin/terminal/helper.go b/cf-plugin/terminal/helper.go deleted file mode 100644 index 6760f2c..0000000 --- a/cf-plugin/terminal/helper.go +++ /dev/null @@ -1,47 +0,0 @@ -package terminal - -import ( - "io" - - "github.com/docker/docker/pkg/term" -) - -//go:generate counterfeiter -o terminal_helper_fakes/fake_terminal_helper.go . TerminalHelper -type TerminalHelper interface { - StdStreams() (stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) - GetFdInfo(in interface{}) (fd uintptr, isTerminal bool) - SetRawTerminal(fd uintptr) (*term.State, error) - RestoreTerminal(fd uintptr, state *term.State) error - IsTerminal(fd uintptr) bool - GetWinsize(fd uintptr) (*term.Winsize, error) -} - -type terminalHelper struct{} - -func DefaultHelper() TerminalHelper { - return &terminalHelper{} -} - -func (t *terminalHelper) StdStreams() (io.ReadCloser, io.Writer, io.Writer) { - return term.StdStreams() -} - -func (t *terminalHelper) GetFdInfo(in interface{}) (uintptr, bool) { - return term.GetFdInfo(in) -} - -func (t *terminalHelper) SetRawTerminal(fd uintptr) (*term.State, error) { - return term.SetRawTerminal(fd) -} - -func (t *terminalHelper) RestoreTerminal(fd uintptr, state *term.State) error { - return term.RestoreTerminal(fd, state) -} - -func (t *terminalHelper) IsTerminal(fd uintptr) bool { - return term.IsTerminal(fd) -} - -func (t *terminalHelper) GetWinsize(fd uintptr) (*term.Winsize, error) { - return term.GetWinsize(fd) -} diff --git a/cf-plugin/terminal/helper_test.go b/cf-plugin/terminal/helper_test.go deleted file mode 100644 index b2eb82e..0000000 --- a/cf-plugin/terminal/helper_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package terminal_test - -import ( - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/terminal" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Helper", func() { - var helper terminal.TerminalHelper - - BeforeEach(func() { - helper = terminal.DefaultHelper() - }) - - Describe("DefaultTerminalHelper", func() { - It("returns a terminal helper", func() { - Expect(helper).NotTo(BeNil()) - }) - }) -}) diff --git a/cf-plugin/terminal/terminal_helper_fakes/fake_terminal_helper.go b/cf-plugin/terminal/terminal_helper_fakes/fake_terminal_helper.go deleted file mode 100644 index 3e35fc7..0000000 --- a/cf-plugin/terminal/terminal_helper_fakes/fake_terminal_helper.go +++ /dev/null @@ -1,257 +0,0 @@ -// This file was generated by counterfeiter -package terminal_helper_fakes - -import ( - "io" - "sync" - - "github.com/cloudfoundry-incubator/diego-ssh/cf-plugin/terminal" - "github.com/docker/docker/pkg/term" -) - -type FakeTerminalHelper struct { - StdStreamsStub func() (stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) - stdStreamsMutex sync.RWMutex - stdStreamsArgsForCall []struct{} - stdStreamsReturns struct { - result1 io.ReadCloser - result2 io.Writer - result3 io.Writer - } - GetFdInfoStub func(in interface{}) (fd uintptr, isTerminal bool) - getFdInfoMutex sync.RWMutex - getFdInfoArgsForCall []struct { - in interface{} - } - getFdInfoReturns struct { - result1 uintptr - result2 bool - } - SetRawTerminalStub func(fd uintptr) (*term.State, error) - setRawTerminalMutex sync.RWMutex - setRawTerminalArgsForCall []struct { - fd uintptr - } - setRawTerminalReturns struct { - result1 *term.State - result2 error - } - RestoreTerminalStub func(fd uintptr, state *term.State) error - restoreTerminalMutex sync.RWMutex - restoreTerminalArgsForCall []struct { - fd uintptr - state *term.State - } - restoreTerminalReturns struct { - result1 error - } - IsTerminalStub func(fd uintptr) bool - isTerminalMutex sync.RWMutex - isTerminalArgsForCall []struct { - fd uintptr - } - isTerminalReturns struct { - result1 bool - } - GetWinsizeStub func(fd uintptr) (*term.Winsize, error) - getWinsizeMutex sync.RWMutex - getWinsizeArgsForCall []struct { - fd uintptr - } - getWinsizeReturns struct { - result1 *term.Winsize - result2 error - } -} - -func (fake *FakeTerminalHelper) StdStreams() (stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) { - fake.stdStreamsMutex.Lock() - fake.stdStreamsArgsForCall = append(fake.stdStreamsArgsForCall, struct{}{}) - fake.stdStreamsMutex.Unlock() - if fake.StdStreamsStub != nil { - return fake.StdStreamsStub() - } else { - return fake.stdStreamsReturns.result1, fake.stdStreamsReturns.result2, fake.stdStreamsReturns.result3 - } -} - -func (fake *FakeTerminalHelper) StdStreamsCallCount() int { - fake.stdStreamsMutex.RLock() - defer fake.stdStreamsMutex.RUnlock() - return len(fake.stdStreamsArgsForCall) -} - -func (fake *FakeTerminalHelper) StdStreamsReturns(result1 io.ReadCloser, result2 io.Writer, result3 io.Writer) { - fake.StdStreamsStub = nil - fake.stdStreamsReturns = struct { - result1 io.ReadCloser - result2 io.Writer - result3 io.Writer - }{result1, result2, result3} -} - -func (fake *FakeTerminalHelper) GetFdInfo(in interface{}) (fd uintptr, isTerminal bool) { - fake.getFdInfoMutex.Lock() - fake.getFdInfoArgsForCall = append(fake.getFdInfoArgsForCall, struct { - in interface{} - }{in}) - fake.getFdInfoMutex.Unlock() - if fake.GetFdInfoStub != nil { - return fake.GetFdInfoStub(in) - } else { - return fake.getFdInfoReturns.result1, fake.getFdInfoReturns.result2 - } -} - -func (fake *FakeTerminalHelper) GetFdInfoCallCount() int { - fake.getFdInfoMutex.RLock() - defer fake.getFdInfoMutex.RUnlock() - return len(fake.getFdInfoArgsForCall) -} - -func (fake *FakeTerminalHelper) GetFdInfoArgsForCall(i int) interface{} { - fake.getFdInfoMutex.RLock() - defer fake.getFdInfoMutex.RUnlock() - return fake.getFdInfoArgsForCall[i].in -} - -func (fake *FakeTerminalHelper) GetFdInfoReturns(result1 uintptr, result2 bool) { - fake.GetFdInfoStub = nil - fake.getFdInfoReturns = struct { - result1 uintptr - result2 bool - }{result1, result2} -} - -func (fake *FakeTerminalHelper) SetRawTerminal(fd uintptr) (*term.State, error) { - fake.setRawTerminalMutex.Lock() - fake.setRawTerminalArgsForCall = append(fake.setRawTerminalArgsForCall, struct { - fd uintptr - }{fd}) - fake.setRawTerminalMutex.Unlock() - if fake.SetRawTerminalStub != nil { - return fake.SetRawTerminalStub(fd) - } else { - return fake.setRawTerminalReturns.result1, fake.setRawTerminalReturns.result2 - } -} - -func (fake *FakeTerminalHelper) SetRawTerminalCallCount() int { - fake.setRawTerminalMutex.RLock() - defer fake.setRawTerminalMutex.RUnlock() - return len(fake.setRawTerminalArgsForCall) -} - -func (fake *FakeTerminalHelper) SetRawTerminalArgsForCall(i int) uintptr { - fake.setRawTerminalMutex.RLock() - defer fake.setRawTerminalMutex.RUnlock() - return fake.setRawTerminalArgsForCall[i].fd -} - -func (fake *FakeTerminalHelper) SetRawTerminalReturns(result1 *term.State, result2 error) { - fake.SetRawTerminalStub = nil - fake.setRawTerminalReturns = struct { - result1 *term.State - result2 error - }{result1, result2} -} - -func (fake *FakeTerminalHelper) RestoreTerminal(fd uintptr, state *term.State) error { - fake.restoreTerminalMutex.Lock() - fake.restoreTerminalArgsForCall = append(fake.restoreTerminalArgsForCall, struct { - fd uintptr - state *term.State - }{fd, state}) - fake.restoreTerminalMutex.Unlock() - if fake.RestoreTerminalStub != nil { - return fake.RestoreTerminalStub(fd, state) - } else { - return fake.restoreTerminalReturns.result1 - } -} - -func (fake *FakeTerminalHelper) RestoreTerminalCallCount() int { - fake.restoreTerminalMutex.RLock() - defer fake.restoreTerminalMutex.RUnlock() - return len(fake.restoreTerminalArgsForCall) -} - -func (fake *FakeTerminalHelper) RestoreTerminalArgsForCall(i int) (uintptr, *term.State) { - fake.restoreTerminalMutex.RLock() - defer fake.restoreTerminalMutex.RUnlock() - return fake.restoreTerminalArgsForCall[i].fd, fake.restoreTerminalArgsForCall[i].state -} - -func (fake *FakeTerminalHelper) RestoreTerminalReturns(result1 error) { - fake.RestoreTerminalStub = nil - fake.restoreTerminalReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeTerminalHelper) IsTerminal(fd uintptr) bool { - fake.isTerminalMutex.Lock() - fake.isTerminalArgsForCall = append(fake.isTerminalArgsForCall, struct { - fd uintptr - }{fd}) - fake.isTerminalMutex.Unlock() - if fake.IsTerminalStub != nil { - return fake.IsTerminalStub(fd) - } else { - return fake.isTerminalReturns.result1 - } -} - -func (fake *FakeTerminalHelper) IsTerminalCallCount() int { - fake.isTerminalMutex.RLock() - defer fake.isTerminalMutex.RUnlock() - return len(fake.isTerminalArgsForCall) -} - -func (fake *FakeTerminalHelper) IsTerminalArgsForCall(i int) uintptr { - fake.isTerminalMutex.RLock() - defer fake.isTerminalMutex.RUnlock() - return fake.isTerminalArgsForCall[i].fd -} - -func (fake *FakeTerminalHelper) IsTerminalReturns(result1 bool) { - fake.IsTerminalStub = nil - fake.isTerminalReturns = struct { - result1 bool - }{result1} -} - -func (fake *FakeTerminalHelper) GetWinsize(fd uintptr) (*term.Winsize, error) { - fake.getWinsizeMutex.Lock() - fake.getWinsizeArgsForCall = append(fake.getWinsizeArgsForCall, struct { - fd uintptr - }{fd}) - fake.getWinsizeMutex.Unlock() - if fake.GetWinsizeStub != nil { - return fake.GetWinsizeStub(fd) - } else { - return fake.getWinsizeReturns.result1, fake.getWinsizeReturns.result2 - } -} - -func (fake *FakeTerminalHelper) GetWinsizeCallCount() int { - fake.getWinsizeMutex.RLock() - defer fake.getWinsizeMutex.RUnlock() - return len(fake.getWinsizeArgsForCall) -} - -func (fake *FakeTerminalHelper) GetWinsizeArgsForCall(i int) uintptr { - fake.getWinsizeMutex.RLock() - defer fake.getWinsizeMutex.RUnlock() - return fake.getWinsizeArgsForCall[i].fd -} - -func (fake *FakeTerminalHelper) GetWinsizeReturns(result1 *term.Winsize, result2 error) { - fake.GetWinsizeStub = nil - fake.getWinsizeReturns = struct { - result1 *term.Winsize - result2 error - }{result1, result2} -} - -var _ terminal.TerminalHelper = new(FakeTerminalHelper) diff --git a/cf-plugin/terminal/terminal_suite_test.go b/cf-plugin/terminal/terminal_suite_test.go deleted file mode 100644 index 7ae041f..0000000 --- a/cf-plugin/terminal/terminal_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package terminal_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestTerminal(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Terminal Suite") -}