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