Browse code

Merged branch 79-rmi_regexp-feature

Solomon Hykes authored on 2013/03/16 06:08:02
Showing 7 changed files
... ...
@@ -4,3 +4,5 @@ docker/docker
4 4
 a.out
5 5
 *.orig
6 6
 build_src
7
+command-line-arguments.test
8
+.flymake*
... ...
@@ -54,7 +54,7 @@ func (srv *Server) Help() string {
54 54
 		{"reset", "Reset changes to a container's filesystem"},
55 55
 		{"restart", "Restart a running container"},
56 56
 		{"rm", "Remove a container"},
57
-		{"rmimage", "Remove an image"},
57
+		{"rmi", "Remove an image"},
58 58
 		{"run", "Run a command in a new container"},
59 59
 		{"start", "Start a stopped container"},
60 60
 		{"stop", "Stop a running container"},
... ...
@@ -356,29 +356,27 @@ func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string
356 356
 }
357 357
 
358 358
 // 'docker rmi NAME' removes all images with the name NAME
359
-func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
359
+func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) (err error) {
360 360
 	cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image")
361 361
 	fl_all := cmd.Bool("a", false, "Use IMAGE as a path and remove ALL images in this path")
362
-	if err := cmd.Parse(args); err != nil {
363
-		cmd.Usage()
364
-		return nil
365
-	}
366
-	if cmd.NArg() < 1 {
362
+	fl_regexp := cmd.Bool("r", false, "Use IMAGE as a regular expression instead of an exact name")
363
+	if cmd.Parse(args) != nil || cmd.NArg() < 1 {
367 364
 		cmd.Usage()
368 365
 		return nil
369 366
 	}
370 367
 	for _, name := range cmd.Args() {
371
-		var err error
372
-		if *fl_all {
368
+		if *fl_regexp {
369
+			err = srv.images.RemoveRegexp(name)
370
+		} else if *fl_all {
373 371
 			err = srv.images.RemoveInPath(name)
374 372
 		} else {
375
-			image, err := srv.images.Get(name)
376
-			if err != nil {
377
-				return err
378
-			} else if image == nil {
379
-				return errors.New("No such image: " + name)
373
+			if image, err1 := srv.images.Find(name); err1 != nil {
374
+				err = err1
375
+			} else if err1 == nil && image == nil {
376
+				err = fmt.Errorf("No such image: %s", name)
377
+			} else {
378
+				err = srv.images.Remove(image)
380 379
 			}
381
-			err = srv.images.Remove(image)
382 380
 		}
383 381
 		if err != nil {
384 382
 			return err
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"io/ioutil"
7 7
 	"os"
8 8
 	"os/exec"
9
+	"os/user"
9 10
 	"testing"
10 11
 )
11 12
 
... ...
@@ -42,6 +43,12 @@ func init() {
42 42
 		return
43 43
 	}
44 44
 
45
+	if usr, err := user.Current(); err != nil {
46
+		panic(err)
47
+	} else if usr.Uid != "0" {
48
+		panic("docker tests needs to be run as root")
49
+	}
50
+
45 51
 	// Create a temp directory
46 52
 	root, err := ioutil.TempDir("", "docker-test")
47 53
 	if err != nil {
48 54
new file mode 100644
... ...
@@ -0,0 +1,223 @@
0
+package fs
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/fake"
5
+	"testing"
6
+)
7
+
8
+func countImages(store *Store) int {
9
+	paths, err := store.Images()
10
+	if err != nil {
11
+		panic(err)
12
+	}
13
+	return len(paths)
14
+}
15
+
16
+func TestRemoveInPath(t *testing.T) {
17
+	store, err := TempStore("test-remove-in-path")
18
+	if err != nil {
19
+		t.Fatal(err)
20
+	}
21
+	defer nuke(store)
22
+	archive, err := fake.FakeTar()
23
+	if err != nil {
24
+		t.Fatal(err)
25
+	}
26
+	if c := countImages(store); c != 0 {
27
+		t.Fatalf("Expected 0 images, %d found", c)
28
+	}
29
+
30
+	// Test 10 create / Delete all
31
+	for i := 0; i < 10; i++ {
32
+		if _, err := store.Create(archive, nil, "foo", "Testing"); err != nil {
33
+			t.Fatal(err)
34
+		}
35
+	}
36
+	if c := countImages(store); c != 10 {
37
+		t.Fatalf("Expected 10 images, %d found", c)
38
+	}
39
+	if err := store.RemoveInPath("foo"); err != nil {
40
+		t.Fatal(err)
41
+	}
42
+	if c := countImages(store); c != 0 {
43
+		t.Fatalf("Expected 0 images, %d found", c)
44
+	}
45
+
46
+	// Test 10 create / Delete 1
47
+	for i := 0; i < 10; i++ {
48
+		if _, err := store.Create(archive, nil, fmt.Sprintf("foo-%d", i), "Testing"); err != nil {
49
+			t.Fatal(err)
50
+		}
51
+	}
52
+	if c := countImages(store); c != 10 {
53
+		t.Fatalf("Expected 10 images, %d found", c)
54
+	}
55
+	if err := store.RemoveInPath("foo-0"); err != nil {
56
+		t.Fatal(err)
57
+	}
58
+	if c := countImages(store); c != 9 {
59
+		t.Fatalf("Expected 9 images, %d found", c)
60
+	}
61
+
62
+	// Delete failure
63
+	if err := store.RemoveInPath("Not_Foo"); err != nil {
64
+		t.Fatal(err)
65
+	}
66
+	if c := countImages(store); c != 9 {
67
+		t.Fatalf("Expected 9 images, %d found", c)
68
+	}
69
+}
70
+
71
+func TestRemove(t *testing.T) {
72
+	store, err := TempStore("test-remove")
73
+	if err != nil {
74
+		t.Fatal(err)
75
+	}
76
+	defer nuke(store)
77
+	archive, err := fake.FakeTar()
78
+	if err != nil {
79
+		t.Fatal(err)
80
+	}
81
+	if c := countImages(store); c != 0 {
82
+		t.Fatalf("Expected 0 images, %d found", c)
83
+	}
84
+
85
+	// Test 1 create / 1 delete
86
+	img, err := store.Create(archive, nil, "foo", "Testing")
87
+	if err != nil {
88
+		t.Fatal(err)
89
+	}
90
+	if c := countImages(store); c != 1 {
91
+		t.Fatalf("Expected 1 images, %d found", c)
92
+	}
93
+	if err := store.Remove(img); err != nil {
94
+		t.Fatal(err)
95
+	}
96
+	if c := countImages(store); c != 0 {
97
+		t.Fatalf("Expected 0 images, %d found", c)
98
+	}
99
+
100
+	// Test 2 create (same name) / 1 delete
101
+	img1, err := store.Create(archive, nil, "foo", "Testing")
102
+	if err != nil {
103
+		t.Fatal(err)
104
+	}
105
+	img2, err := store.Create(archive, nil, "foo", "Testing")
106
+	if err != nil {
107
+		t.Fatal(err)
108
+	}
109
+	if c := countImages(store); c != 2 {
110
+		t.Fatalf("Expected 2 images, %d found", c)
111
+	}
112
+	if err := store.Remove(img1); err != nil {
113
+		t.Fatal(err)
114
+	}
115
+	if c := countImages(store); c != 1 {
116
+		t.Fatalf("Expected 1 images, %d found", c)
117
+	}
118
+
119
+	// Test delete wrong name
120
+	// Note: If we change orm and Delete of non existing return error, we will need to change this test
121
+	if err := store.Remove(&Image{Id: "Not_foo", store: img2.store}); err != nil {
122
+		t.Fatal(err)
123
+	}
124
+	if c := countImages(store); c != 1 {
125
+		t.Fatalf("Expected 1 images, %d found", c)
126
+	}
127
+
128
+	// Test delete last one
129
+	if err := store.Remove(img2); err != nil {
130
+		t.Fatal(err)
131
+	}
132
+	if c := countImages(store); c != 0 {
133
+		t.Fatalf("Expected 0 images, %d found", c)
134
+	}
135
+}
136
+
137
+func TestRemoveRegexp(t *testing.T) {
138
+	store, err := TempStore("test-remove-regexp")
139
+	if err != nil {
140
+		t.Fatal(err)
141
+	}
142
+	defer nuke(store)
143
+	archive, err := fake.FakeTar()
144
+	if err != nil {
145
+		t.Fatal(err)
146
+	}
147
+	if c := countImages(store); c != 0 {
148
+		t.Fatalf("Expected 0 images, %d found", c)
149
+	}
150
+
151
+	// Test 10 create with different names / Delete all good regexp
152
+	for i := 0; i < 10; i++ {
153
+		if _, err := store.Create(archive, nil, fmt.Sprintf("foo-%d", i), "Testing"); err != nil {
154
+			t.Fatal(err)
155
+		}
156
+	}
157
+	if c := countImages(store); c != 10 {
158
+		t.Fatalf("Expected 10 images, %d found", c)
159
+	}
160
+	if err := store.RemoveRegexp("foo"); err != nil {
161
+		t.Fatal(err)
162
+	}
163
+	if c := countImages(store); c != 0 {
164
+		t.Fatalf("Expected 0 images, %d found", c)
165
+	}
166
+
167
+	// Test 10 create with different names / Delete all good regexp globing
168
+	for i := 0; i < 10; i++ {
169
+		if _, err := store.Create(archive, nil, fmt.Sprintf("foo-%d", i), "Testing"); err != nil {
170
+			t.Fatal(err)
171
+		}
172
+	}
173
+	if c := countImages(store); c != 10 {
174
+		t.Fatalf("Expected 10 images, %d found", c)
175
+	}
176
+	if err := store.RemoveRegexp("foo-*"); err != nil {
177
+		t.Fatal(err)
178
+	}
179
+	if c := countImages(store); c != 0 {
180
+		t.Fatalf("Expected 0 images, %d found", c)
181
+	}
182
+
183
+	// Test 10 create with different names / Delete all bad regexp
184
+	for i := 0; i < 10; i++ {
185
+		if _, err := store.Create(archive, nil, fmt.Sprintf("foo-%d", i), "Testing"); err != nil {
186
+			t.Fatal(err)
187
+		}
188
+	}
189
+	if c := countImages(store); c != 10 {
190
+		t.Fatalf("Expected 10 images, %d found", c)
191
+	}
192
+	if err := store.RemoveRegexp("oo-*"); err != nil {
193
+		t.Fatal(err)
194
+	}
195
+	if c := countImages(store); c != 0 {
196
+		t.Fatalf("Expected 0 images, %d found", c)
197
+	}
198
+
199
+	// Test 10 create with different names / Delete none strict regexp
200
+	for i := 0; i < 10; i++ {
201
+		if _, err := store.Create(archive, nil, fmt.Sprintf("foo-%d", i), "Testing"); err != nil {
202
+			t.Fatal(err)
203
+		}
204
+	}
205
+	if c := countImages(store); c != 10 {
206
+		t.Fatalf("Expected 10 images, %d found", c)
207
+	}
208
+	if err := store.RemoveRegexp("^oo-"); err != nil {
209
+		t.Fatal(err)
210
+	}
211
+	if c := countImages(store); c != 10 {
212
+		t.Fatalf("Expected 10 images, %d found", c)
213
+	}
214
+
215
+	// Test delete 2
216
+	if err := store.RemoveRegexp("^foo-[1,2]$"); err != nil {
217
+		t.Fatal(err)
218
+	}
219
+	if c := countImages(store); c != 8 {
220
+		t.Fatalf("Expected 8 images, %d found", c)
221
+	}
222
+}
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"os"
12 12
 	"path"
13 13
 	"path/filepath"
14
+	"regexp"
14 15
 	"strings"
15 16
 	"syscall"
16 17
 	"time"
... ...
@@ -105,6 +106,27 @@ func (store *Store) RemoveInPath(pth string) error {
105 105
 	return nil
106 106
 }
107 107
 
108
+// DeleteMatch deletes all images whose name matches `pattern`
109
+func (store *Store) RemoveRegexp(pattern string) error {
110
+	// Retrieve all the paths
111
+	paths, err := store.Paths()
112
+	if err != nil {
113
+		return err
114
+	}
115
+	// Check the pattern on each elements
116
+	for _, pth := range paths {
117
+		if match, err := regexp.MatchString(pattern, pth); err != nil {
118
+			return err
119
+		} else if match {
120
+			// If there is a match, remove it
121
+			if err := store.RemoveInPath(pth); err != nil {
122
+				return nil
123
+			}
124
+		}
125
+	}
126
+	return nil
127
+}
128
+
108 129
 func (store *Store) Remove(img *Image) error {
109 130
 	_, err := store.orm.Delete(img)
110 131
 	return err
... ...
@@ -1,7 +1,6 @@
1 1
 package fs
2 2
 
3 3
 import (
4
-	"errors"
5 4
 	"fmt"
6 5
 	"github.com/dotcloud/docker/fake"
7 6
 	"github.com/dotcloud/docker/future"
... ...
@@ -11,6 +10,8 @@ import (
11 11
 	"time"
12 12
 )
13 13
 
14
+// FIXME: Remove the Fake package
15
+
14 16
 func TestInit(t *testing.T) {
15 17
 	store, err := TempStore("testinit")
16 18
 	if err != nil {
... ...
@@ -26,6 +27,8 @@ func TestInit(t *testing.T) {
26 26
 	}
27 27
 }
28 28
 
29
+// FIXME: Do more extensive tests (ex: create multiple, delete, recreate;
30
+//       create multiple, check the amount of images and paths, etc..)
29 31
 func TestCreate(t *testing.T) {
30 32
 	store, err := TempStore("testcreate")
31 33
 	if err != nil {
... ...
@@ -229,63 +232,6 @@ func TestMountpointDuplicateRoot(t *testing.T) {
229 229
 	}
230 230
 }
231 231
 
232
-func TestMount(t *testing.T) {
233
-	store, err := TempStore("test-mount")
234
-	if err != nil {
235
-		t.Fatal(err)
236
-	}
237
-	defer nuke(store)
238
-	archive, err := fake.FakeTar()
239
-	if err != nil {
240
-		t.Fatal(err)
241
-	}
242
-	image, err := store.Create(archive, nil, "foo", "Testing")
243
-	if err != nil {
244
-		t.Fatal(err)
245
-	}
246
-	// Create mount targets
247
-	root, err := ioutil.TempDir("", "docker-fs-test")
248
-	if err != nil {
249
-		t.Fatal(err)
250
-	}
251
-	rw, err := ioutil.TempDir("", "docker-fs-test")
252
-	if err != nil {
253
-		t.Fatal(err)
254
-	}
255
-	mountpoint, err := image.Mount(root, rw)
256
-	if err != nil {
257
-		t.Fatal(err)
258
-	}
259
-	defer mountpoint.Umount()
260
-	// Mountpoint should be marked as mounted
261
-	if !mountpoint.Mounted() {
262
-		t.Fatal("Mountpoint not mounted")
263
-	}
264
-	// There should be one mountpoint registered
265
-	if mps, err := image.Mountpoints(); err != nil {
266
-		t.Fatal(err)
267
-	} else if len(mps) != 1 {
268
-		t.Fatal("Wrong number of mountpoints registered (should be %d, not %d)", 1, len(mps))
269
-	}
270
-	// Unmounting should work
271
-	if err := mountpoint.Umount(); err != nil {
272
-		t.Fatal(err)
273
-	}
274
-	// De-registering should work
275
-	if err := mountpoint.Deregister(); err != nil {
276
-		t.Fatal(err)
277
-	}
278
-	if mps, err := image.Mountpoints(); err != nil {
279
-		t.Fatal(err)
280
-	} else if len(mps) != 0 {
281
-		t.Fatal("Wrong number of mountpoints registered (should be %d, not %d)", 0, len(mps))
282
-	}
283
-	// General health check
284
-	if err := healthCheck(store); err != nil {
285
-		t.Fatal(err)
286
-	}
287
-}
288
-
289 232
 func TempStore(prefix string) (*Store, error) {
290 233
 	dir, err := ioutil.TempDir("", "docker-fs-test-"+prefix)
291 234
 	if err != nil {
... ...
@@ -314,7 +260,7 @@ func healthCheck(store *Store) error {
314 314
 		for _, img := range images {
315 315
 			// Check for duplicate IDs per path
316 316
 			if _, exists := IDs[img.Id]; exists {
317
-				return errors.New(fmt.Sprintf("Duplicate ID: %s", img.Id))
317
+				return fmt.Errorf("Duplicate ID: %s", img.Id)
318 318
 			} else {
319 319
 				IDs[img.Id] = true
320 320
 			}
... ...
@@ -327,7 +273,7 @@ func healthCheck(store *Store) error {
327 327
 	// Check non-existing parents
328 328
 	for parent := range parents {
329 329
 		if _, exists := parents[parent]; !exists {
330
-			return errors.New("Reference to non-registered parent: " + parent)
330
+			return fmt.Errorf("Reference to non-registered parent: %s", parent)
331 331
 		}
332 332
 	}
333 333
 	return nil
334 334
new file mode 100644
... ...
@@ -0,0 +1,115 @@
0
+package docker
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/fake"
5
+	"github.com/dotcloud/docker/fs"
6
+	"io/ioutil"
7
+	"os"
8
+	"testing"
9
+)
10
+
11
+// Look for inconsistencies in a store.
12
+func healthCheck(store *fs.Store) error {
13
+	parents := make(map[string]bool)
14
+	paths, err := store.Paths()
15
+	if err != nil {
16
+		return err
17
+	}
18
+	for _, path := range paths {
19
+		images, err := store.List(path)
20
+		if err != nil {
21
+			return err
22
+		}
23
+		IDs := make(map[string]bool) // All IDs for this path
24
+		for _, img := range images {
25
+			// Check for duplicate IDs per path
26
+			if _, exists := IDs[img.Id]; exists {
27
+				return fmt.Errorf("Duplicate ID: %s", img.Id)
28
+			} else {
29
+				IDs[img.Id] = true
30
+			}
31
+			// Store parent for 2nd pass
32
+			if parent := img.Parent; parent != "" {
33
+				parents[parent] = true
34
+			}
35
+		}
36
+	}
37
+	// Check non-existing parents
38
+	for parent := range parents {
39
+		if _, exists := parents[parent]; !exists {
40
+			return fmt.Errorf("Reference to non-registered parent: %s", parent)
41
+		}
42
+	}
43
+	return nil
44
+}
45
+
46
+// Note: This test is in the docker package because he needs to be run as root
47
+func TestMount(t *testing.T) {
48
+	dir, err := ioutil.TempDir("", "docker-fs-test-mount")
49
+	if err != nil {
50
+		t.Fatal(err)
51
+	}
52
+	defer os.RemoveAll(dir)
53
+
54
+	store, err := fs.New(dir)
55
+	if err != nil {
56
+		t.Fatal(err)
57
+	}
58
+
59
+	archive, err := fake.FakeTar()
60
+	if err != nil {
61
+		t.Fatal(err)
62
+	}
63
+
64
+	image, err := store.Create(archive, nil, "foo", "Testing")
65
+	if err != nil {
66
+		t.Fatal(err)
67
+	}
68
+
69
+	// Create mount targets
70
+	root, err := ioutil.TempDir("", "docker-fs-test")
71
+	if err != nil {
72
+		t.Fatal(err)
73
+	}
74
+	defer os.RemoveAll(root)
75
+
76
+	rw, err := ioutil.TempDir("", "docker-fs-test")
77
+	if err != nil {
78
+		t.Fatal(err)
79
+	}
80
+	defer os.RemoveAll(rw)
81
+
82
+	mountpoint, err := image.Mount(root, rw)
83
+	if err != nil {
84
+		t.Fatal(err)
85
+	}
86
+	defer mountpoint.Umount()
87
+	// Mountpoint should be marked as mounted
88
+	if !mountpoint.Mounted() {
89
+		t.Fatal("Mountpoint not mounted")
90
+	}
91
+	// There should be one mountpoint registered
92
+	if mps, err := image.Mountpoints(); err != nil {
93
+		t.Fatal(err)
94
+	} else if len(mps) != 1 {
95
+		t.Fatal("Wrong number of mountpoints registered (should be %d, not %d)", 1, len(mps))
96
+	}
97
+	// Unmounting should work
98
+	if err := mountpoint.Umount(); err != nil {
99
+		t.Fatal(err)
100
+	}
101
+	// De-registering should work
102
+	if err := mountpoint.Deregister(); err != nil {
103
+		t.Fatal(err)
104
+	}
105
+	if mps, err := image.Mountpoints(); err != nil {
106
+		t.Fatal(err)
107
+	} else if len(mps) != 0 {
108
+		t.Fatal("Wrong number of mountpoints registered (should be %d, not %d)", 0, len(mps))
109
+	}
110
+	// General health check
111
+	if err := healthCheck(store); err != nil {
112
+		t.Fatal(err)
113
+	}
114
+}