Merge lp:~axwalk/juju-core/sshstorage-stream-input into lp:~go-bot/juju-core/trunk

Proposed by Andrew Wilkins
Status: Merged
Approved by: Andrew Wilkins
Approved revision: no longer in the source branch.
Merged at revision: 1922
Proposed branch: lp:~axwalk/juju-core/sshstorage-stream-input
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 366 lines (+256/-22)
6 files modified
environs/sshstorage/export_test.go (+8/-0)
environs/sshstorage/linewrapwriter.go (+57/-0)
environs/sshstorage/linewrapwriter_test.go (+151/-0)
environs/sshstorage/storage.go (+26/-17)
environs/sshstorage/storage_test.go (+0/-5)
environs/sshstorage/suite_test.go (+14/-0)
To merge this branch: bzr merge lp:~axwalk/juju-core/sshstorage-stream-input
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+188508@code.launchpad.net

Commit message

environs/sshstorage: stream stdin

Change sshstorage's Put to stream to the
ssh command's stdin, rather than reading
into memory and then writing.

Also, drive-by: use `mktemp --tmpdir` rather
than exporting $TMPDIR.

https://codereview.appspot.com/14197043/

Description of the change

environs/sshstorage: stream stdin

Change sshstorage's Put to stream to the
ssh command's stdin, rather than reading
into memory and then writing.

Also, drive-by: use `mktemp --tmpdir` rather
than exporting $TMPDIR.

https://codereview.appspot.com/14197043/

To post a comment you must log in.
Revision history for this message
Andrew Wilkins (axwalk) wrote :
Download full text (3.4 KiB)

Reviewers: mp+188508_code.launchpad.net,

Message:
Please take a look.

Description:
environs/sshstorage: stream stdin

Change sshstorage's Put to stream to the
ssh command's stdin, rather than reading
into memory and then writing.

Also, drive-by: use `mktemp --tmpdir` rather
than exporting $TMPDIR.

https://code.launchpad.net/~axwalk/juju-core/sshstorage-stream-input/+merge/188508

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/14197043/

Affected files (+15, -16 lines):
   A [revision details]
   M environs/sshstorage/storage.go

Index: [revision details]
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: tarmac-20130930183118-qlwtuzv7j6d2iy73
+New revision: <email address hidden>

Index: environs/sshstorage/storage.go
=== modified file 'environs/sshstorage/storage.go'
--- environs/sshstorage/storage.go 2013-09-24 01:55:30 +0000
+++ environs/sshstorage/storage.go 2013-10-01 04:04:41 +0000
@@ -129,10 +129,10 @@

  func (s *SSHStorage) runf(flockmode flockmode, command string,
args ...interface{}) (string, error) {
   command = fmt.Sprintf(command, args...)
- return s.run(flockmode, command, nil)
+ return s.run(flockmode, command, nil, 0)
  }

-func (s *SSHStorage) run(flockmode flockmode, command string, input
[]byte) (string, error) {
+func (s *SSHStorage) run(flockmode flockmode, command string, input
io.Reader, inputlen int64) (string, error) {
   const rcPrefix = "JUJU-RC: "
   command = fmt.Sprintf(
    "SHELL=/bin/bash flock %s %s -c %s",
@@ -140,23 +140,24 @@
    s.remotepath,
    utils.ShQuote(command),
   )
- var encoded string
   if input != nil {
- encoded = base64.StdEncoding.EncodeToString(input)
- command = fmt.Sprintf(
- "head -q -c %d | base64 -d | (%s)",
- len(encoded),
- command,
- )
+ command = fmt.Sprintf("head -q -n 1 | base64 -d | (%s)", command)
   }
   command = fmt.Sprintf("(%s) 2>&1; echo %s$?", command, rcPrefix)
   if _, err := s.stdin.Write([]byte(command + "\n")); err != nil {
    return "", fmt.Errorf("failed to write command: %v", err)
   }
   if input != nil {
- if _, err := s.stdin.Write([]byte(encoded)); err != nil {
+ encoder := base64.NewEncoder(base64.StdEncoding, s.stdin)
+ if _, err := io.CopyN(encoder, input, inputlen); err != nil {
     return "", fmt.Errorf("failed to write input: %v", err)
    }
+ if err := encoder.Close(); err != nil {
+ return "", fmt.Errorf("failed to flush input: %v", err)
+ }
+ if _, err := s.stdin.Write([]byte("\n")); err != nil {
+ return "", fmt.Errorf("failed to terminate input: %v", err)
+ }
   }
   var output []string
   for s.scanner.Scan() {
@@ -259,21 +260,17 @@
   if err != nil {
    return err
   }
- buf := make([]byte, length)
- if _, err := r.Read(buf); err != nil {
- return err
- }
   path = utils.ShQuote(path)
   tmpdir := utils.ShQuote(s.tmpdir)

   // Write to a temporary file ($TMPFILE), then mv atomically.
   command := fmt.Sprintf("mkdir -p `dirname %s` && cat > $TMPFILE", path)
   command = fmt.Sprintf(
- "export TMPDIR=%...

Read more...

Revision history for this message
Andrew Wilkins (axwalk) wrote :
Revision history for this message
Andrew Wilkins (axwalk) wrote :
Revision history for this message
Roger Peppe (rogpeppe) wrote :

Thanks for doing this. Looks good, with a few suggestions below.

https://codereview.appspot.com/14197043/diff/1003/environs/sshstorage/storage.go
File environs/sshstorage/storage.go (right):

https://codereview.appspot.com/14197043/diff/1003/environs/sshstorage/storage.go#newcode153
environs/sshstorage/storage.go:153: command = fmt.Sprintf("(%s) << EOF",
command)
s/EOF/'@EOF'/

using quotes means that the shell doesn't scan through
for potential variable expansion.

The @ character is just so it can't appear in a normal
base64 output.

https://codereview.appspot.com/14197043/diff/1003/environs/sshstorage/storage.go#newcode159
environs/sshstorage/storage.go:159: wrapper, err :=
utils.NewWrapWriter(s.stdin, base64LineLength)
You should consider wrapping s.stdin in a bufio.Writer here.
(perhaps even wrap it inside the SSHStorage and flushing
when appropriate)

https://codereview.appspot.com/14197043/diff/1003/utils/wrapwriter.go
File utils/wrapwriter.go (right):

https://codereview.appspot.com/14197043/diff/1003/utils/wrapwriter.go#newcode44
utils/wrapwriter.go:44: return n, err
s/n/total/

https://codereview.appspot.com/14197043/diff/1003/utils/wrapwriter.go#newcode44
utils/wrapwriter.go:44: return n, err
s/n/total/

https://codereview.appspot.com/14197043/diff/1003/utils/wrapwriter_test.go
File utils/wrapwriter_test.go (right):

https://codereview.appspot.com/14197043/diff/1003/utils/wrapwriter_test.go#newcode29
utils/wrapwriter_test.go:29: type test struct {
I'd like to see better tests here:

- what happens when writes return errors or part
- does it cope ok with several writes in a row?

For the latter, perhaps take a piece of input, split it up
many different ways (use some simple algorithm or
table to do that), and check that the output is
always the same.

https://codereview.appspot.com/14197043/

Revision history for this message
John A Meinel (jameinel) wrote :

https://codereview.appspot.com/14197043/diff/1003/environs/sshstorage/storage.go
File environs/sshstorage/storage.go (right):

https://codereview.appspot.com/14197043/diff/1003/environs/sshstorage/storage.go#newcode153
environs/sshstorage/storage.go:153: command = fmt.Sprintf("(%s) << EOF",
command)
On 2013/10/01 10:27:39, rog wrote:
> s/EOF/'@EOF'/

> using quotes means that the shell doesn't scan through
> for potential variable expansion.

> The @ character is just so it can't appear in a normal
> base64 output.

Sounds interesting, though we should test that it works properly with
/bin/dash.

https://codereview.appspot.com/14197043/

Revision history for this message
Roger Peppe (rogpeppe) wrote :

On 1 October 2013 12:09, <email address hidden> wrote:
>
> https://codereview.appspot.com/14197043/diff/1003/environs/sshstorage/storage.go
> File environs/sshstorage/storage.go (right):
>
> https://codereview.appspot.com/14197043/diff/1003/environs/sshstorage/storage.go#newcode153
> environs/sshstorage/storage.go:153: command = fmt.Sprintf("(%s) << EOF",
> command)
> On 2013/10/01 10:27:39, rog wrote:
>>
>> s/EOF/'@EOF'/
>
>
>> using quotes means that the shell doesn't scan through
>> for potential variable expansion.
>
>
>> The @ character is just so it can't appear in a normal
>> base64 output.
>
>
> Sounds interesting, though we should test that it works properly with
> /bin/dash.

It does (it's original Bourne shell behaviour).

Revision history for this message
Andrew Wilkins (axwalk) wrote :
Revision history for this message
Roger Peppe (rogpeppe) wrote :

LGTM modulo the below suggestions. Thanks!

https://codereview.appspot.com/14197043/diff/14001/environs/sshstorage/linewrapwriter.go
File environs/sshstorage/linewrapwriter.go (right):

https://codereview.appspot.com/14197043/diff/14001/environs/sshstorage/linewrapwriter.go#newcode22
environs/sshstorage/linewrapwriter.go:22: // after every "lineLength"
bytes.
// Moreover it gives no consideration to multibyte
// utf-8 characters, which it can split arbitrarily.

https://codereview.appspot.com/14197043/diff/14001/environs/sshstorage/storage.go
File environs/sshstorage/storage.go (right):

https://codereview.appspot.com/14197043/diff/14001/environs/sshstorage/storage.go#newcode149
environs/sshstorage/storage.go:149: stdin = bufio.NewWriter(s.stdin)
I'm not sure this half-way house is great.
I'd either make s.stdin a bufio.Writer, or
call NewWriter inside the later "if input != nil" block,
avoiding the ugly dynamic type conversion either way,
and allowing the use of the more natural WriteString
methods.

https://codereview.appspot.com/14197043/diff/14001/environs/sshstorage/storage.go#newcode154
environs/sshstorage/storage.go:154: // here-document must start on the
outer-most command.
That's not actually the case, I think. Did you try it?

https://codereview.appspot.com/14197043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :
Revision history for this message
Andrew Wilkins (axwalk) wrote :

https://codereview.appspot.com/14197043/diff/14001/environs/sshstorage/linewrapwriter.go
File environs/sshstorage/linewrapwriter.go (right):

https://codereview.appspot.com/14197043/diff/14001/environs/sshstorage/linewrapwriter.go#newcode22
environs/sshstorage/linewrapwriter.go:22: // after every "lineLength"
bytes.
On 2013/10/01 15:28:47, rog wrote:
> // Moreover it gives no consideration to multibyte
> // utf-8 characters, which it can split arbitrarily.

Done.

https://codereview.appspot.com/14197043/diff/14001/environs/sshstorage/storage.go
File environs/sshstorage/storage.go (right):

https://codereview.appspot.com/14197043/diff/14001/environs/sshstorage/storage.go#newcode149
environs/sshstorage/storage.go:149: stdin = bufio.NewWriter(s.stdin)
On 2013/10/01 15:28:47, rog wrote:
> I'm not sure this half-way house is great.
> I'd either make s.stdin a bufio.Writer, or
> call NewWriter inside the later "if input != nil" block,
> avoiding the ugly dynamic type conversion either way,
> and allowing the use of the more natural WriteString
> methods.

It's not in the later block because it's used before then. If it were in
there, then we'd have two writes to s.stdin.

I agree about the ugliness, though. I'll wrap s.stdin, but only in this
function so as not to lose the WriteCloser interface.

https://codereview.appspot.com/14197043/diff/14001/environs/sshstorage/storage.go#newcode154
environs/sshstorage/storage.go:154: // here-document must start on the
outer-most command.
On 2013/10/01 15:28:47, rog wrote:
> That's not actually the case, I think. Did you try it?

I did, but I'm not sure what I was doing before to cause it to fail. I
*think* I may have had a bug where something was being wrapped twice,
and ended up as ((command << EOF)).

Anyway, I've put it after 'base64 -d' and it does indeed work fine.
Thanks.

https://codereview.appspot.com/14197043/

Revision history for this message
Go Bot (go-bot) wrote :

There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'environs/sshstorage/export_test.go'
2--- environs/sshstorage/export_test.go 1970-01-01 00:00:00 +0000
3+++ environs/sshstorage/export_test.go 2013-10-02 05:50:56 +0000
4@@ -0,0 +1,8 @@
5+// Copyright 2012, 2013 Canonical Ltd.
6+// Licensed under the AGPLv3, see LICENCE file for details.
7+
8+package sshstorage
9+
10+var (
11+ NewLineWrapWriter = newLineWrapWriter
12+)
13
14=== added file 'environs/sshstorage/linewrapwriter.go'
15--- environs/sshstorage/linewrapwriter.go 1970-01-01 00:00:00 +0000
16+++ environs/sshstorage/linewrapwriter.go 2013-10-02 05:50:56 +0000
17@@ -0,0 +1,57 @@
18+// Copyright 2013 Canonical Ltd.
19+// Licensed under the AGPLv3, see LICENCE file for details.
20+
21+package sshstorage
22+
23+import (
24+ "fmt"
25+ "io"
26+)
27+
28+type lineWrapWriter struct {
29+ out io.Writer
30+ remain int
31+ max int
32+}
33+
34+// NewLineWrapWriter returns an io.Writer that encloses the given
35+// io.Writer, wrapping lines at the the specified line length.
36+//
37+// Note: there is no special consideration for input that
38+// already contains newlines; this will simply add newlines
39+// after every "lineLength" bytes. Moreover it gives no
40+// consideration to multibyte utf-8 characters, which it can split
41+// arbitrarily.
42+//
43+// This is currently only appropriate for wrapping base64-encoded
44+// data, which is why it lives here.
45+func newLineWrapWriter(out io.Writer, lineLength int) (io.Writer, error) {
46+ if lineLength <= 0 {
47+ return nil, fmt.Errorf("line length %d <= 0", lineLength)
48+ }
49+ return &lineWrapWriter{
50+ out: out,
51+ remain: lineLength,
52+ max: lineLength,
53+ }, nil
54+}
55+
56+func (w *lineWrapWriter) Write(buf []byte) (int, error) {
57+ total := 0
58+ for len(buf) >= w.remain {
59+ n, err := w.out.Write(buf[0:w.remain])
60+ w.remain -= n
61+ total += n
62+ if err != nil {
63+ return total, err
64+ }
65+ if _, err := w.out.Write([]byte("\n")); err != nil {
66+ return total, err
67+ }
68+ w.remain = w.max
69+ buf = buf[n:]
70+ }
71+ n, err := w.out.Write(buf)
72+ w.remain -= n
73+ return total + n, err
74+}
75
76=== added file 'environs/sshstorage/linewrapwriter_test.go'
77--- environs/sshstorage/linewrapwriter_test.go 1970-01-01 00:00:00 +0000
78+++ environs/sshstorage/linewrapwriter_test.go 2013-10-02 05:50:56 +0000
79@@ -0,0 +1,151 @@
80+// Copyright 2013 Canonical Ltd.
81+// Licensed under the AGPLv3, see LICENCE file for details.
82+
83+package sshstorage_test
84+
85+import (
86+ "bytes"
87+ "errors"
88+ "io"
89+
90+ gc "launchpad.net/gocheck"
91+
92+ "launchpad.net/juju-core/environs/sshstorage"
93+)
94+
95+type wrapWriterSuite struct{}
96+
97+var _ = gc.Suite(&wrapWriterSuite{})
98+
99+func (*wrapWriterSuite) TestLineWrapWriterBadLength(c *gc.C) {
100+ var buf bytes.Buffer
101+ w, err := sshstorage.NewLineWrapWriter(&buf, 0)
102+ c.Assert(err, gc.ErrorMatches, "line length 0 <= 0")
103+ c.Assert(w, gc.IsNil)
104+ w, err = sshstorage.NewLineWrapWriter(&buf, -1)
105+ c.Assert(err, gc.ErrorMatches, "line length -1 <= 0")
106+}
107+
108+func (*wrapWriterSuite) TestLineWrapWriter(c *gc.C) {
109+ type test struct {
110+ input []string
111+ lineLength int
112+ expected string
113+ }
114+ tests := []test{{
115+ input: []string{""},
116+ lineLength: 1,
117+ expected: "",
118+ }, {
119+ input: []string{"hi!"},
120+ lineLength: 1,
121+ expected: "h\ni\n!\n",
122+ }, {
123+ input: []string{"hi!"},
124+ lineLength: 2,
125+ // Note: no trailing newline.
126+ expected: "hi\n!",
127+ }, {
128+ input: []string{"", "h", "i!"},
129+ lineLength: 2,
130+ expected: "hi\n!",
131+ }, {
132+ input: []string{"", "h", "i!"},
133+ lineLength: 2,
134+ expected: "hi\n!",
135+ }, {
136+ input: []string{"hi", "!!"},
137+ lineLength: 2,
138+ expected: "hi\n!!\n",
139+ }, {
140+ input: []string{"hi", "!", "!"},
141+ lineLength: 2,
142+ expected: "hi\n!!\n",
143+ }, {
144+ input: []string{"h", "i", "!!"},
145+ lineLength: 2,
146+ expected: "hi\n!!\n",
147+ }}
148+ for i, t := range tests {
149+ c.Logf("test %d: %q, line length %d", i, t.input, t.lineLength)
150+ var buf bytes.Buffer
151+ w, err := sshstorage.NewLineWrapWriter(&buf, t.lineLength)
152+ c.Assert(err, gc.IsNil)
153+ c.Assert(w, gc.NotNil)
154+ for _, input := range t.input {
155+ n, err := w.Write([]byte(input))
156+ c.Assert(err, gc.IsNil)
157+ c.Assert(n, gc.Equals, len(input))
158+ }
159+ c.Assert(buf.String(), gc.Equals, t.expected)
160+ }
161+}
162+
163+type limitedWriter struct {
164+ io.Writer
165+ remaining int
166+}
167+
168+var writeLimited = errors.New("write limited")
169+
170+func (w *limitedWriter) Write(buf []byte) (int, error) {
171+ inputlen := len(buf)
172+ if len(buf) > w.remaining {
173+ buf = buf[:w.remaining]
174+ }
175+ n, err := w.Writer.Write(buf)
176+ w.remaining -= n
177+ if n < inputlen && err == nil {
178+ err = writeLimited
179+ }
180+ return n, err
181+}
182+
183+func (*wrapWriterSuite) TestLineWrapWriterErrors(c *gc.C) {
184+ // Note: after an error is returned, all bets are off.
185+ // In the only place we use this code, we bail out immediately.
186+ const lineLength = 3
187+ type test struct {
188+ input string
189+ output string
190+ limit int
191+ written int
192+ err error
193+ }
194+ tests := []test{{
195+ input: "abc",
196+ output: "abc",
197+ limit: 3, // "\n" will be limited
198+ written: 3,
199+ err: writeLimited,
200+ }, {
201+ input: "abc",
202+ output: "abc\n",
203+ limit: 4,
204+ written: 3, // 3/3 bytes of input
205+ }, {
206+ input: "abc",
207+ output: "ab",
208+ limit: 2,
209+ written: 2, // 2/3 bytes of input
210+ err: writeLimited,
211+ }, {
212+ input: "abc!",
213+ output: "abc\n",
214+ limit: 4,
215+ written: 3, // 3/4 bytes of input
216+ err: writeLimited,
217+ }}
218+ for i, t := range tests {
219+ c.Logf("test %d: %q, limit %d", i, t.input, t.limit)
220+ var buf bytes.Buffer
221+ wrapWriter := &limitedWriter{&buf, t.limit}
222+ w, err := sshstorage.NewLineWrapWriter(wrapWriter, lineLength)
223+ c.Assert(err, gc.IsNil)
224+ c.Assert(w, gc.NotNil)
225+ n, err := w.Write([]byte(t.input))
226+ c.Assert(n, gc.Equals, t.written)
227+ c.Assert(buf.String(), gc.Equals, t.output)
228+ c.Assert(err, gc.Equals, t.err)
229+ }
230+}
231
232=== modified file 'environs/sshstorage/storage.go'
233--- environs/sshstorage/storage.go 2013-09-27 05:19:30 +0000
234+++ environs/sshstorage/storage.go 2013-10-02 05:50:56 +0000
235@@ -22,6 +22,10 @@
236 "launchpad.net/juju-core/utils"
237 )
238
239+// base64LineLength is the default line length for wrapping
240+// output generated by the base64 command line utility.
241+const base64LineLength = 76
242+
243 // SSHStorage implements storage.Storage.
244 //
245 // The storage is created under sudo, and ownership given over to the
246@@ -129,10 +133,10 @@
247
248 func (s *SSHStorage) runf(flockmode flockmode, command string, args ...interface{}) (string, error) {
249 command = fmt.Sprintf(command, args...)
250- return s.run(flockmode, command, nil)
251+ return s.run(flockmode, command, nil, 0)
252 }
253
254-func (s *SSHStorage) run(flockmode flockmode, command string, input []byte) (string, error) {
255+func (s *SSHStorage) run(flockmode flockmode, command string, input io.Reader, inputlen int64) (string, error) {
256 const rcPrefix = "JUJU-RC: "
257 command = fmt.Sprintf(
258 "SHELL=/bin/bash flock %s %s -c %s",
259@@ -140,23 +144,32 @@
260 s.remotepath,
261 utils.ShQuote(command),
262 )
263- var encoded string
264+ stdin := bufio.NewWriter(s.stdin)
265 if input != nil {
266- encoded = base64.StdEncoding.EncodeToString(input)
267- command = fmt.Sprintf(
268- "head -q -c %d | base64 -d | (%s)",
269- len(encoded),
270- command,
271- )
272+ command = fmt.Sprintf("base64 -d << '@EOF' | (%s)", command)
273 }
274 command = fmt.Sprintf("(%s) 2>&1; echo %s$?", command, rcPrefix)
275- if _, err := s.stdin.Write([]byte(command + "\n")); err != nil {
276+ if _, err := stdin.WriteString(command + "\n"); err != nil {
277 return "", fmt.Errorf("failed to write command: %v", err)
278 }
279 if input != nil {
280- if _, err := s.stdin.Write([]byte(encoded)); err != nil {
281+ wrapper, err := newLineWrapWriter(stdin, base64LineLength)
282+ if err != nil {
283+ return "", fmt.Errorf("failed to create split writer: %v", err)
284+ }
285+ encoder := base64.NewEncoder(base64.StdEncoding, wrapper)
286+ if _, err := io.CopyN(encoder, input, inputlen); err != nil {
287 return "", fmt.Errorf("failed to write input: %v", err)
288 }
289+ if err := encoder.Close(); err != nil {
290+ return "", fmt.Errorf("failed to flush encoder: %v", err)
291+ }
292+ if _, err := stdin.WriteString("\n@EOF\n"); err != nil {
293+ return "", fmt.Errorf("failed to terminate input: %v", err)
294+ }
295+ }
296+ if err := stdin.Flush(); err != nil {
297+ return "", fmt.Errorf("failed to flush input: %v", err)
298 }
299 var output []string
300 for s.scanner.Scan() {
301@@ -259,21 +272,17 @@
302 if err != nil {
303 return err
304 }
305- buf := make([]byte, length)
306- if _, err := io.ReadFull(r, buf); err != nil {
307- return err
308- }
309 path = utils.ShQuote(path)
310 tmpdir := utils.ShQuote(s.tmpdir)
311
312 // Write to a temporary file ($TMPFILE), then mv atomically.
313 command := fmt.Sprintf("mkdir -p `dirname %s` && cat > $TMPFILE", path)
314 command = fmt.Sprintf(
315- "export TMPDIR=%s && TMPFILE=`mktemp` && ((%s && mv $TMPFILE %s) || rm -f $TMPFILE)",
316+ "TMPFILE=`mktemp --tmpdir=%s` && ((%s && mv $TMPFILE %s) || rm -f $TMPFILE)",
317 tmpdir, command, path,
318 )
319
320- _, err = s.run(flockExclusive, command+"\n", buf)
321+ _, err = s.run(flockExclusive, command+"\n", r, length)
322 return err
323 }
324
325
326=== modified file 'environs/sshstorage/storage_test.go'
327--- environs/sshstorage/storage_test.go 2013-09-23 03:07:31 +0000
328+++ environs/sshstorage/storage_test.go 2013-10-02 05:50:56 +0000
329@@ -13,7 +13,6 @@
330 "path"
331 "path/filepath"
332 "regexp"
333- stdtesting "testing"
334 "time"
335
336 gc "launchpad.net/gocheck"
337@@ -31,10 +30,6 @@
338
339 var _ = gc.Suite(&storageSuite{})
340
341-func Test(t *stdtesting.T) {
342- gc.TestingT(t)
343-}
344-
345 func sshCommandTesting(host string, tty bool, command string) *exec.Cmd {
346 cmd := exec.Command("bash", "-c", command)
347 uid := fmt.Sprint(os.Getuid())
348
349=== added file 'environs/sshstorage/suite_test.go'
350--- environs/sshstorage/suite_test.go 1970-01-01 00:00:00 +0000
351+++ environs/sshstorage/suite_test.go 2013-10-02 05:50:56 +0000
352@@ -0,0 +1,14 @@
353+// Copyright 2013 Canonical Ltd.
354+// Licensed under the AGPLv3, see LICENCE file for details.
355+
356+package sshstorage_test
357+
358+import (
359+ "testing"
360+
361+ gc "launchpad.net/gocheck"
362+)
363+
364+func Test(t *testing.T) {
365+ gc.TestingT(t)
366+}

Subscribers

People subscribed via source and target branches

to status/vote changes: