github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/distribution/xfer/download_test.go (about) 1 package xfer 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "runtime" 10 "sync/atomic" 11 "testing" 12 "time" 13 14 "github.com/docker/distribution" 15 "github.com/docker/docker/image" 16 "github.com/docker/docker/layer" 17 "github.com/docker/docker/pkg/progress" 18 "github.com/opencontainers/go-digest" 19 "golang.org/x/net/context" 20 ) 21 22 const maxDownloadConcurrency = 3 23 24 type mockLayer struct { 25 layerData bytes.Buffer 26 diffID layer.DiffID 27 chainID layer.ChainID 28 parent layer.Layer 29 os layer.OS 30 } 31 32 func (ml *mockLayer) TarStream() (io.ReadCloser, error) { 33 return ioutil.NopCloser(bytes.NewBuffer(ml.layerData.Bytes())), nil 34 } 35 36 func (ml *mockLayer) TarStreamFrom(layer.ChainID) (io.ReadCloser, error) { 37 return nil, fmt.Errorf("not implemented") 38 } 39 40 func (ml *mockLayer) ChainID() layer.ChainID { 41 return ml.chainID 42 } 43 44 func (ml *mockLayer) DiffID() layer.DiffID { 45 return ml.diffID 46 } 47 48 func (ml *mockLayer) Parent() layer.Layer { 49 return ml.parent 50 } 51 52 func (ml *mockLayer) Size() (size int64, err error) { 53 return 0, nil 54 } 55 56 func (ml *mockLayer) DiffSize() (size int64, err error) { 57 return 0, nil 58 } 59 60 func (ml *mockLayer) OS() layer.OS { 61 return ml.os 62 } 63 64 func (ml *mockLayer) Metadata() (map[string]string, error) { 65 return make(map[string]string), nil 66 } 67 68 type mockLayerStore struct { 69 layers map[layer.ChainID]*mockLayer 70 } 71 72 func createChainIDFromParent(parent layer.ChainID, dgsts ...layer.DiffID) layer.ChainID { 73 if len(dgsts) == 0 { 74 return parent 75 } 76 if parent == "" { 77 return createChainIDFromParent(layer.ChainID(dgsts[0]), dgsts[1:]...) 78 } 79 // H = "H(n-1) SHA256(n)" 80 dgst := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0]))) 81 return createChainIDFromParent(layer.ChainID(dgst), dgsts[1:]...) 82 } 83 84 func (ls *mockLayerStore) Map() map[layer.ChainID]layer.Layer { 85 layers := map[layer.ChainID]layer.Layer{} 86 87 for k, v := range ls.layers { 88 layers[k] = v 89 } 90 91 return layers 92 } 93 94 func (ls *mockLayerStore) Register(reader io.Reader, parentID layer.ChainID, os layer.OS) (layer.Layer, error) { 95 return ls.RegisterWithDescriptor(reader, parentID, distribution.Descriptor{}) 96 } 97 98 func (ls *mockLayerStore) RegisterWithDescriptor(reader io.Reader, parentID layer.ChainID, _ distribution.Descriptor) (layer.Layer, error) { 99 var ( 100 parent layer.Layer 101 err error 102 ) 103 104 if parentID != "" { 105 parent, err = ls.Get(parentID) 106 if err != nil { 107 return nil, err 108 } 109 } 110 111 l := &mockLayer{parent: parent} 112 _, err = l.layerData.ReadFrom(reader) 113 if err != nil { 114 return nil, err 115 } 116 l.diffID = layer.DiffID(digest.FromBytes(l.layerData.Bytes())) 117 l.chainID = createChainIDFromParent(parentID, l.diffID) 118 119 ls.layers[l.chainID] = l 120 return l, nil 121 } 122 123 func (ls *mockLayerStore) Get(chainID layer.ChainID) (layer.Layer, error) { 124 l, ok := ls.layers[chainID] 125 if !ok { 126 return nil, layer.ErrLayerDoesNotExist 127 } 128 return l, nil 129 } 130 131 func (ls *mockLayerStore) Release(l layer.Layer) ([]layer.Metadata, error) { 132 return []layer.Metadata{}, nil 133 } 134 func (ls *mockLayerStore) CreateRWLayer(string, layer.ChainID, *layer.CreateRWLayerOpts) (layer.RWLayer, error) { 135 return nil, errors.New("not implemented") 136 } 137 138 func (ls *mockLayerStore) GetRWLayer(string) (layer.RWLayer, error) { 139 return nil, errors.New("not implemented") 140 } 141 142 func (ls *mockLayerStore) ReleaseRWLayer(layer.RWLayer) ([]layer.Metadata, error) { 143 return nil, errors.New("not implemented") 144 } 145 func (ls *mockLayerStore) GetMountID(string) (string, error) { 146 return "", errors.New("not implemented") 147 } 148 149 func (ls *mockLayerStore) Cleanup() error { 150 return nil 151 } 152 153 func (ls *mockLayerStore) DriverStatus() [][2]string { 154 return [][2]string{} 155 } 156 157 func (ls *mockLayerStore) DriverName() string { 158 return "mock" 159 } 160 161 type mockDownloadDescriptor struct { 162 currentDownloads *int32 163 id string 164 diffID layer.DiffID 165 registeredDiffID layer.DiffID 166 expectedDiffID layer.DiffID 167 simulateRetries int 168 } 169 170 // Key returns the key used to deduplicate downloads. 171 func (d *mockDownloadDescriptor) Key() string { 172 return d.id 173 } 174 175 // ID returns the ID for display purposes. 176 func (d *mockDownloadDescriptor) ID() string { 177 return d.id 178 } 179 180 // DiffID should return the DiffID for this layer, or an error 181 // if it is unknown (for example, if it has not been downloaded 182 // before). 183 func (d *mockDownloadDescriptor) DiffID() (layer.DiffID, error) { 184 if d.diffID != "" { 185 return d.diffID, nil 186 } 187 return "", errors.New("no diffID available") 188 } 189 190 func (d *mockDownloadDescriptor) Registered(diffID layer.DiffID) { 191 d.registeredDiffID = diffID 192 } 193 194 func (d *mockDownloadDescriptor) mockTarStream() io.ReadCloser { 195 // The mock implementation returns the ID repeated 5 times as a tar 196 // stream instead of actual tar data. The data is ignored except for 197 // computing IDs. 198 return ioutil.NopCloser(bytes.NewBuffer([]byte(d.id + d.id + d.id + d.id + d.id))) 199 } 200 201 // Download is called to perform the download. 202 func (d *mockDownloadDescriptor) Download(ctx context.Context, progressOutput progress.Output) (io.ReadCloser, int64, error) { 203 if d.currentDownloads != nil { 204 defer atomic.AddInt32(d.currentDownloads, -1) 205 206 if atomic.AddInt32(d.currentDownloads, 1) > maxDownloadConcurrency { 207 return nil, 0, errors.New("concurrency limit exceeded") 208 } 209 } 210 211 // Sleep a bit to simulate a time-consuming download. 212 for i := int64(0); i <= 10; i++ { 213 select { 214 case <-ctx.Done(): 215 return nil, 0, ctx.Err() 216 case <-time.After(10 * time.Millisecond): 217 progressOutput.WriteProgress(progress.Progress{ID: d.ID(), Action: "Downloading", Current: i, Total: 10}) 218 } 219 } 220 221 if d.simulateRetries != 0 { 222 d.simulateRetries-- 223 return nil, 0, errors.New("simulating retry") 224 } 225 226 return d.mockTarStream(), 0, nil 227 } 228 229 func (d *mockDownloadDescriptor) Close() { 230 } 231 232 func downloadDescriptors(currentDownloads *int32) []DownloadDescriptor { 233 return []DownloadDescriptor{ 234 &mockDownloadDescriptor{ 235 currentDownloads: currentDownloads, 236 id: "id1", 237 expectedDiffID: layer.DiffID("sha256:68e2c75dc5c78ea9240689c60d7599766c213ae210434c53af18470ae8c53ec1"), 238 }, 239 &mockDownloadDescriptor{ 240 currentDownloads: currentDownloads, 241 id: "id2", 242 expectedDiffID: layer.DiffID("sha256:64a636223116aa837973a5d9c2bdd17d9b204e4f95ac423e20e65dfbb3655473"), 243 }, 244 &mockDownloadDescriptor{ 245 currentDownloads: currentDownloads, 246 id: "id3", 247 expectedDiffID: layer.DiffID("sha256:58745a8bbd669c25213e9de578c4da5c8ee1c836b3581432c2b50e38a6753300"), 248 }, 249 &mockDownloadDescriptor{ 250 currentDownloads: currentDownloads, 251 id: "id2", 252 expectedDiffID: layer.DiffID("sha256:64a636223116aa837973a5d9c2bdd17d9b204e4f95ac423e20e65dfbb3655473"), 253 }, 254 &mockDownloadDescriptor{ 255 currentDownloads: currentDownloads, 256 id: "id4", 257 expectedDiffID: layer.DiffID("sha256:0dfb5b9577716cc173e95af7c10289322c29a6453a1718addc00c0c5b1330936"), 258 simulateRetries: 1, 259 }, 260 &mockDownloadDescriptor{ 261 currentDownloads: currentDownloads, 262 id: "id5", 263 expectedDiffID: layer.DiffID("sha256:0a5f25fa1acbc647f6112a6276735d0fa01e4ee2aa7ec33015e337350e1ea23d"), 264 }, 265 } 266 } 267 268 func TestSuccessfulDownload(t *testing.T) { 269 // TODO Windows: Fix this unit text 270 if runtime.GOOS == "windows" { 271 t.Skip("Needs fixing on Windows") 272 } 273 274 layerStore := &mockLayerStore{make(map[layer.ChainID]*mockLayer)} 275 lsMap := make(map[string]layer.Store) 276 lsMap[runtime.GOOS] = layerStore 277 ldm := NewLayerDownloadManager(lsMap, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond }) 278 279 progressChan := make(chan progress.Progress) 280 progressDone := make(chan struct{}) 281 receivedProgress := make(map[string]progress.Progress) 282 283 go func() { 284 for p := range progressChan { 285 receivedProgress[p.ID] = p 286 } 287 close(progressDone) 288 }() 289 290 var currentDownloads int32 291 descriptors := downloadDescriptors(¤tDownloads) 292 293 firstDescriptor := descriptors[0].(*mockDownloadDescriptor) 294 295 // Pre-register the first layer to simulate an already-existing layer 296 l, err := layerStore.Register(firstDescriptor.mockTarStream(), "", layer.OS(runtime.GOOS)) 297 if err != nil { 298 t.Fatal(err) 299 } 300 firstDescriptor.diffID = l.DiffID() 301 302 rootFS, releaseFunc, err := ldm.Download(context.Background(), *image.NewRootFS(), layer.OS(runtime.GOOS), descriptors, progress.ChanOutput(progressChan)) 303 if err != nil { 304 t.Fatalf("download error: %v", err) 305 } 306 307 releaseFunc() 308 309 close(progressChan) 310 <-progressDone 311 312 if len(rootFS.DiffIDs) != len(descriptors) { 313 t.Fatal("got wrong number of diffIDs in rootfs") 314 } 315 316 for i, d := range descriptors { 317 descriptor := d.(*mockDownloadDescriptor) 318 319 if descriptor.diffID != "" { 320 if receivedProgress[d.ID()].Action != "Already exists" { 321 t.Fatalf("did not get 'Already exists' message for %v", d.ID()) 322 } 323 } else if receivedProgress[d.ID()].Action != "Pull complete" { 324 t.Fatalf("did not get 'Pull complete' message for %v", d.ID()) 325 } 326 327 if rootFS.DiffIDs[i] != descriptor.expectedDiffID { 328 t.Fatalf("rootFS item %d has the wrong diffID (expected: %v got: %v)", i, descriptor.expectedDiffID, rootFS.DiffIDs[i]) 329 } 330 331 if descriptor.diffID == "" && descriptor.registeredDiffID != rootFS.DiffIDs[i] { 332 t.Fatal("diffID mismatch between rootFS and Registered callback") 333 } 334 } 335 } 336 337 func TestCancelledDownload(t *testing.T) { 338 layerStore := &mockLayerStore{make(map[layer.ChainID]*mockLayer)} 339 lsMap := make(map[string]layer.Store) 340 lsMap[runtime.GOOS] = layerStore 341 ldm := NewLayerDownloadManager(lsMap, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond }) 342 343 progressChan := make(chan progress.Progress) 344 progressDone := make(chan struct{}) 345 346 go func() { 347 for range progressChan { 348 } 349 close(progressDone) 350 }() 351 352 ctx, cancel := context.WithCancel(context.Background()) 353 354 go func() { 355 <-time.After(time.Millisecond) 356 cancel() 357 }() 358 359 descriptors := downloadDescriptors(nil) 360 _, _, err := ldm.Download(ctx, *image.NewRootFS(), layer.OS(runtime.GOOS), descriptors, progress.ChanOutput(progressChan)) 361 if err != context.Canceled { 362 t.Fatal("expected download to be cancelled") 363 } 364 365 close(progressChan) 366 <-progressDone 367 }