1package ssh_test
2
3import (
4	"errors"
5
6	boshsys "github.com/cloudfoundry/bosh-utils/system"
7	fakesys "github.com/cloudfoundry/bosh-utils/system/fakes"
8	. "github.com/onsi/ginkgo"
9	. "github.com/onsi/gomega"
10
11	boshdir "github.com/cloudfoundry/bosh-cli/director"
12	. "github.com/cloudfoundry/bosh-cli/ssh"
13)
14
15var _ = Describe("SessionImpl", func() {
16	var (
17		connOpts       ConnectionOpts
18		sessOpts       SessionImplOpts
19		result         boshdir.SSHResult
20		privKeyFile    *fakesys.FakeFile
21		knownHostsFile *fakesys.FakeFile
22		fs             *fakesys.FakeFileSystem
23		session        *SessionImpl
24	)
25
26	BeforeEach(func() {
27		connOpts = ConnectionOpts{}
28		sessOpts = SessionImplOpts{}
29		result = boshdir.SSHResult{}
30		fs = fakesys.NewFakeFileSystem()
31		privKeyFile = fakesys.NewFakeFile("/tmp/priv-key", fs)
32		knownHostsFile = fakesys.NewFakeFile("/tmp/known-hosts", fs)
33		fs.ReturnTempFilesByPrefix = map[string]boshsys.File{
34			"ssh-priv-key":    privKeyFile,
35			"ssh-known-hosts": knownHostsFile,
36		}
37		session = NewSessionImpl(connOpts, sessOpts, result, fs)
38	})
39
40	Describe("Start", func() {
41		act := func() *SessionImpl { return NewSessionImpl(connOpts, sessOpts, result, fs) }
42
43		It("writes out private key", func() {
44			connOpts.PrivateKey = "priv-key"
45
46			_, err := act().Start()
47			Expect(err).ToNot(HaveOccurred())
48			Expect(fs.ReadFileString("/tmp/priv-key")).To(Equal("priv-key"))
49		})
50
51		It("returns error if cannot create private key temp file", func() {
52			fs.TempFileErrorsByPrefix = map[string]error{
53				"ssh-priv-key": errors.New("fake-err"),
54			}
55
56			_, err := act().Start()
57			Expect(err).To(HaveOccurred())
58			Expect(err.Error()).To(ContainSubstring("fake-err"))
59		})
60
61		It("returns error if writing public key failed", func() {
62			privKeyFile.WriteErr = errors.New("fake-err")
63
64			_, err := act().Start()
65			Expect(err).To(HaveOccurred())
66			Expect(err.Error()).To(ContainSubstring("fake-err"))
67		})
68
69		It("writes out all known hosts", func() {
70			result.Hosts = []boshdir.Host{
71				{Host: "127.0.0.1", HostPublicKey: "pub-key1"},
72				{Host: "127.0.0.2", HostPublicKey: "pub-key2"},
73				{Host: "::1", HostPublicKey: "pub-key3"},
74			}
75
76			_, err := act().Start()
77			Expect(err).ToNot(HaveOccurred())
78			Expect(fs.ReadFileString("/tmp/known-hosts")).To(Equal(
79				"127.0.0.1 pub-key1\n127.0.0.2 pub-key2\n::1 pub-key3\n"))
80		})
81
82		It("returns error if cannot create known hosts temp file and deletes private key", func() {
83			fs.TempFileErrorsByPrefix = map[string]error{
84				"ssh-known-hosts": errors.New("fake-err"),
85			}
86
87			_, err := act().Start()
88			Expect(err).To(HaveOccurred())
89			Expect(err.Error()).To(ContainSubstring("fake-err"))
90
91			Expect(fs.FileExists("/tmp/priv-key")).To(BeFalse())
92		})
93
94		It("returns error if writing known hosts failed and deletes private key", func() {
95			result.Hosts = []boshdir.Host{
96				{Host: "127.0.0.1", HostPublicKey: "pub-key1"},
97			}
98			knownHostsFile.WriteErr = errors.New("fake-err")
99
100			_, err := act().Start()
101			Expect(err).To(HaveOccurred())
102			Expect(err.Error()).To(ContainSubstring("fake-err"))
103
104			Expect(fs.FileExists("/tmp/priv-key")).To(BeFalse())
105		})
106
107		It("returns ssh arguments with appropriate configuration", func() {
108			result.Hosts = []boshdir.Host{{Host: "127.0.0.1"}} // populate results
109			connOpts.PrivateKey = "priv-key"                   // populate connOpts
110
111			args, err := act().Start()
112			Expect(err).ToNot(HaveOccurred())
113			Expect(args.ConnOpts).To(Equal(connOpts))
114			Expect(args.Result).To(Equal(result))
115			Expect(args.ForceTTY).To(BeFalse())
116			Expect(args.PrivKeyFile).To(Equal(privKeyFile))
117			Expect(args.KnownHostsFile).To(Equal(knownHostsFile))
118
119			sessOpts.ForceTTY = true
120
121			args, err = act().Start()
122			Expect(err).ToNot(HaveOccurred())
123			Expect(args.ConnOpts).To(Equal(connOpts))
124			Expect(args.Result).To(Equal(result))
125			Expect(args.ForceTTY).To(BeTrue())
126			Expect(args.PrivKeyFile).To(Equal(privKeyFile))
127			Expect(args.KnownHostsFile).To(Equal(knownHostsFile))
128		})
129	})
130
131	Describe("Finish", func() {
132		BeforeEach(func() {
133			_, err := session.Start()
134			Expect(err).ToNot(HaveOccurred())
135		})
136
137		It("removes private key and known hosts files", func() {
138			err := session.Finish()
139			Expect(err).ToNot(HaveOccurred())
140			Expect(fs.FileExists("/tmp/priv-key")).To(BeFalse())
141			Expect(fs.FileExists("/tmp/known-hosts")).To(BeFalse())
142		})
143
144		It("returns error if deleting private key file fails but still deletes known hosts file", func() {
145			fs.RemoveAllStub = func(path string) error {
146				if path == "/tmp/priv-key" {
147					return errors.New("fake-err")
148				}
149				return nil
150			}
151			err := session.Finish()
152			Expect(err).To(HaveOccurred())
153			Expect(err.Error()).To(ContainSubstring("fake-err"))
154			Expect(fs.FileExists("/tmp/known-hosts")).To(BeFalse())
155		})
156
157		It("returns error if deleting known hosts file fails but still deletes private key file", func() {
158			fs.RemoveAllStub = func(path string) error {
159				if path == "/tmp/known-hosts" {
160					return errors.New("fake-err")
161				}
162				return nil
163			}
164			err := session.Finish()
165			Expect(err).To(HaveOccurred())
166			Expect(err.Error()).To(ContainSubstring("fake-err"))
167			Expect(fs.FileExists("/tmp/priv-key")).To(BeFalse())
168		})
169	})
170})
171