~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/tools/lxdclient/client_image_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2016 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
// +build go1.3
 
5
 
 
6
package lxdclient
 
7
 
 
8
import (
 
9
        "fmt"
 
10
        "time"
 
11
 
 
12
        "github.com/juju/errors"
 
13
        "github.com/juju/testing"
 
14
        jc "github.com/juju/testing/checkers"
 
15
        gc "gopkg.in/check.v1"
 
16
 
 
17
        coretesting "github.com/juju/juju/testing"
 
18
)
 
19
 
 
20
type imageSuite struct {
 
21
        testing.IsolationSuite
 
22
        Stub              *testing.Stub
 
23
        remoteWithTrusty  *stubRemoteClient
 
24
        remoteWithNothing *stubRemoteClient
 
25
}
 
26
 
 
27
var _ = gc.Suite(&imageSuite{})
 
28
 
 
29
func (s *imageSuite) SetUpTest(c *gc.C) {
 
30
        s.IsolationSuite.SetUpTest(c)
 
31
        s.Stub = &testing.Stub{}
 
32
        s.remoteWithTrusty = &stubRemoteClient{
 
33
                stub: s.Stub,
 
34
                url:  "https://match",
 
35
                aliases: map[string]string{
 
36
                        "trusty": "deadbeef",
 
37
                },
 
38
        }
 
39
        s.remoteWithNothing = &stubRemoteClient{
 
40
                stub:    s.Stub,
 
41
                url:     "https://missing",
 
42
                aliases: nil,
 
43
        }
 
44
}
 
45
 
 
46
type stubRemoteClient struct {
 
47
        stub    *testing.Stub
 
48
        url     string
 
49
        aliases map[string]string
 
50
}
 
51
 
 
52
var _ remoteClient = (*stubRemoteClient)(nil)
 
53
 
 
54
func (s *stubRemoteClient) URL() string {
 
55
        // Note we don't log calls to URL because they are not interesting, and
 
56
        // are generally just used for logging, etc.
 
57
        return s.url
 
58
}
 
59
 
 
60
func (s *stubRemoteClient) GetAlias(alias string) string {
 
61
        s.stub.AddCall("GetAlias", alias)
 
62
        if err := s.stub.NextErr(); err != nil {
 
63
                // GetAlias can't return an Err, but if we get an error, we'll
 
64
                // just treat that as a miss on the Alias lookup.
 
65
                return ""
 
66
        }
 
67
        return s.aliases[alias]
 
68
}
 
69
 
 
70
func (s *stubRemoteClient) CopyImage(imageTarget string, dest rawImageClient, aliases []string, callback func(string)) error {
 
71
        // We don't include the destination or the callback because they aren't
 
72
        // objects we can easily assert against.
 
73
        s.stub.AddCall("CopyImage", imageTarget, aliases)
 
74
        if err := s.stub.NextErr(); err != nil {
 
75
                return err
 
76
        }
 
77
        // This is to make this CopyImage act a bit like a real CopyImage. it
 
78
        // gives some Progress callbacks, and then sets the alias in the
 
79
        // target.
 
80
        if callback != nil {
 
81
                // The real one gives progress every 1%
 
82
                for i := 10; i <= 100; i += 10 {
 
83
                        callback(fmt.Sprintf("%d%%", i))
 
84
                        time.Sleep(1 * time.Microsecond)
 
85
                }
 
86
        }
 
87
        if stubDest, ok := dest.(*stubClient); ok {
 
88
                if stubDest.Aliases == nil {
 
89
                        stubDest.Aliases = make(map[string]string)
 
90
                }
 
91
                for _, alias := range aliases {
 
92
                        stubDest.Aliases[alias] = imageTarget
 
93
                }
 
94
        }
 
95
        return nil
 
96
}
 
97
 
 
98
func (s *stubRemoteClient) AsRemote() Remote {
 
99
        return Remote{
 
100
                Host:     s.url,
 
101
                Protocol: SimplestreamsProtocol,
 
102
        }
 
103
}
 
104
 
 
105
type stubConnector struct {
 
106
        stub          *testing.Stub
 
107
        remoteClients map[string]remoteClient
 
108
}
 
109
 
 
110
func MakeConnector(stub *testing.Stub, remotes ...remoteClient) *stubConnector {
 
111
        remoteMap := make(map[string]remoteClient)
 
112
        for _, remote := range remotes {
 
113
                remoteMap[remote.URL()] = remote
 
114
        }
 
115
        return &stubConnector{
 
116
                stub:          stub,
 
117
                remoteClients: remoteMap,
 
118
        }
 
119
}
 
120
 
 
121
func (s *stubConnector) connectToSource(remote Remote) (remoteClient, error) {
 
122
        s.stub.AddCall("connectToSource", remote.Host)
 
123
        if err := s.stub.NextErr(); err != nil {
 
124
                return nil, err
 
125
        }
 
126
        return s.remoteClients[remote.Host], nil
 
127
}
 
128
 
 
129
func (s *imageSuite) TestEnsureImageExistsAlreadyPresent(c *gc.C) {
 
130
        raw := &stubClient{
 
131
                stub: s.Stub,
 
132
                Aliases: map[string]string{
 
133
                        "ubuntu-trusty": "dead-beef",
 
134
                },
 
135
        }
 
136
        client := &imageClient{
 
137
                raw: raw,
 
138
        }
 
139
        err := client.EnsureImageExists("trusty", nil, nil)
 
140
        c.Assert(err, jc.ErrorIsNil)
 
141
        s.Stub.CheckCall(c, 0, "GetAlias", "ubuntu-trusty")
 
142
}
 
143
 
 
144
func (s *imageSuite) TestEnsureImageExistsFirstRemote(c *gc.C) {
 
145
        connector := MakeConnector(s.Stub, s.remoteWithTrusty)
 
146
        raw := &stubClient{
 
147
                stub: s.Stub,
 
148
                // We don't have the image locally
 
149
                Aliases: nil,
 
150
        }
 
151
        client := &imageClient{
 
152
                raw:             raw,
 
153
                connectToSource: connector.connectToSource,
 
154
        }
 
155
        remotes := []Remote{s.remoteWithTrusty.AsRemote()}
 
156
        s.Stub.ResetCalls()
 
157
        err := client.EnsureImageExists("trusty", remotes, nil)
 
158
        c.Assert(err, jc.ErrorIsNil)
 
159
        // We didn't find it locally
 
160
        s.Stub.CheckCalls(c, []testing.StubCall{
 
161
                { // Check if we already have 'ubuntu-trusty' locally
 
162
                        FuncName: "GetAlias",
 
163
                        Args:     []interface{}{"ubuntu-trusty"},
 
164
                },
 
165
                { // We didn't so connect to the first remote
 
166
                        FuncName: "connectToSource",
 
167
                        Args:     []interface{}{"https://match"},
 
168
                },
 
169
                { // And check if it has trusty (which it should)
 
170
                        FuncName: "GetAlias",
 
171
                        Args:     []interface{}{"trusty"},
 
172
                },
 
173
                { // So Copy the Image
 
174
                        FuncName: "CopyImage",
 
175
                        Args:     []interface{}{"deadbeef", []string{"ubuntu-trusty"}},
 
176
                },
 
177
        })
 
178
        // We've updated the aliases
 
179
        c.Assert(raw.Aliases, gc.DeepEquals, map[string]string{
 
180
                "ubuntu-trusty": "deadbeef",
 
181
        })
 
182
}
 
183
 
 
184
func (s *imageSuite) TestEnsureImageExistsUnableToConnect(c *gc.C) {
 
185
        connector := MakeConnector(s.Stub, s.remoteWithTrusty)
 
186
        raw := &stubClient{
 
187
                stub: s.Stub,
 
188
                // We don't have the image locally
 
189
                Aliases: nil,
 
190
        }
 
191
        client := &imageClient{
 
192
                raw:             raw,
 
193
                connectToSource: connector.connectToSource,
 
194
        }
 
195
        badRemote := Remote{
 
196
                Host:     "https://nosuch-remote.invalid",
 
197
                Protocol: SimplestreamsProtocol,
 
198
        }
 
199
        s.Stub.ResetCalls()
 
200
        s.Stub.SetErrors(nil, errors.Errorf("unable-to-connect"))
 
201
        remotes := []Remote{badRemote, s.remoteWithTrusty.AsRemote()}
 
202
        err := client.EnsureImageExists("trusty", remotes, nil)
 
203
        c.Assert(err, jc.ErrorIsNil)
 
204
        // We didn't find it locally
 
205
        s.Stub.CheckCalls(c, []testing.StubCall{
 
206
                { // Check if we already have 'ubuntu-trusty' locally
 
207
                        FuncName: "GetAlias",
 
208
                        Args:     []interface{}{"ubuntu-trusty"},
 
209
                },
 
210
                { // We didn't so connect to the first remote
 
211
                        FuncName: "connectToSource",
 
212
                        Args:     []interface{}{"https://nosuch-remote.invalid"},
 
213
                },
 
214
                { // Connect failed to first, so connect to second and copy
 
215
                        FuncName: "connectToSource",
 
216
                        Args:     []interface{}{"https://match"},
 
217
                },
 
218
                { // And check if it has trusty (which it should)
 
219
                        FuncName: "GetAlias",
 
220
                        Args:     []interface{}{"trusty"},
 
221
                },
 
222
                { // So Copy the Image
 
223
                        FuncName: "CopyImage",
 
224
                        Args:     []interface{}{"deadbeef", []string{"ubuntu-trusty"}},
 
225
                },
 
226
        })
 
227
        // We've updated the aliases
 
228
        c.Assert(raw.Aliases, gc.DeepEquals, map[string]string{
 
229
                "ubuntu-trusty": "deadbeef",
 
230
        })
 
231
}
 
232
 
 
233
func (s *imageSuite) TestEnsureImageExistsNotPresentInFirstRemote(c *gc.C) {
 
234
        connector := MakeConnector(s.Stub, s.remoteWithNothing, s.remoteWithTrusty)
 
235
        raw := &stubClient{
 
236
                stub: s.Stub,
 
237
                // We don't have the image locally
 
238
                Aliases: nil,
 
239
        }
 
240
        client := &imageClient{
 
241
                raw:             raw,
 
242
                connectToSource: connector.connectToSource,
 
243
        }
 
244
        s.Stub.ResetCalls()
 
245
        remotes := []Remote{s.remoteWithNothing.AsRemote(), s.remoteWithTrusty.AsRemote()}
 
246
        err := client.EnsureImageExists("trusty", remotes, nil)
 
247
        c.Assert(err, jc.ErrorIsNil)
 
248
        // We didn't find it locally
 
249
        s.Stub.CheckCalls(c, []testing.StubCall{
 
250
                { // Check if we already have 'ubuntu-trusty' locally
 
251
                        FuncName: "GetAlias",
 
252
                        Args:     []interface{}{"ubuntu-trusty"},
 
253
                },
 
254
                { // We didn't so connect to the first remote
 
255
                        FuncName: "connectToSource",
 
256
                        Args:     []interface{}{s.remoteWithNothing.URL()},
 
257
                },
 
258
                { // Lookup the Alias
 
259
                        FuncName: "GetAlias",
 
260
                        Args:     []interface{}{"trusty"},
 
261
                },
 
262
                { // It wasn't found, so connect to second and look there
 
263
                        FuncName: "connectToSource",
 
264
                        Args:     []interface{}{s.remoteWithTrusty.URL()},
 
265
                },
 
266
                { // And check if it has trusty (which it should)
 
267
                        FuncName: "GetAlias",
 
268
                        Args:     []interface{}{"trusty"},
 
269
                },
 
270
                { // So Copy the Image
 
271
                        FuncName: "CopyImage",
 
272
                        Args:     []interface{}{"deadbeef", []string{"ubuntu-trusty"}},
 
273
                },
 
274
        })
 
275
        // We've updated the aliases
 
276
        c.Assert(raw.Aliases, gc.DeepEquals, map[string]string{
 
277
                "ubuntu-trusty": "deadbeef",
 
278
        })
 
279
}
 
280
 
 
281
func (s *imageSuite) TestEnsureImageExistsCallbackIncludesSourceURL(c *gc.C) {
 
282
        calls := make(chan string, 1)
 
283
        callback := func(message string) {
 
284
                select {
 
285
                case calls <- message:
 
286
                default:
 
287
                }
 
288
        }
 
289
        connector := MakeConnector(s.Stub, s.remoteWithTrusty)
 
290
        raw := &stubClient{
 
291
                stub: s.Stub,
 
292
                // We don't have the image locally
 
293
                Aliases: nil,
 
294
        }
 
295
        client := &imageClient{
 
296
                raw:             raw,
 
297
                connectToSource: connector.connectToSource,
 
298
        }
 
299
        remotes := []Remote{s.remoteWithTrusty.AsRemote()}
 
300
        err := client.EnsureImageExists("trusty", remotes, callback)
 
301
        c.Assert(err, jc.ErrorIsNil)
 
302
        select {
 
303
        case message := <-calls:
 
304
                c.Check(message, gc.Matches, "copying image for ubuntu-trusty from https://match: \\d+%")
 
305
        case <-time.After(coretesting.LongWait):
 
306
                // The callbacks are made asynchronously, and so may not
 
307
                // have happened by the time EnsureImageExists exits.
 
308
                c.Fatalf("no messages received")
 
309
        }
 
310
}