Browse code

Merge pull request #85 from smarterclayton/refactor_image_dirs

Merged by openshift-bot

OpenShift Bot authored on 2014/09/15 23:51:51
Showing 28 changed files
... ...
@@ -35,7 +35,10 @@ import (
35 35
 	"github.com/openshift/origin/pkg/build/webhook/github"
36 36
 	osclient "github.com/openshift/origin/pkg/client"
37 37
 	"github.com/openshift/origin/pkg/cmd/util/docker"
38
-	"github.com/openshift/origin/pkg/image"
38
+	imageetcd "github.com/openshift/origin/pkg/image/registry/etcd"
39
+	"github.com/openshift/origin/pkg/image/registry/image"
40
+	"github.com/openshift/origin/pkg/image/registry/imagerepository"
41
+	"github.com/openshift/origin/pkg/image/registry/imagerepositorymapping"
39 42
 	"github.com/openshift/origin/pkg/template"
40 43
 
41 44
 	// Register versioned api types
... ...
@@ -163,15 +166,15 @@ func (c *config) runApiserver() {
163 163
 	osClient := c.getOsClient()
164 164
 	etcdClient, etcdServers := c.getEtcdClient()
165 165
 
166
-	imageRegistry := image.NewEtcdRegistry(etcdClient)
166
+	imageRegistry := imageetcd.NewEtcd(etcdClient)
167 167
 
168 168
 	// initialize OpenShift API
169 169
 	storage := map[string]apiserver.RESTStorage{
170 170
 		"builds":                  buildregistry.NewStorage(build.NewEtcdRegistry(etcdClient)),
171 171
 		"buildConfigs":            buildconfigregistry.NewStorage(build.NewEtcdRegistry(etcdClient)),
172
-		"images":                  image.NewImageStorage(imageRegistry),
173
-		"imageRepositories":       image.NewImageRepositoryStorage(imageRegistry),
174
-		"imageRepositoryMappings": image.NewImageRepositoryMappingStorage(imageRegistry, imageRegistry),
172
+		"images":                  image.NewREST(imageRegistry),
173
+		"imageRepositories":       imagerepository.NewREST(imageRegistry),
174
+		"imageRepositoryMappings": imagerepositorymapping.NewREST(imageRegistry, imageRegistry),
175 175
 		"templateConfigs":         template.NewStorage(),
176 176
 	}
177 177
 
178 178
new file mode 100644
... ...
@@ -0,0 +1,40 @@
0
+package validation
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
4
+	"github.com/openshift/origin/pkg/image/api"
5
+)
6
+
7
+// ValidateImage tests required fields for an Image.
8
+func ValidateImage(image *api.Image) errors.ErrorList {
9
+	result := errors.ErrorList{}
10
+
11
+	if len(image.ID) == 0 {
12
+		result = append(result, errors.NewFieldRequired("ID", image.ID))
13
+	}
14
+
15
+	if len(image.DockerImageReference) == 0 {
16
+		result = append(result, errors.NewFieldRequired("DockerImageReference", image.DockerImageReference))
17
+	}
18
+
19
+	return result
20
+}
21
+
22
+// ValidateImageRepositoryMapping tests required fields for an ImageRepositoryMapping.
23
+func ValidateImageRepositoryMapping(mapping *api.ImageRepositoryMapping) errors.ErrorList {
24
+	result := errors.ErrorList{}
25
+
26
+	if len(mapping.DockerImageRepository) == 0 {
27
+		result = append(result, errors.NewFieldRequired("DockerImageRepository", mapping.DockerImageRepository))
28
+	}
29
+
30
+	if len(mapping.Tag) == 0 {
31
+		result = append(result, errors.NewFieldRequired("Tag", mapping.Tag))
32
+	}
33
+
34
+	for _, err := range ValidateImage(&mapping.Image).Prefix("image") {
35
+		result = append(result, err)
36
+	}
37
+
38
+	return result
39
+}
0 40
new file mode 100644
... ...
@@ -0,0 +1,108 @@
0
+package validation
1
+
2
+import (
3
+	"testing"
4
+
5
+	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
7
+	"github.com/openshift/origin/pkg/image/api"
8
+)
9
+
10
+func TestValidateImageOK(t *testing.T) {
11
+	errs := ValidateImage(&api.Image{
12
+		JSONBase:             kubeapi.JSONBase{ID: "foo"},
13
+		DockerImageReference: "openshift/ruby-19-centos",
14
+	})
15
+	if len(errs) > 0 {
16
+		t.Errorf("Unexpected non-empty error list: %#v", errs)
17
+	}
18
+}
19
+
20
+func TestValidateImageMissingFields(t *testing.T) {
21
+	errorCases := map[string]struct {
22
+		I api.Image
23
+		T errors.ValidationErrorType
24
+		F string
25
+	}{
26
+		"missing ID":                   {api.Image{DockerImageReference: "ref"}, errors.ValidationErrorTypeRequired, "ID"},
27
+		"missing DockerImageReference": {api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}, errors.ValidationErrorTypeRequired, "DockerImageReference"},
28
+	}
29
+
30
+	for k, v := range errorCases {
31
+		errs := ValidateImage(&v.I)
32
+		if len(errs) == 0 {
33
+			t.Errorf("Expected failure for %s", k)
34
+			continue
35
+		}
36
+		for i := range errs {
37
+			if errs[i].(errors.ValidationError).Type != v.T {
38
+				t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
39
+			}
40
+			if errs[i].(errors.ValidationError).Field != v.F {
41
+				t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
42
+			}
43
+		}
44
+	}
45
+}
46
+
47
+func TestValidateImageRepositoryMappingNotOK(t *testing.T) {
48
+	errorCases := map[string]struct {
49
+		I api.ImageRepositoryMapping
50
+		T errors.ValidationErrorType
51
+		F string
52
+	}{
53
+		"missing DockerImageRepository": {
54
+			api.ImageRepositoryMapping{
55
+				Tag: "latest",
56
+				Image: api.Image{
57
+					JSONBase: kubeapi.JSONBase{
58
+						ID: "foo",
59
+					},
60
+					DockerImageReference: "openshift/ruby-19-centos",
61
+				},
62
+			},
63
+			errors.ValidationErrorTypeRequired,
64
+			"DockerImageRepository",
65
+		},
66
+		"missing Tag": {
67
+			api.ImageRepositoryMapping{
68
+				DockerImageRepository: "openshift/ruby-19-centos",
69
+				Image: api.Image{
70
+					JSONBase: kubeapi.JSONBase{
71
+						ID: "foo",
72
+					},
73
+					DockerImageReference: "openshift/ruby-19-centos",
74
+				},
75
+			},
76
+			errors.ValidationErrorTypeRequired,
77
+			"Tag",
78
+		},
79
+		"missing image attributes": {
80
+			api.ImageRepositoryMapping{
81
+				Tag: "latest",
82
+				DockerImageRepository: "openshift/ruby-19-centos",
83
+				Image: api.Image{
84
+					DockerImageReference: "openshift/ruby-19-centos",
85
+				},
86
+			},
87
+			errors.ValidationErrorTypeRequired,
88
+			"image.ID",
89
+		},
90
+	}
91
+
92
+	for k, v := range errorCases {
93
+		errs := ValidateImageRepositoryMapping(&v.I)
94
+		if len(errs) == 0 {
95
+			t.Errorf("Expected failure for %s", k)
96
+			continue
97
+		}
98
+		for i := range errs {
99
+			if errs[i].(errors.ValidationError).Type != v.T {
100
+				t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
101
+			}
102
+			if errs[i].(errors.ValidationError).Field != v.F {
103
+				t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
104
+			}
105
+		}
106
+	}
107
+}
0 108
deleted file mode 100644
... ...
@@ -1,152 +0,0 @@
1
-package image
2
-
3
-import (
4
-	"errors"
5
-
6
-	apierrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
7
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
8
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
9
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
10
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
11
-	"github.com/golang/glog"
12
-	"github.com/openshift/origin/pkg/image/api"
13
-)
14
-
15
-// EtcdRegistry implements ImageRegistry and ImageRepositoryRegistry backed by etcd.
16
-type EtcdRegistry struct {
17
-	tools.EtcdHelper
18
-}
19
-
20
-// NewEtcdRegistry returns a new EtcdRegistry.
21
-func NewEtcdRegistry(client tools.EtcdClient) *EtcdRegistry {
22
-	registry := &EtcdRegistry{
23
-		EtcdHelper: tools.EtcdHelper{
24
-			client,
25
-			runtime.Codec,
26
-			runtime.ResourceVersioner,
27
-		},
28
-	}
29
-
30
-	return registry
31
-}
32
-
33
-// ListImages retrieves a list of images that match selector.
34
-func (r *EtcdRegistry) ListImages(selector labels.Selector) (*api.ImageList, error) {
35
-	list := api.ImageList{}
36
-	err := r.ExtractList("/images", &list.Items, &list.ResourceVersion)
37
-	if err != nil {
38
-		return nil, err
39
-	}
40
-	filtered := []api.Image{}
41
-	for _, item := range list.Items {
42
-		if selector.Matches(labels.Set(item.Labels)) {
43
-			filtered = append(filtered, item)
44
-		}
45
-	}
46
-	list.Items = filtered
47
-	return &list, nil
48
-}
49
-
50
-func makeImageKey(id string) string {
51
-	return "/images/" + id
52
-}
53
-
54
-// GetImage retrieves a specific image
55
-func (r *EtcdRegistry) GetImage(id string) (*api.Image, error) {
56
-	var image api.Image
57
-	if err := r.ExtractObj(makeImageKey(id), &image, false); err != nil {
58
-		return nil, err
59
-	}
60
-	return &image, nil
61
-}
62
-
63
-// CreateImage creates a new image
64
-func (r *EtcdRegistry) CreateImage(image *api.Image) error {
65
-	err := r.CreateObj(makeImageKey(image.ID), image)
66
-	if tools.IsEtcdNodeExist(err) {
67
-		return apierrors.NewAlreadyExists("image", image.ID)
68
-	}
69
-	return err
70
-}
71
-
72
-// UpdateImage updates an existing image
73
-func (r *EtcdRegistry) UpdateImage(image *api.Image) error {
74
-	return errors.New("not supported")
75
-}
76
-
77
-// DeleteImage deletes an existing image
78
-func (r *EtcdRegistry) DeleteImage(id string) error {
79
-	key := makeImageKey(id)
80
-	err := r.Delete(key, false)
81
-	if tools.IsEtcdNotFound(err) {
82
-		return apierrors.NewNotFound("image", id)
83
-	}
84
-	return err
85
-}
86
-
87
-// ListImageRepositories retrieves a list of ImageRepositories that match selector.
88
-func (r *EtcdRegistry) ListImageRepositories(selector labels.Selector) (*api.ImageRepositoryList, error) {
89
-	list := api.ImageRepositoryList{}
90
-	err := r.ExtractList("/imageRepositories", &list.Items, &list.ResourceVersion)
91
-	if err != nil {
92
-		return nil, err
93
-	}
94
-	filtered := []api.ImageRepository{}
95
-	for _, item := range list.Items {
96
-		if selector.Matches(labels.Set(item.Labels)) {
97
-			filtered = append(filtered, item)
98
-		}
99
-	}
100
-	list.Items = filtered
101
-	return &list, nil
102
-}
103
-
104
-func makeImageRepositoryKey(id string) string {
105
-	return "/imageRepositories/" + id
106
-}
107
-
108
-// GetImageRepository retrieves an ImageRepository by id.
109
-func (r *EtcdRegistry) GetImageRepository(id string) (*api.ImageRepository, error) {
110
-	var repo api.ImageRepository
111
-	if err := r.ExtractObj(makeImageRepositoryKey(id), &repo, false); err != nil {
112
-		return nil, err
113
-	}
114
-	return &repo, nil
115
-}
116
-
117
-// WatchImageRepositories begins watching for new, changed, or deleted ImageRepositories.
118
-func (r *EtcdRegistry) WatchImageRepositories(resourceVersion uint64, filter func(repo *api.ImageRepository) bool) (watch.Interface, error) {
119
-	return r.WatchList("/imageRepositories", resourceVersion, func(obj interface{}) bool {
120
-		repo, ok := obj.(*api.ImageRepository)
121
-		if !ok {
122
-			glog.Errorf("Unexpected object during image repository watch: %#v", obj)
123
-			return false
124
-		}
125
-		return filter(repo)
126
-	})
127
-}
128
-
129
-// CreateImageRepository registers the given ImageRepository.
130
-func (r *EtcdRegistry) CreateImageRepository(repo *api.ImageRepository) error {
131
-	err := r.CreateObj(makeImageRepositoryKey(repo.ID), repo)
132
-	if err != nil && tools.IsEtcdNodeExist(err) {
133
-		return apierrors.NewAlreadyExists("imageRepository", repo.ID)
134
-	}
135
-
136
-	return err
137
-}
138
-
139
-// UpdateImageRepository replaces an existing ImageRepository in the registry with the given ImageRepository.
140
-func (r *EtcdRegistry) UpdateImageRepository(repo *api.ImageRepository) error {
141
-	return r.SetObj(makeImageRepositoryKey(repo.ID), repo)
142
-}
143
-
144
-// DeleteImageRepository deletes an ImageRepository by id.
145
-func (r *EtcdRegistry) DeleteImageRepository(id string) error {
146
-	imageRepositoryKey := makeImageRepositoryKey(id)
147
-	err := r.Delete(imageRepositoryKey, false)
148
-	if err != nil && tools.IsEtcdNotFound(err) {
149
-		return apierrors.NewNotFound("imageRepository", id)
150
-	}
151
-	return err
152
-}
153 1
deleted file mode 100644
... ...
@@ -1,594 +0,0 @@
1
-package image
2
-
3
-import (
4
-	"fmt"
5
-	"reflect"
6
-	"testing"
7
-
8
-	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
9
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
10
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
11
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
12
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
13
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
14
-	"github.com/coreos/go-etcd/etcd"
15
-	"github.com/fsouza/go-dockerclient"
16
-	"github.com/openshift/origin/pkg/image/api"
17
-	_ "github.com/openshift/origin/pkg/image/api/v1beta1"
18
-)
19
-
20
-func NewTestEtcdRegistry(client tools.EtcdClient) *EtcdRegistry {
21
-	return NewEtcdRegistry(client)
22
-}
23
-
24
-func TestEtcdListImagesEmpty(t *testing.T) {
25
-	fakeClient := tools.NewFakeEtcdClient(t)
26
-	key := "/images"
27
-	fakeClient.Data[key] = tools.EtcdResponseWithError{
28
-		R: &etcd.Response{
29
-			Node: &etcd.Node{
30
-				Nodes: []*etcd.Node{},
31
-			},
32
-		},
33
-		E: nil,
34
-	}
35
-	registry := NewTestEtcdRegistry(fakeClient)
36
-	images, err := registry.ListImages(labels.Everything())
37
-	if err != nil {
38
-		t.Errorf("unexpected error: %v", err)
39
-	}
40
-
41
-	if len(images.Items) != 0 {
42
-		t.Errorf("Unexpected images list: %#v", images)
43
-	}
44
-}
45
-
46
-func TestEtcdListImagesError(t *testing.T) {
47
-	fakeClient := tools.NewFakeEtcdClient(t)
48
-	key := "/images"
49
-	fakeClient.Data[key] = tools.EtcdResponseWithError{
50
-		R: &etcd.Response{
51
-			Node: nil,
52
-		},
53
-		E: fmt.Errorf("some error"),
54
-	}
55
-	registry := NewTestEtcdRegistry(fakeClient)
56
-	images, err := registry.ListImages(labels.Everything())
57
-	if err == nil {
58
-		t.Error("unexpected nil error")
59
-	}
60
-
61
-	if images != nil {
62
-		t.Errorf("Unexpected non-nil images: %#v", images)
63
-	}
64
-}
65
-
66
-func TestEtcdListImagesEverything(t *testing.T) {
67
-	fakeClient := tools.NewFakeEtcdClient(t)
68
-	key := "/images"
69
-	fakeClient.Data[key] = tools.EtcdResponseWithError{
70
-		R: &etcd.Response{
71
-			Node: &etcd.Node{
72
-				Nodes: []*etcd.Node{
73
-					{
74
-						Value: runtime.EncodeOrDie(api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
75
-					},
76
-					{
77
-						Value: runtime.EncodeOrDie(api.Image{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
78
-					},
79
-				},
80
-			},
81
-		},
82
-		E: nil,
83
-	}
84
-	registry := NewTestEtcdRegistry(fakeClient)
85
-	images, err := registry.ListImages(labels.Everything())
86
-	if err != nil {
87
-		t.Errorf("unexpected error: %v", err)
88
-	}
89
-
90
-	if len(images.Items) != 2 || images.Items[0].ID != "foo" || images.Items[1].ID != "bar" {
91
-		t.Errorf("Unexpected images list: %#v", images)
92
-	}
93
-}
94
-
95
-func TestEtcdListImagesFiltered(t *testing.T) {
96
-	fakeClient := tools.NewFakeEtcdClient(t)
97
-	key := "/images"
98
-	fakeClient.Data[key] = tools.EtcdResponseWithError{
99
-		R: &etcd.Response{
100
-			Node: &etcd.Node{
101
-				Nodes: []*etcd.Node{
102
-					{
103
-						Value: runtime.EncodeOrDie(api.Image{
104
-							JSONBase: kubeapi.JSONBase{ID: "foo"},
105
-							Labels:   map[string]string{"env": "prod"},
106
-						}),
107
-					},
108
-					{
109
-						Value: runtime.EncodeOrDie(api.Image{
110
-							JSONBase: kubeapi.JSONBase{ID: "bar"},
111
-							Labels:   map[string]string{"env": "dev"},
112
-						}),
113
-					},
114
-				},
115
-			},
116
-		},
117
-		E: nil,
118
-	}
119
-	registry := NewTestEtcdRegistry(fakeClient)
120
-	images, err := registry.ListImages(labels.SelectorFromSet(labels.Set{"env": "dev"}))
121
-	if err != nil {
122
-		t.Errorf("unexpected error: %v", err)
123
-	}
124
-
125
-	if len(images.Items) != 1 || images.Items[0].ID != "bar" {
126
-		t.Errorf("Unexpected images list: %#v", images)
127
-	}
128
-}
129
-
130
-func TestEtcdGetImage(t *testing.T) {
131
-	fakeClient := tools.NewFakeEtcdClient(t)
132
-	fakeClient.Set("/images/foo", runtime.EncodeOrDie(api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
133
-	registry := NewTestEtcdRegistry(fakeClient)
134
-	image, err := registry.GetImage("foo")
135
-	if err != nil {
136
-		t.Errorf("unexpected error: %v", err)
137
-	}
138
-
139
-	if image.ID != "foo" {
140
-		t.Errorf("Unexpected image: %#v", image)
141
-	}
142
-}
143
-
144
-func TestEtcdGetImageNotFound(t *testing.T) {
145
-	fakeClient := tools.NewFakeEtcdClient(t)
146
-	fakeClient.Data["/images/foo"] = tools.EtcdResponseWithError{
147
-		R: &etcd.Response{
148
-			Node: nil,
149
-		},
150
-		E: tools.EtcdErrorNotFound,
151
-	}
152
-	registry := NewTestEtcdRegistry(fakeClient)
153
-	image, err := registry.GetImage("foo")
154
-	if err == nil {
155
-		t.Errorf("Unexpected non-error.")
156
-	}
157
-	if image != nil {
158
-		t.Errorf("Unexpected image: %#v", image)
159
-	}
160
-}
161
-
162
-func TestEtcdCreateImage(t *testing.T) {
163
-	fakeClient := tools.NewFakeEtcdClient(t)
164
-	fakeClient.TestIndex = true
165
-	fakeClient.Data["/images/foo"] = tools.EtcdResponseWithError{
166
-		R: &etcd.Response{
167
-			Node: nil,
168
-		},
169
-		E: tools.EtcdErrorNotFound,
170
-	}
171
-	registry := NewTestEtcdRegistry(fakeClient)
172
-	err := registry.CreateImage(&api.Image{
173
-		JSONBase: kubeapi.JSONBase{
174
-			ID: "foo",
175
-		},
176
-		DockerImageReference: "openshift/ruby-19-centos",
177
-		Metadata: docker.Image{
178
-			ID: "abc123",
179
-		},
180
-	})
181
-	if err != nil {
182
-		t.Fatalf("unexpected error: %v", err)
183
-	}
184
-
185
-	resp, err := fakeClient.Get("/images/foo", false, false)
186
-	if err != nil {
187
-		t.Fatalf("Unexpected error %v", err)
188
-	}
189
-	var image api.Image
190
-	err = runtime.DecodeInto([]byte(resp.Node.Value), &image)
191
-	if err != nil {
192
-		t.Errorf("unexpected error: %v", err)
193
-	}
194
-
195
-	if image.ID != "foo" {
196
-		t.Errorf("Unexpected image: %#v %s", image, resp.Node.Value)
197
-	}
198
-
199
-	if e, a := "openshift/ruby-19-centos", image.DockerImageReference; e != a {
200
-		t.Errorf("Expected %v, got %v", e, a)
201
-	}
202
-
203
-	if e, a := "abc123", image.Metadata.ID; e != a {
204
-		t.Errorf("Expected %v, got %v", e, a)
205
-	}
206
-}
207
-
208
-func TestEtcdCreateImageAlreadyExists(t *testing.T) {
209
-	fakeClient := tools.NewFakeEtcdClient(t)
210
-	fakeClient.Data["/images/foo"] = tools.EtcdResponseWithError{
211
-		R: &etcd.Response{
212
-			Node: &etcd.Node{
213
-				Value: runtime.EncodeOrDie(api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
214
-			},
215
-		},
216
-		E: nil,
217
-	}
218
-	registry := NewTestEtcdRegistry(fakeClient)
219
-	err := registry.CreateImage(&api.Image{
220
-		JSONBase: kubeapi.JSONBase{
221
-			ID: "foo",
222
-		},
223
-	})
224
-	if err == nil {
225
-		t.Error("Unexpected non-error")
226
-	}
227
-	if !errors.IsAlreadyExists(err) {
228
-		t.Errorf("Expected 'already exists' error, got %#v", err)
229
-	}
230
-}
231
-
232
-func TestEtcdUpdateImage(t *testing.T) {
233
-	fakeClient := tools.NewFakeEtcdClient(t)
234
-	registry := NewTestEtcdRegistry(fakeClient)
235
-	err := registry.UpdateImage(&api.Image{})
236
-	if err == nil {
237
-		t.Error("Unexpected non-error")
238
-	}
239
-}
240
-
241
-func TestEtcdDeleteImageNotFound(t *testing.T) {
242
-	fakeClient := tools.NewFakeEtcdClient(t)
243
-	fakeClient.Err = tools.EtcdErrorNotFound
244
-	registry := NewTestEtcdRegistry(fakeClient)
245
-	err := registry.DeleteImage("foo")
246
-	if err == nil {
247
-		t.Error("Unexpected non-error")
248
-	}
249
-	if !errors.IsNotFound(err) {
250
-		t.Errorf("Expected 'not found' error, got %#v", err)
251
-	}
252
-}
253
-
254
-func TestEtcdDeleteImageError(t *testing.T) {
255
-	fakeClient := tools.NewFakeEtcdClient(t)
256
-	fakeClient.Err = fmt.Errorf("Some error")
257
-	registry := NewTestEtcdRegistry(fakeClient)
258
-	err := registry.DeleteImage("foo")
259
-	if err == nil {
260
-		t.Error("Unexpected non-error")
261
-	}
262
-}
263
-
264
-func TestEtcdDeleteImageOK(t *testing.T) {
265
-	fakeClient := tools.NewFakeEtcdClient(t)
266
-	registry := NewTestEtcdRegistry(fakeClient)
267
-	key := "/images/foo"
268
-	err := registry.DeleteImage("foo")
269
-	if err != nil {
270
-		t.Errorf("Unexpected error: %#v", err)
271
-	}
272
-	if len(fakeClient.DeletedKeys) != 1 {
273
-		t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
274
-	} else if fakeClient.DeletedKeys[0] != key {
275
-		t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
276
-	}
277
-}
278
-
279
-func TestEtcdListImagesRepositoriesEmpty(t *testing.T) {
280
-	fakeClient := tools.NewFakeEtcdClient(t)
281
-	key := "/imageRepositories"
282
-	fakeClient.Data[key] = tools.EtcdResponseWithError{
283
-		R: &etcd.Response{
284
-			Node: &etcd.Node{
285
-				Nodes: []*etcd.Node{},
286
-			},
287
-		},
288
-		E: nil,
289
-	}
290
-	registry := NewTestEtcdRegistry(fakeClient)
291
-	repos, err := registry.ListImageRepositories(labels.Everything())
292
-	if err != nil {
293
-		t.Errorf("unexpected error: %v", err)
294
-	}
295
-
296
-	if len(repos.Items) != 0 {
297
-		t.Errorf("Unexpected image repositories list: %#v", repos)
298
-	}
299
-}
300
-
301
-func TestEtcdListImageRepositoriesError(t *testing.T) {
302
-	fakeClient := tools.NewFakeEtcdClient(t)
303
-	key := "/imageRepositories"
304
-	fakeClient.Data[key] = tools.EtcdResponseWithError{
305
-		R: &etcd.Response{
306
-			Node: nil,
307
-		},
308
-		E: fmt.Errorf("some error"),
309
-	}
310
-	registry := NewTestEtcdRegistry(fakeClient)
311
-	repos, err := registry.ListImageRepositories(labels.Everything())
312
-	if err == nil {
313
-		t.Error("unexpected nil error")
314
-	}
315
-
316
-	if repos != nil {
317
-		t.Errorf("Unexpected non-nil repos: %#v", repos)
318
-	}
319
-}
320
-
321
-func TestEtcdListImageRepositoriesEverything(t *testing.T) {
322
-	fakeClient := tools.NewFakeEtcdClient(t)
323
-	key := "/imageRepositories"
324
-	fakeClient.Data[key] = tools.EtcdResponseWithError{
325
-		R: &etcd.Response{
326
-			Node: &etcd.Node{
327
-				Nodes: []*etcd.Node{
328
-					{
329
-						Value: runtime.EncodeOrDie(api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
330
-					},
331
-					{
332
-						Value: runtime.EncodeOrDie(api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
333
-					},
334
-				},
335
-			},
336
-		},
337
-		E: nil,
338
-	}
339
-	registry := NewTestEtcdRegistry(fakeClient)
340
-	repos, err := registry.ListImageRepositories(labels.Everything())
341
-	if err != nil {
342
-		t.Errorf("unexpected error: %v", err)
343
-	}
344
-
345
-	if len(repos.Items) != 2 || repos.Items[0].ID != "foo" || repos.Items[1].ID != "bar" {
346
-		t.Errorf("Unexpected images list: %#v", repos)
347
-	}
348
-}
349
-
350
-func TestEtcdListImageRepositoriesFiltered(t *testing.T) {
351
-	fakeClient := tools.NewFakeEtcdClient(t)
352
-	key := "/imageRepositories"
353
-	fakeClient.Data[key] = tools.EtcdResponseWithError{
354
-		R: &etcd.Response{
355
-			Node: &etcd.Node{
356
-				Nodes: []*etcd.Node{
357
-					{
358
-						Value: runtime.EncodeOrDie(api.ImageRepository{
359
-							JSONBase: kubeapi.JSONBase{ID: "foo"},
360
-							Labels:   map[string]string{"env": "prod"},
361
-						}),
362
-					},
363
-					{
364
-						Value: runtime.EncodeOrDie(api.ImageRepository{
365
-							JSONBase: kubeapi.JSONBase{ID: "bar"},
366
-							Labels:   map[string]string{"env": "dev"},
367
-						}),
368
-					},
369
-				},
370
-			},
371
-		},
372
-		E: nil,
373
-	}
374
-	registry := NewTestEtcdRegistry(fakeClient)
375
-	repos, err := registry.ListImageRepositories(labels.SelectorFromSet(labels.Set{"env": "dev"}))
376
-	if err != nil {
377
-		t.Errorf("unexpected error: %v", err)
378
-	}
379
-
380
-	if len(repos.Items) != 1 || repos.Items[0].ID != "bar" {
381
-		t.Errorf("Unexpected repos list: %#v", repos)
382
-	}
383
-}
384
-
385
-func TestEtcdGetImageRepository(t *testing.T) {
386
-	fakeClient := tools.NewFakeEtcdClient(t)
387
-	fakeClient.Set("/imageRepositories/foo", runtime.EncodeOrDie(api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
388
-	registry := NewTestEtcdRegistry(fakeClient)
389
-	repo, err := registry.GetImageRepository("foo")
390
-	if err != nil {
391
-		t.Errorf("unexpected error: %v", err)
392
-	}
393
-
394
-	if repo.ID != "foo" {
395
-		t.Errorf("Unexpected repo: %#v", repo)
396
-	}
397
-}
398
-
399
-func TestEtcdGetImageRepositoryNotFound(t *testing.T) {
400
-	fakeClient := tools.NewFakeEtcdClient(t)
401
-	fakeClient.Data["/imageRepositories/foo"] = tools.EtcdResponseWithError{
402
-		R: &etcd.Response{
403
-			Node: nil,
404
-		},
405
-		E: tools.EtcdErrorNotFound,
406
-	}
407
-	registry := NewTestEtcdRegistry(fakeClient)
408
-	repo, err := registry.GetImageRepository("foo")
409
-	if err == nil {
410
-		t.Errorf("Unexpected non-error.")
411
-	}
412
-	if repo != nil {
413
-		t.Errorf("Unexpected non-nil repo: %#v", repo)
414
-	}
415
-}
416
-
417
-func TestEtcdCreateImageRepository(t *testing.T) {
418
-	fakeClient := tools.NewFakeEtcdClient(t)
419
-	fakeClient.TestIndex = true
420
-	fakeClient.Data["/imageRepositories/foo"] = tools.EtcdResponseWithError{
421
-		R: &etcd.Response{
422
-			Node: nil,
423
-		},
424
-		E: tools.EtcdErrorNotFound,
425
-	}
426
-	registry := NewTestEtcdRegistry(fakeClient)
427
-	err := registry.CreateImageRepository(&api.ImageRepository{
428
-		JSONBase: kubeapi.JSONBase{
429
-			ID: "foo",
430
-		},
431
-		Labels:                map[string]string{"a": "b"},
432
-		DockerImageRepository: "c/d",
433
-		Tags: map[string]string{"t1": "v1"},
434
-	})
435
-	if err != nil {
436
-		t.Fatalf("unexpected error: %v", err)
437
-	}
438
-
439
-	resp, err := fakeClient.Get("/imageRepositories/foo", false, false)
440
-	if err != nil {
441
-		t.Fatalf("Unexpected error %v", err)
442
-	}
443
-	var repo api.ImageRepository
444
-	err = runtime.DecodeInto([]byte(resp.Node.Value), &repo)
445
-	if err != nil {
446
-		t.Errorf("unexpected error: %v", err)
447
-	}
448
-
449
-	if repo.ID != "foo" {
450
-		t.Errorf("Unexpected repo: %#v %s", repo, resp.Node.Value)
451
-	}
452
-
453
-	if len(repo.Labels) != 1 || repo.Labels["a"] != "b" {
454
-		t.Errorf("Unexpected labels: %#v", repo.Labels)
455
-	}
456
-
457
-	if repo.DockerImageRepository != "c/d" {
458
-		t.Errorf("Unexpected docker image repo: %s", repo.DockerImageRepository)
459
-	}
460
-
461
-	if len(repo.Tags) != 1 || repo.Tags["t1"] != "v1" {
462
-		t.Errorf("Unexpected tags: %#v", repo.Tags)
463
-	}
464
-}
465
-
466
-func TestEtcdCreateImageRepositoryAlreadyExists(t *testing.T) {
467
-	fakeClient := tools.NewFakeEtcdClient(t)
468
-	fakeClient.Data["/imageRepositories/foo"] = tools.EtcdResponseWithError{
469
-		R: &etcd.Response{
470
-			Node: &etcd.Node{
471
-				Value: runtime.EncodeOrDie(api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
472
-			},
473
-		},
474
-		E: nil,
475
-	}
476
-	registry := NewTestEtcdRegistry(fakeClient)
477
-	err := registry.CreateImageRepository(&api.ImageRepository{
478
-		JSONBase: kubeapi.JSONBase{
479
-			ID: "foo",
480
-		},
481
-	})
482
-	if err == nil {
483
-		t.Error("Unexpected non-error")
484
-	}
485
-	if !errors.IsAlreadyExists(err) {
486
-		t.Errorf("Expected 'already exists' error, got %#v", err)
487
-	}
488
-}
489
-
490
-func TestEtcdUpdateImageRepository(t *testing.T) {
491
-	fakeClient := tools.NewFakeEtcdClient(t)
492
-	fakeClient.TestIndex = true
493
-
494
-	resp, _ := fakeClient.Set("/imageRepositories/foo", runtime.EncodeOrDie(api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
495
-	registry := NewTestEtcdRegistry(fakeClient)
496
-	err := registry.UpdateImageRepository(&api.ImageRepository{
497
-		JSONBase:              kubeapi.JSONBase{ID: "foo", ResourceVersion: resp.Node.ModifiedIndex},
498
-		DockerImageRepository: "some/repo",
499
-	})
500
-	if err != nil {
501
-		t.Errorf("unexpected error: %v", err)
502
-	}
503
-
504
-	repo, err := registry.GetImageRepository("foo")
505
-	if repo.DockerImageRepository != "some/repo" {
506
-		t.Errorf("Unexpected repo: %#v", repo)
507
-	}
508
-}
509
-
510
-func TestEtcdDeleteImageRepositoryNotFound(t *testing.T) {
511
-	fakeClient := tools.NewFakeEtcdClient(t)
512
-	fakeClient.Err = tools.EtcdErrorNotFound
513
-	registry := NewTestEtcdRegistry(fakeClient)
514
-	err := registry.DeleteImageRepository("foo")
515
-	if err == nil {
516
-		t.Error("Unexpected non-error")
517
-	}
518
-	if !errors.IsNotFound(err) {
519
-		t.Errorf("Expected 'not found' error, got %#v", err)
520
-	}
521
-}
522
-
523
-func TestEtcdDeleteImageRepositoryError(t *testing.T) {
524
-	fakeClient := tools.NewFakeEtcdClient(t)
525
-	fakeClient.Err = fmt.Errorf("Some error")
526
-	registry := NewTestEtcdRegistry(fakeClient)
527
-	err := registry.DeleteImageRepository("foo")
528
-	if err == nil {
529
-		t.Error("Unexpected non-error")
530
-	}
531
-}
532
-
533
-func TestEtcdDeleteImageRepositoryOK(t *testing.T) {
534
-	fakeClient := tools.NewFakeEtcdClient(t)
535
-	registry := NewTestEtcdRegistry(fakeClient)
536
-	key := "/imageRepositories/foo"
537
-	err := registry.DeleteImageRepository("foo")
538
-	if err != nil {
539
-		t.Errorf("Unexpected error: %#v", err)
540
-	}
541
-	if len(fakeClient.DeletedKeys) != 1 {
542
-		t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
543
-	} else if fakeClient.DeletedKeys[0] != key {
544
-		t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
545
-	}
546
-}
547
-
548
-func TestEtcdWatchImageRepositories(t *testing.T) {
549
-	fakeClient := tools.NewFakeEtcdClient(t)
550
-	registry := NewTestEtcdRegistry(fakeClient)
551
-	filterFields := labels.SelectorFromSet(labels.Set{"ID": "foo"})
552
-
553
-	watching, err := registry.WatchImageRepositories(1, func(repo *api.ImageRepository) bool {
554
-		fields := labels.Set{
555
-			"ID": repo.ID,
556
-		}
557
-		return filterFields.Matches(fields)
558
-	})
559
-	if err != nil {
560
-		t.Fatalf("unexpected error: %v", err)
561
-	}
562
-	fakeClient.WaitForWatchCompletion()
563
-
564
-	repo := &api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}
565
-	repoBytes, _ := runtime.Codec.Encode(repo)
566
-	fakeClient.WatchResponse <- &etcd.Response{
567
-		Action: "set",
568
-		Node: &etcd.Node{
569
-			Value: string(repoBytes),
570
-		},
571
-	}
572
-
573
-	event := <-watching.ResultChan()
574
-	if e, a := watch.Added, event.Type; e != a {
575
-		t.Errorf("Expected %v, got %v", e, a)
576
-	}
577
-	if e, a := repo, event.Object; !reflect.DeepEqual(e, a) {
578
-		t.Errorf("Expected %v, got %v", e, a)
579
-	}
580
-
581
-	select {
582
-	case _, ok := <-watching.ResultChan():
583
-		if !ok {
584
-			t.Errorf("watching channel should be open")
585
-		}
586
-	default:
587
-	}
588
-
589
-	fakeClient.WatchInjectError <- nil
590
-	if _, ok := <-watching.ResultChan(); ok {
591
-		t.Errorf("watching channel should be closed")
592
-	}
593
-	watching.Stop()
594
-}
595 1
deleted file mode 100644
... ...
@@ -1,115 +0,0 @@
1
-package image
2
-
3
-import (
4
-	"fmt"
5
-
6
-	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
8
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
9
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
10
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
11
-	"github.com/openshift/origin/pkg/image/api"
12
-)
13
-
14
-// ImageRepositoryMappingStorage implements the RESTStorage interface in terms of an ImageRegistry and ImageRepositoryRegistry.
15
-// It Only supports the Create method and is used to simply adding a new Image and tag to an ImageRepository.
16
-type ImageRepositoryMappingStorage struct {
17
-	imageRegistry           ImageRegistry
18
-	imageRepositoryRegistry ImageRepositoryRegistry
19
-}
20
-
21
-// NewImageRepositoryMappingStorage returns a new ImageRepositoryMappingStorage.
22
-func NewImageRepositoryMappingStorage(imageRegistry ImageRegistry, imageRepositoryRegistry ImageRepositoryRegistry) apiserver.RESTStorage {
23
-	return &ImageRepositoryMappingStorage{imageRegistry, imageRepositoryRegistry}
24
-}
25
-
26
-// New returns a new ImageRepositoryMapping for use with Create.
27
-func (s *ImageRepositoryMappingStorage) New() interface{} {
28
-	return &api.ImageRepositoryMapping{}
29
-}
30
-
31
-// Get is not supported.
32
-func (s *ImageRepositoryMappingStorage) Get(id string) (interface{}, error) {
33
-	return nil, errors.NewNotFound("imageRepositoryMapping", id)
34
-}
35
-
36
-// List is not supported.
37
-func (s *ImageRepositoryMappingStorage) List(selector labels.Selector) (interface{}, error) {
38
-	return nil, errors.NewNotFound("imageRepositoryMapping", "list")
39
-}
40
-
41
-// Create registers a new image (if it doesn't exist) and updates the specified ImageRepository's tags.
42
-func (s *ImageRepositoryMappingStorage) Create(obj interface{}) (<-chan interface{}, error) {
43
-	mapping, ok := obj.(*api.ImageRepositoryMapping)
44
-	if !ok {
45
-		return nil, fmt.Errorf("not an image repository mapping: %#v", obj)
46
-	}
47
-
48
-	repo, err := s.findImageRepository(mapping.DockerImageRepository)
49
-	if err != nil {
50
-		return nil, err
51
-	}
52
-	if repo == nil {
53
-		return nil, errors.NewInvalid("imageRepositoryMapping", mapping.ID, errors.ErrorList{
54
-			errors.NewFieldNotFound("DockerImageRepository", mapping.DockerImageRepository),
55
-		})
56
-	}
57
-
58
-	if errs := ValidateImageRepositoryMapping(mapping); len(errs) > 0 {
59
-		return nil, errors.NewInvalid("imageRepositoryMapping", mapping.ID, errs)
60
-	}
61
-
62
-	image := mapping.Image
63
-
64
-	image.CreationTimestamp = util.Now()
65
-
66
-	//TODO apply metadata overrides
67
-
68
-	if repo.Tags == nil {
69
-		repo.Tags = make(map[string]string)
70
-	}
71
-	repo.Tags[mapping.Tag] = image.ID
72
-
73
-	return apiserver.MakeAsync(func() (interface{}, error) {
74
-		err = s.imageRegistry.CreateImage(&image)
75
-		if err != nil && !errors.IsAlreadyExists(err) {
76
-			return nil, err
77
-		}
78
-
79
-		err = s.imageRepositoryRegistry.UpdateImageRepository(repo)
80
-		if err != nil {
81
-			return nil, err
82
-		}
83
-
84
-		return &kubeapi.Status{Status: kubeapi.StatusSuccess}, nil
85
-	}), nil
86
-}
87
-
88
-// findImageRepository retrieves an ImageRepository whose DockerImageRepository matches dockerRepo.
89
-func (s *ImageRepositoryMappingStorage) findImageRepository(dockerRepo string) (*api.ImageRepository, error) {
90
-	//TODO make this more efficient
91
-	list, err := s.imageRepositoryRegistry.ListImageRepositories(labels.Everything())
92
-	if err != nil {
93
-		return nil, err
94
-	}
95
-
96
-	var repo *api.ImageRepository
97
-	for _, r := range list.Items {
98
-		if dockerRepo == r.DockerImageRepository {
99
-			repo = &r
100
-			break
101
-		}
102
-	}
103
-
104
-	return repo, nil
105
-}
106
-
107
-// Update is not supported.
108
-func (s *ImageRepositoryMappingStorage) Update(obj interface{}) (<-chan interface{}, error) {
109
-	return nil, fmt.Errorf("ImageRepositoryMappings may not be changed.")
110
-}
111
-
112
-// Delete is not supported.
113
-func (s *ImageRepositoryMappingStorage) Delete(id string) (<-chan interface{}, error) {
114
-	return nil, errors.NewNotFound("imageRepositoryMapping", id)
115
-}
116 1
deleted file mode 100644
... ...
@@ -1,230 +0,0 @@
1
-package image
2
-
3
-import (
4
-	"fmt"
5
-	"reflect"
6
-	"strings"
7
-	"testing"
8
-
9
-	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
10
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
11
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
12
-	"github.com/fsouza/go-dockerclient"
13
-	"github.com/openshift/origin/pkg/image/api"
14
-	"github.com/openshift/origin/pkg/image/imagetest"
15
-)
16
-
17
-func TestGetImageRepositoryMapping(t *testing.T) {
18
-	imageRegistry := imagetest.NewImageRegistry()
19
-	imageRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
20
-	storage := &ImageRepositoryMappingStorage{imageRegistry, imageRepositoryRegistry}
21
-
22
-	obj, err := storage.Get("foo")
23
-	if obj != nil {
24
-		t.Errorf("Unexpected non-nil object %#v", obj)
25
-	}
26
-	if err == nil {
27
-		t.Fatal("Unexpected nil err")
28
-	}
29
-	if !errors.IsNotFound(err) {
30
-		t.Errorf("Expected 'not found' error, got %#v", err)
31
-	}
32
-}
33
-
34
-func TestListImageRepositoryMappings(t *testing.T) {
35
-	imageRegistry := imagetest.NewImageRegistry()
36
-	imageRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
37
-	storage := &ImageRepositoryMappingStorage{imageRegistry, imageRepositoryRegistry}
38
-
39
-	list, err := storage.List(labels.Everything())
40
-	if list != nil {
41
-		t.Errorf("Unexpected non-nil list %#v", list)
42
-	}
43
-	if err == nil {
44
-		t.Fatal("Unexpected nil err")
45
-	}
46
-	if !errors.IsNotFound(err) {
47
-		t.Errorf("Expected 'not found' error, got %#v", err)
48
-	}
49
-}
50
-
51
-func TestDeleteImageRepositoryMapping(t *testing.T) {
52
-	imageRegistry := imagetest.NewImageRegistry()
53
-	imageRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
54
-	storage := &ImageRepositoryMappingStorage{imageRegistry, imageRepositoryRegistry}
55
-
56
-	channel, err := storage.Delete("repo1")
57
-	if channel != nil {
58
-		t.Errorf("Unexpected non-nil channel %#v", channel)
59
-	}
60
-	if err == nil {
61
-		t.Fatal("Unexpected nil err")
62
-	}
63
-	if !errors.IsNotFound(err) {
64
-		t.Errorf("Expected 'not found' error, got %#v", err)
65
-	}
66
-}
67
-
68
-func TestUpdateImageRepositoryMapping(t *testing.T) {
69
-	imageRegistry := imagetest.NewImageRegistry()
70
-	imageRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
71
-	storage := &ImageRepositoryMappingStorage{imageRegistry, imageRepositoryRegistry}
72
-
73
-	channel, err := storage.Update("repo1")
74
-	if channel != nil {
75
-		t.Errorf("Unexpected non-nil channel %#v", channel)
76
-	}
77
-	if err == nil {
78
-		t.Fatal("Unexpected nil err")
79
-	}
80
-	if strings.Index(err.Error(), "ImageRepositoryMappings may not be changed.") == -1 {
81
-		t.Errorf("Expected 'may not be changed' error, got %#v", err)
82
-	}
83
-}
84
-
85
-func TestCreateImageRepositoryMappingBadObject(t *testing.T) {
86
-	imageRegistry := imagetest.NewImageRegistry()
87
-	imageRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
88
-	storage := &ImageRepositoryMappingStorage{imageRegistry, imageRepositoryRegistry}
89
-
90
-	channel, err := storage.Create("bad object")
91
-	if channel != nil {
92
-		t.Errorf("Unexpected non-nil channel %#v", channel)
93
-	}
94
-	if err == nil {
95
-		t.Fatal("Unexpected nil err")
96
-	}
97
-	if strings.Index(err.Error(), "not an image repository mapping") == -1 {
98
-		t.Errorf("Expected 'not an image repository mapping' error, got %#v", err)
99
-	}
100
-}
101
-
102
-func TestCreateImageRepositoryMappingFindError(t *testing.T) {
103
-	imageRegistry := imagetest.NewImageRegistry()
104
-	imageRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
105
-	imageRepositoryRegistry.Err = fmt.Errorf("123")
106
-	storage := &ImageRepositoryMappingStorage{imageRegistry, imageRepositoryRegistry}
107
-
108
-	mapping := api.ImageRepositoryMapping{
109
-		DockerImageRepository: "localhost:5000/someproject/somerepo",
110
-		Image: api.Image{
111
-			JSONBase: kubeapi.JSONBase{
112
-				ID: "imageID1",
113
-			},
114
-			DockerImageReference: "localhost:5000/someproject/somerepo:imageID1",
115
-		},
116
-		Tag: "latest",
117
-	}
118
-
119
-	channel, err := storage.Create(&mapping)
120
-	if channel != nil {
121
-		t.Errorf("Unexpected non-nil channel %#v", channel)
122
-	}
123
-	if err == nil {
124
-		t.Fatal("Unexpected nil err")
125
-	}
126
-	if err.Error() != "123" {
127
-		t.Errorf("Expected 'unable to locate' error, got %#v", err)
128
-	}
129
-}
130
-
131
-func TestCreateImageRepositoryMappingNotFound(t *testing.T) {
132
-	imageRegistry := imagetest.NewImageRegistry()
133
-	imageRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
134
-	imageRepositoryRegistry.ImageRepositories = &api.ImageRepositoryList{
135
-		Items: []api.ImageRepository{
136
-			{
137
-				JSONBase: kubeapi.JSONBase{
138
-					ID: "repo1",
139
-				},
140
-				DockerImageRepository: "localhost:5000/test/repo",
141
-			},
142
-		},
143
-	}
144
-	storage := &ImageRepositoryMappingStorage{imageRegistry, imageRepositoryRegistry}
145
-
146
-	mapping := api.ImageRepositoryMapping{
147
-		DockerImageRepository: "localhost:5000/someproject/somerepo",
148
-		Image: api.Image{
149
-			JSONBase: kubeapi.JSONBase{
150
-				ID: "imageID1",
151
-			},
152
-			DockerImageReference: "localhost:5000/someproject/somerepo:imageID1",
153
-		},
154
-		Tag: "latest",
155
-	}
156
-
157
-	channel, err := storage.Create(&mapping)
158
-	if channel != nil {
159
-		t.Errorf("Unexpected non-nil channel %#v", channel)
160
-	}
161
-	if err == nil {
162
-		t.Fatal("Unexpected nil err")
163
-	}
164
-	if !errors.IsInvalid(err) {
165
-		t.Fatalf("Expected 'invalid' err, got: %#v", err)
166
-	}
167
-}
168
-
169
-func TestCreateImageRepositoryMapping(t *testing.T) {
170
-	imageRegistry := imagetest.NewImageRegistry()
171
-	imageRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
172
-	imageRepositoryRegistry.ImageRepositories = &api.ImageRepositoryList{
173
-		Items: []api.ImageRepository{
174
-			{
175
-				JSONBase: kubeapi.JSONBase{
176
-					ID: "repo1",
177
-				},
178
-				DockerImageRepository: "localhost:5000/someproject/somerepo",
179
-			},
180
-		},
181
-	}
182
-	storage := &ImageRepositoryMappingStorage{imageRegistry, imageRepositoryRegistry}
183
-
184
-	mapping := api.ImageRepositoryMapping{
185
-		DockerImageRepository: "localhost:5000/someproject/somerepo",
186
-		Image: api.Image{
187
-			JSONBase: kubeapi.JSONBase{
188
-				ID: "imageID1",
189
-			},
190
-			DockerImageReference: "localhost:5000/someproject/somerepo:imageID1",
191
-			Metadata: docker.Image{
192
-				Config: &docker.Config{
193
-					Cmd:          []string{"ls", "/"},
194
-					Env:          []string{"a=1"},
195
-					ExposedPorts: map[docker.Port]struct{}{"1234/tcp": {}},
196
-					Memory:       1234,
197
-					CpuShares:    99,
198
-					WorkingDir:   "/workingDir",
199
-				},
200
-			},
201
-		},
202
-		Tag: "latest",
203
-	}
204
-	ch, err := storage.Create(&mapping)
205
-	if err != nil {
206
-		t.Errorf("Unexpected error creating mapping: %#v", err)
207
-	}
208
-
209
-	out := <-ch
210
-	t.Logf("out = '%#v'", out)
211
-
212
-	image, err := imageRegistry.GetImage("imageID1")
213
-	if err != nil {
214
-		t.Errorf("Unexpected error retrieving image: %#v", err)
215
-	}
216
-	if e, a := mapping.Image.DockerImageReference, image.DockerImageReference; e != a {
217
-		t.Errorf("Expected %s, got %s", e, a)
218
-	}
219
-	if !reflect.DeepEqual(mapping.Image.Metadata, image.Metadata) {
220
-		t.Errorf("Expected %#v, got %#v", mapping.Image, image)
221
-	}
222
-
223
-	repo, err := imageRepositoryRegistry.GetImageRepository("repo1")
224
-	if err != nil {
225
-		t.Errorf("Unexpected non-nil err: %#v", err)
226
-	}
227
-	if e, a := "imageID1", repo.Tags["latest"]; e != a {
228
-		t.Errorf("Expected %s, got %s", e, a)
229
-	}
230
-}
231 1
deleted file mode 100644
... ...
@@ -1,109 +0,0 @@
1
-package image
2
-
3
-import (
4
-	"fmt"
5
-
6
-	"code.google.com/p/go-uuid/uuid"
7
-
8
-	baseapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
9
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
10
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
11
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
12
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
13
-	"github.com/openshift/origin/pkg/image/api"
14
-)
15
-
16
-// ImageRepositoryStorage implements the RESTStorage interface in terms of an ImageRepositoryRegistry.
17
-type ImageRepositoryStorage struct {
18
-	registry ImageRepositoryRegistry
19
-}
20
-
21
-// NewImageRepositoryStorage returns a new ImageRepositoryStorage.
22
-func NewImageRepositoryStorage(registry ImageRepositoryRegistry) apiserver.RESTStorage {
23
-	return &ImageRepositoryStorage{registry}
24
-}
25
-
26
-// New returns a new ImageRepository for use with Create and Update.
27
-func (s *ImageRepositoryStorage) New() interface{} {
28
-	return &api.ImageRepository{}
29
-}
30
-
31
-// Get retrieves an ImageRepository by id.
32
-func (s *ImageRepositoryStorage) Get(id string) (interface{}, error) {
33
-	repo, err := s.registry.GetImageRepository(id)
34
-	if err != nil {
35
-		return nil, err
36
-	}
37
-	return repo, nil
38
-}
39
-
40
-// List retrieves a list of ImageRepositories that match selector.
41
-func (s *ImageRepositoryStorage) List(selector labels.Selector) (interface{}, error) {
42
-	imageRepositories, err := s.registry.ListImageRepositories(selector)
43
-	if err != nil {
44
-		return nil, err
45
-	}
46
-	return imageRepositories, err
47
-}
48
-
49
-// Watch begins watching for new, changed, or deleted ImageRepositories.
50
-func (s *ImageRepositoryStorage) Watch(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) {
51
-	return s.registry.WatchImageRepositories(resourceVersion, func(repo *api.ImageRepository) bool {
52
-		fields := labels.Set{
53
-			"ID": repo.ID,
54
-			"DockerImageRepository": repo.DockerImageRepository,
55
-		}
56
-		return label.Matches(labels.Set(repo.Labels)) && field.Matches(fields)
57
-	})
58
-}
59
-
60
-// Create registers the given ImageRepository.
61
-func (s *ImageRepositoryStorage) Create(obj interface{}) (<-chan interface{}, error) {
62
-	repo, ok := obj.(*api.ImageRepository)
63
-	if !ok {
64
-		return nil, fmt.Errorf("not an image repository: %#v", obj)
65
-	}
66
-
67
-	if len(repo.ID) == 0 {
68
-		repo.ID = uuid.NewUUID().String()
69
-	}
70
-
71
-	if repo.Tags == nil {
72
-		repo.Tags = make(map[string]string)
73
-	}
74
-
75
-	repo.CreationTimestamp = util.Now()
76
-
77
-	return apiserver.MakeAsync(func() (interface{}, error) {
78
-		if err := s.registry.CreateImageRepository(repo); err != nil {
79
-			return nil, err
80
-		}
81
-		return s.Get(repo.ID)
82
-	}), nil
83
-}
84
-
85
-// Update replaces an existing ImageRepository in the registry with the given ImageRepository.
86
-func (s *ImageRepositoryStorage) Update(obj interface{}) (<-chan interface{}, error) {
87
-	repo, ok := obj.(*api.ImageRepository)
88
-	if !ok {
89
-		return nil, fmt.Errorf("not an image repository: %#v", obj)
90
-	}
91
-	if len(repo.ID) == 0 {
92
-		return nil, fmt.Errorf("id is unspecified: %#v", repo)
93
-	}
94
-
95
-	return apiserver.MakeAsync(func() (interface{}, error) {
96
-		err := s.registry.UpdateImageRepository(repo)
97
-		if err != nil {
98
-			return nil, err
99
-		}
100
-		return s.Get(repo.ID)
101
-	}), nil
102
-}
103
-
104
-// Delete asynchronously deletes an ImageRepository specified by its id.
105
-func (s *ImageRepositoryStorage) Delete(id string) (<-chan interface{}, error) {
106
-	return apiserver.MakeAsync(func() (interface{}, error) {
107
-		return &baseapi.Status{Status: baseapi.StatusSuccess}, s.registry.DeleteImageRepository(id)
108
-	}), nil
109
-}
110 1
deleted file mode 100644
... ...
@@ -1,254 +0,0 @@
1
-package image
2
-
3
-import (
4
-	"fmt"
5
-	"reflect"
6
-	"strings"
7
-	"testing"
8
-
9
-	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
10
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
11
-	"github.com/openshift/origin/pkg/image/api"
12
-	"github.com/openshift/origin/pkg/image/imagetest"
13
-)
14
-
15
-func TestGetImageRepositoryError(t *testing.T) {
16
-	mockRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
17
-	mockRepositoryRegistry.Err = fmt.Errorf("test error")
18
-	storage := ImageRepositoryStorage{registry: mockRepositoryRegistry}
19
-
20
-	image, err := storage.Get("image1")
21
-	if image != nil {
22
-		t.Errorf("Unexpected non-nil image: %#v", image)
23
-	}
24
-	if err != mockRepositoryRegistry.Err {
25
-		t.Errorf("Expected %#v, got %#v", mockRepositoryRegistry.Err, err)
26
-	}
27
-}
28
-
29
-func TestGetImageRepositoryOK(t *testing.T) {
30
-	mockRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
31
-	mockRepositoryRegistry.ImageRepository = &api.ImageRepository{
32
-		JSONBase:              kubeapi.JSONBase{ID: "foo"},
33
-		DockerImageRepository: "openshift/ruby-19-centos",
34
-	}
35
-	storage := ImageRepositoryStorage{registry: mockRepositoryRegistry}
36
-
37
-	repo, err := storage.Get("foo")
38
-	if repo == nil {
39
-		t.Errorf("Unexpected nil repo: %#v", repo)
40
-	}
41
-	if err != nil {
42
-		t.Errorf("Unexpected non-nil error: %#v", err)
43
-	}
44
-	if e, a := mockRepositoryRegistry.ImageRepository, repo; !reflect.DeepEqual(e, a) {
45
-		t.Errorf("Expected %#v, got %#v", e, a)
46
-	}
47
-}
48
-
49
-func TestListImageRepositoriesError(t *testing.T) {
50
-	mockRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
51
-	mockRepositoryRegistry.Err = fmt.Errorf("test error")
52
-
53
-	storage := ImageRepositoryStorage{
54
-		registry: mockRepositoryRegistry,
55
-	}
56
-
57
-	imageRepositories, err := storage.List(nil)
58
-	if err != mockRepositoryRegistry.Err {
59
-		t.Errorf("Expected %#v, Got %#v", mockRepositoryRegistry.Err, err)
60
-	}
61
-
62
-	if imageRepositories != nil {
63
-		t.Errorf("Unexpected non-nil imageRepositories list: %#v", imageRepositories)
64
-	}
65
-}
66
-
67
-func TestListImageRepositoriesEmptyList(t *testing.T) {
68
-	mockRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
69
-	mockRepositoryRegistry.ImageRepositories = &api.ImageRepositoryList{
70
-		Items: []api.ImageRepository{},
71
-	}
72
-
73
-	storage := ImageRepositoryStorage{
74
-		registry: mockRepositoryRegistry,
75
-	}
76
-
77
-	imageRepositories, err := storage.List(labels.Everything())
78
-	if err != nil {
79
-		t.Errorf("Unexpected non-nil error: %#v", err)
80
-	}
81
-
82
-	if len(imageRepositories.(*api.ImageRepositoryList).Items) != 0 {
83
-		t.Errorf("Unexpected non-zero imageRepositories list: %#v", imageRepositories)
84
-	}
85
-}
86
-
87
-func TestListImageRepositoriesPopulatedList(t *testing.T) {
88
-	mockRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
89
-	mockRepositoryRegistry.ImageRepositories = &api.ImageRepositoryList{
90
-		Items: []api.ImageRepository{
91
-			{
92
-				JSONBase: kubeapi.JSONBase{
93
-					ID: "foo",
94
-				},
95
-			},
96
-			{
97
-				JSONBase: kubeapi.JSONBase{
98
-					ID: "bar",
99
-				},
100
-			},
101
-		},
102
-	}
103
-
104
-	storage := ImageRepositoryStorage{
105
-		registry: mockRepositoryRegistry,
106
-	}
107
-
108
-	list, err := storage.List(labels.Everything())
109
-	if err != nil {
110
-		t.Errorf("Unexpected non-nil error: %#v", err)
111
-	}
112
-
113
-	imageRepositories := list.(*api.ImageRepositoryList)
114
-
115
-	if e, a := 2, len(imageRepositories.Items); e != a {
116
-		t.Errorf("Expected %v, got %v", e, a)
117
-	}
118
-}
119
-
120
-func TestCreateImageRepositoryBadObject(t *testing.T) {
121
-	storage := ImageRepositoryStorage{}
122
-
123
-	channel, err := storage.Create("hello")
124
-	if channel != nil {
125
-		t.Errorf("Expected nil, got %v", channel)
126
-	}
127
-	if strings.Index(err.Error(), "not an image repository:") == -1 {
128
-		t.Errorf("Expected 'not an image repository' error, got %v", err)
129
-	}
130
-}
131
-
132
-func TestCreateImageRepositoryOK(t *testing.T) {
133
-	mockRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
134
-	storage := ImageRepositoryStorage{registry: mockRepositoryRegistry}
135
-
136
-	channel, err := storage.Create(&api.ImageRepository{})
137
-	if err != nil {
138
-		t.Errorf("Unexpected non-nil error: %#v", err)
139
-	}
140
-
141
-	result := <-channel
142
-	repo, ok := result.(*api.ImageRepository)
143
-	if !ok {
144
-		t.Errorf("Unexpected result: %#v", result)
145
-	}
146
-	if len(repo.ID) == 0 {
147
-		t.Errorf("Expected repo's ID to be set: %#v", repo)
148
-	}
149
-	if repo.CreationTimestamp.IsZero() {
150
-		t.Error("Unexpected zero CreationTimestamp")
151
-	}
152
-}
153
-
154
-func TestCreateImageRepositoryRegistryErrorSaving(t *testing.T) {
155
-	mockRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
156
-	mockRepositoryRegistry.Err = fmt.Errorf("foo")
157
-	storage := ImageRepositoryStorage{registry: mockRepositoryRegistry}
158
-
159
-	channel, err := storage.Create(&api.ImageRepository{})
160
-	if err != nil {
161
-		t.Errorf("Unexpected non-nil error: %#v", err)
162
-	}
163
-	result := <-channel
164
-	status, ok := result.(*kubeapi.Status)
165
-	if !ok {
166
-		t.Errorf("Expected status, got %#v", result)
167
-	}
168
-	if status.Status != "failure" || status.Message != "foo" {
169
-		t.Errorf("Expected status=failure, message=foo, got %#v", status)
170
-	}
171
-}
172
-
173
-func TestUpdateImageRepositoryBadObject(t *testing.T) {
174
-	storage := ImageRepositoryStorage{}
175
-
176
-	channel, err := storage.Update("hello")
177
-	if channel != nil {
178
-		t.Errorf("Expected nil, got %v", channel)
179
-	}
180
-	if strings.Index(err.Error(), "not an image repository:") == -1 {
181
-		t.Errorf("Expected 'not an image repository' error, got %v", err)
182
-	}
183
-}
184
-
185
-func TestUpdateImageRepositoryMissingID(t *testing.T) {
186
-	storage := ImageRepositoryStorage{}
187
-
188
-	channel, err := storage.Update(&api.ImageRepository{})
189
-	if channel != nil {
190
-		t.Errorf("Expected nil, got %v", channel)
191
-	}
192
-	if strings.Index(err.Error(), "id is unspecified:") == -1 {
193
-		t.Errorf("Expected 'id is unspecified' error, got %v", err)
194
-	}
195
-}
196
-
197
-func TestUpdateImageRepositoryRegistryErrorSaving(t *testing.T) {
198
-	mockRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
199
-	mockRepositoryRegistry.Err = fmt.Errorf("foo")
200
-	storage := ImageRepositoryStorage{registry: mockRepositoryRegistry}
201
-
202
-	channel, err := storage.Update(&api.ImageRepository{
203
-		JSONBase: kubeapi.JSONBase{ID: "bar"},
204
-	})
205
-	if err != nil {
206
-		t.Errorf("Unexpected non-nil error: %#v", err)
207
-	}
208
-	result := <-channel
209
-	status, ok := result.(*kubeapi.Status)
210
-	if !ok {
211
-		t.Errorf("Expected status, got %#v", result)
212
-	}
213
-	if status.Status != "failure" || status.Message != "foo" {
214
-		t.Errorf("Expected status=failure, message=foo, got %#v", status)
215
-	}
216
-}
217
-
218
-func TestUpdateImageRepositoryOK(t *testing.T) {
219
-	mockRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
220
-	storage := ImageRepositoryStorage{registry: mockRepositoryRegistry}
221
-
222
-	channel, err := storage.Update(&api.ImageRepository{
223
-		JSONBase: kubeapi.JSONBase{ID: "bar"},
224
-	})
225
-	if err != nil {
226
-		t.Errorf("Unexpected non-nil error: %#v", err)
227
-	}
228
-	result := <-channel
229
-	repo, ok := result.(*api.ImageRepository)
230
-	if !ok {
231
-		t.Errorf("Expected image repository, got %#v", result)
232
-	}
233
-	if repo.ID != "bar" {
234
-		t.Errorf("Unexpected repo returned: %#v", repo)
235
-	}
236
-}
237
-
238
-func TestDeleteImageRepository(t *testing.T) {
239
-	mockRepositoryRegistry := imagetest.NewImageRepositoryRegistry()
240
-	storage := ImageRepositoryStorage{registry: mockRepositoryRegistry}
241
-
242
-	channel, err := storage.Delete("foo")
243
-	if err != nil {
244
-		t.Errorf("Unexpected non-nil error: %#v", err)
245
-	}
246
-	result := <-channel
247
-	status, ok := result.(*kubeapi.Status)
248
-	if !ok {
249
-		t.Errorf("Expected status, got %#v", result)
250
-	}
251
-	if status.Status != "success" {
252
-		t.Errorf("Expected status=success, got %#v", status)
253
-	}
254
-}
255 1
deleted file mode 100644
... ...
@@ -1,79 +0,0 @@
1
-package image
2
-
3
-import (
4
-	"fmt"
5
-
6
-	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
8
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
9
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
10
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
11
-	"github.com/openshift/origin/pkg/image/api"
12
-)
13
-
14
-// ImageStorage implements the RESTStorage interface in terms of an ImageRegistry.
15
-type ImageStorage struct {
16
-	registry ImageRegistry
17
-}
18
-
19
-// NewStorage returns a new ImageStorage.
20
-func NewImageStorage(registry ImageRegistry) apiserver.RESTStorage {
21
-	return &ImageStorage{registry}
22
-}
23
-
24
-// New returns a new Image for use with Create and Update.
25
-func (s *ImageStorage) New() interface{} {
26
-	return &api.Image{}
27
-}
28
-
29
-// Get retrieves an Image by id.
30
-func (s *ImageStorage) Get(id string) (interface{}, error) {
31
-	image, err := s.registry.GetImage(id)
32
-	if err != nil {
33
-		return nil, err
34
-	}
35
-	return image, nil
36
-}
37
-
38
-// List retrieves a list of Images that match selector.
39
-func (s *ImageStorage) List(selector labels.Selector) (interface{}, error) {
40
-	images, err := s.registry.ListImages(selector)
41
-	if err != nil {
42
-		return nil, err
43
-	}
44
-
45
-	return images, nil
46
-}
47
-
48
-// Create registers the given Image.
49
-func (s *ImageStorage) Create(obj interface{}) (<-chan interface{}, error) {
50
-	image, ok := obj.(*api.Image)
51
-	if !ok {
52
-		return nil, fmt.Errorf("not an image: %#v", obj)
53
-	}
54
-
55
-	image.CreationTimestamp = util.Now()
56
-
57
-	if errs := ValidateImage(image); len(errs) > 0 {
58
-		return nil, errors.NewInvalid("image", image.ID, errs)
59
-	}
60
-
61
-	return apiserver.MakeAsync(func() (interface{}, error) {
62
-		if err := s.registry.CreateImage(image); err != nil {
63
-			return nil, err
64
-		}
65
-		return s.Get(image.ID)
66
-	}), nil
67
-}
68
-
69
-// Update is not supported for Images, as they are immutable.
70
-func (s *ImageStorage) Update(obj interface{}) (<-chan interface{}, error) {
71
-	return nil, fmt.Errorf("Images may not be changed.")
72
-}
73
-
74
-// Delete asynchronously deletes an Image specified by its id.
75
-func (s *ImageStorage) Delete(id string) (<-chan interface{}, error) {
76
-	return apiserver.MakeAsync(func() (interface{}, error) {
77
-		return &kubeapi.Status{Status: kubeapi.StatusSuccess}, s.registry.DeleteImage(id)
78
-	}), nil
79
-}
80 1
deleted file mode 100644
... ...
@@ -1,244 +0,0 @@
1
-package image
2
-
3
-import (
4
-	"fmt"
5
-	"strings"
6
-	"testing"
7
-	"time"
8
-
9
-	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
10
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
11
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
12
-	"github.com/openshift/origin/pkg/image/api"
13
-	"github.com/openshift/origin/pkg/image/imagetest"
14
-)
15
-
16
-func TestListImagesError(t *testing.T) {
17
-	mockRegistry := imagetest.NewImageRegistry()
18
-	mockRegistry.Err = fmt.Errorf("test error")
19
-
20
-	storage := ImageStorage{
21
-		registry: mockRegistry,
22
-	}
23
-
24
-	images, err := storage.List(nil)
25
-	if err != mockRegistry.Err {
26
-		t.Errorf("Expected %#v, Got %#v", mockRegistry.Err, err)
27
-	}
28
-
29
-	if images != nil {
30
-		t.Errorf("Unexpected non-nil images list: %#v", images)
31
-	}
32
-}
33
-
34
-func TestListImagesEmptyList(t *testing.T) {
35
-	mockRegistry := imagetest.NewImageRegistry()
36
-	mockRegistry.Images = &api.ImageList{
37
-		Items: []api.Image{},
38
-	}
39
-
40
-	storage := ImageStorage{
41
-		registry: mockRegistry,
42
-	}
43
-
44
-	images, err := storage.List(labels.Everything())
45
-	if err != nil {
46
-		t.Errorf("Unexpected non-nil error: %#v", err)
47
-	}
48
-
49
-	if len(images.(*api.ImageList).Items) != 0 {
50
-		t.Errorf("Unexpected non-zero images list: %#v", images)
51
-	}
52
-}
53
-
54
-func TestListImagesPopulatedList(t *testing.T) {
55
-	mockRegistry := imagetest.NewImageRegistry()
56
-	mockRegistry.Images = &api.ImageList{
57
-		Items: []api.Image{
58
-			{
59
-				JSONBase: kubeapi.JSONBase{
60
-					ID: "foo",
61
-				},
62
-			},
63
-			{
64
-				JSONBase: kubeapi.JSONBase{
65
-					ID: "bar",
66
-				},
67
-			},
68
-		},
69
-	}
70
-
71
-	storage := ImageStorage{
72
-		registry: mockRegistry,
73
-	}
74
-
75
-	list, err := storage.List(labels.Everything())
76
-	if err != nil {
77
-		t.Errorf("Unexpected non-nil error: %#v", err)
78
-	}
79
-
80
-	images := list.(*api.ImageList)
81
-
82
-	if e, a := 2, len(images.Items); e != a {
83
-		t.Errorf("Expected %v, got %v", e, a)
84
-	}
85
-}
86
-
87
-func TestCreateImageBadObject(t *testing.T) {
88
-	storage := ImageStorage{}
89
-
90
-	channel, err := storage.Create("hello")
91
-	if channel != nil {
92
-		t.Errorf("Expected nil, got %v", channel)
93
-	}
94
-	if strings.Index(err.Error(), "not an image:") == -1 {
95
-		t.Errorf("Expected 'not an image' error, got %v", err)
96
-	}
97
-}
98
-
99
-func TestCreateImageMissingID(t *testing.T) {
100
-	storage := ImageStorage{}
101
-
102
-	channel, err := storage.Create(&api.Image{})
103
-	if channel != nil {
104
-		t.Errorf("Expected nil channel, got %v", channel)
105
-	}
106
-	if !errors.IsInvalid(err) {
107
-		t.Errorf("Expected 'invalid' error, got %v", err)
108
-	}
109
-}
110
-
111
-func TestCreateImageRegistrySaveError(t *testing.T) {
112
-	mockRegistry := imagetest.NewImageRegistry()
113
-	mockRegistry.Err = fmt.Errorf("test error")
114
-	storage := ImageStorage{registry: mockRegistry}
115
-
116
-	channel, err := storage.Create(&api.Image{
117
-		JSONBase:             kubeapi.JSONBase{ID: "foo"},
118
-		DockerImageReference: "openshift/ruby-19-centos",
119
-	})
120
-	if channel == nil {
121
-		t.Errorf("Expected nil channel, got %v", channel)
122
-	}
123
-	if err != nil {
124
-		t.Errorf("Unexpected non-nil error: %#v", err)
125
-	}
126
-
127
-	select {
128
-	case result := <-channel:
129
-		status, ok := result.(*kubeapi.Status)
130
-		if !ok {
131
-			t.Errorf("Expected status type, got: %#v", result)
132
-		}
133
-		if status.Status != "failure" || status.Message != "foo" {
134
-			t.Errorf("Expected failure status, got %#V", status)
135
-		}
136
-	case <-time.After(50 * time.Millisecond):
137
-		t.Errorf("Timed out waiting for result")
138
-	default:
139
-	}
140
-}
141
-
142
-func TestCreateImageOK(t *testing.T) {
143
-	mockRegistry := imagetest.NewImageRegistry()
144
-	storage := ImageStorage{registry: mockRegistry}
145
-
146
-	channel, err := storage.Create(&api.Image{
147
-		JSONBase:             kubeapi.JSONBase{ID: "foo"},
148
-		DockerImageReference: "openshift/ruby-19-centos",
149
-	})
150
-	if channel == nil {
151
-		t.Errorf("Expected nil channel, got %v", channel)
152
-	}
153
-	if err != nil {
154
-		t.Errorf("Unexpected non-nil error: %#v", err)
155
-	}
156
-
157
-	select {
158
-	case result := <-channel:
159
-		image, ok := result.(*api.Image)
160
-		if !ok {
161
-			t.Errorf("Expected image type, got: %#v", result)
162
-		}
163
-		if image.ID != "foo" {
164
-			t.Errorf("Unexpected image: %#v", image)
165
-		}
166
-	case <-time.After(50 * time.Millisecond):
167
-		t.Errorf("Timed out waiting for result")
168
-	default:
169
-	}
170
-}
171
-
172
-func TestGetImageError(t *testing.T) {
173
-	mockRegistry := imagetest.NewImageRegistry()
174
-	mockRegistry.Err = fmt.Errorf("bad")
175
-	storage := ImageStorage{registry: mockRegistry}
176
-
177
-	image, err := storage.Get("foo")
178
-	if image != nil {
179
-		t.Errorf("Unexpected non-nil image: %#v", image)
180
-	}
181
-	if err != mockRegistry.Err {
182
-		t.Errorf("Expected %#v, got %#v", mockRegistry.Err, err)
183
-	}
184
-}
185
-
186
-func TestGetImageOK(t *testing.T) {
187
-	mockRegistry := imagetest.NewImageRegistry()
188
-	mockRegistry.Image = &api.Image{
189
-		JSONBase:             kubeapi.JSONBase{ID: "foo"},
190
-		DockerImageReference: "openshift/ruby-19-centos",
191
-	}
192
-	storage := ImageStorage{registry: mockRegistry}
193
-
194
-	image, err := storage.Get("foo")
195
-	if image == nil {
196
-		t.Error("Unexpected nil image")
197
-	}
198
-	if err != nil {
199
-		t.Errorf("Unexpected non-nil error", err)
200
-	}
201
-	if image.(*api.Image).ID != "foo" {
202
-		t.Errorf("Unexpected image: %#v", image)
203
-	}
204
-}
205
-
206
-func TestUpdateImage(t *testing.T) {
207
-	storage := ImageStorage{}
208
-	channel, err := storage.Update(&api.Image{})
209
-	if channel != nil {
210
-		t.Errorf("Unexpected non-nil channel: %#v", channel)
211
-	}
212
-	if err == nil {
213
-		t.Fatal("Unexpected nil err")
214
-	}
215
-	if strings.Index(err.Error(), "Images may not be changed.") == -1 {
216
-		t.Errorf("Expected 'may not be changed' error, got: %#v", err)
217
-	}
218
-}
219
-
220
-func TestDeleteImage(t *testing.T) {
221
-	mockRegistry := imagetest.NewImageRegistry()
222
-	storage := ImageStorage{registry: mockRegistry}
223
-	channel, err := storage.Delete("foo")
224
-	if channel == nil {
225
-		t.Error("Unexpected nil channel")
226
-	}
227
-	if err != nil {
228
-		t.Errorf("Unexpected non-nil error: %#v", err)
229
-	}
230
-
231
-	select {
232
-	case result := <-channel:
233
-		status, ok := result.(*kubeapi.Status)
234
-		if !ok {
235
-			t.Errorf("Expected status type, got: %#v", result)
236
-		}
237
-		if status.Status != "success" {
238
-			t.Errorf("Expected status=success, got: %#v", status)
239
-		}
240
-	case <-time.After(50 * time.Millisecond):
241
-		t.Errorf("Timed out waiting for result")
242
-	default:
243
-	}
244
-}
245 1
deleted file mode 100644
... ...
@@ -1,56 +0,0 @@
1
-package imagetest
2
-
3
-import (
4
-	"sync"
5
-
6
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
7
-	"github.com/openshift/origin/pkg/image/api"
8
-)
9
-
10
-type ImageRegistry struct {
11
-	Err    error
12
-	Image  *api.Image
13
-	Images *api.ImageList
14
-	sync.Mutex
15
-}
16
-
17
-func NewImageRegistry() *ImageRegistry {
18
-	return &ImageRegistry{}
19
-}
20
-
21
-func (r *ImageRegistry) ListImages(selector labels.Selector) (*api.ImageList, error) {
22
-	r.Lock()
23
-	defer r.Unlock()
24
-
25
-	return r.Images, r.Err
26
-}
27
-
28
-func (r *ImageRegistry) GetImage(id string) (*api.Image, error) {
29
-	r.Lock()
30
-	defer r.Unlock()
31
-
32
-	return r.Image, r.Err
33
-}
34
-
35
-func (r *ImageRegistry) CreateImage(image *api.Image) error {
36
-	r.Lock()
37
-	defer r.Unlock()
38
-
39
-	r.Image = image
40
-	return r.Err
41
-}
42
-
43
-func (r *ImageRegistry) UpdateImage(image *api.Image) error {
44
-	r.Lock()
45
-	defer r.Unlock()
46
-
47
-	r.Image = image
48
-	return r.Err
49
-}
50
-
51
-func (r *ImageRegistry) DeleteImage(id string) error {
52
-	r.Lock()
53
-	defer r.Unlock()
54
-
55
-	return r.Err
56
-}
57 1
deleted file mode 100644
... ...
@@ -1,61 +0,0 @@
1
-package imagetest
2
-
3
-import (
4
-	"sync"
5
-
6
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
7
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
8
-	"github.com/openshift/origin/pkg/image/api"
9
-)
10
-
11
-type ImageRepositoryRegistry struct {
12
-	Err               error
13
-	ImageRepository   *api.ImageRepository
14
-	ImageRepositories *api.ImageRepositoryList
15
-	sync.Mutex
16
-}
17
-
18
-func NewImageRepositoryRegistry() *ImageRepositoryRegistry {
19
-	return &ImageRepositoryRegistry{}
20
-}
21
-
22
-func (r *ImageRepositoryRegistry) ListImageRepositories(selector labels.Selector) (*api.ImageRepositoryList, error) {
23
-	r.Lock()
24
-	defer r.Unlock()
25
-
26
-	return r.ImageRepositories, r.Err
27
-}
28
-
29
-func (r *ImageRepositoryRegistry) GetImageRepository(id string) (*api.ImageRepository, error) {
30
-	r.Lock()
31
-	defer r.Unlock()
32
-
33
-	return r.ImageRepository, r.Err
34
-}
35
-
36
-func (r *ImageRepositoryRegistry) WatchImageRepositories(resourceVersion uint64, filter func(repo *api.ImageRepository) bool) (watch.Interface, error) {
37
-	return nil, r.Err
38
-}
39
-
40
-func (r *ImageRepositoryRegistry) CreateImageRepository(repo *api.ImageRepository) error {
41
-	r.Lock()
42
-	defer r.Unlock()
43
-
44
-	r.ImageRepository = repo
45
-	return r.Err
46
-}
47
-
48
-func (r *ImageRepositoryRegistry) UpdateImageRepository(repo *api.ImageRepository) error {
49
-	r.Lock()
50
-	defer r.Unlock()
51
-
52
-	r.ImageRepository = repo
53
-	return r.Err
54
-}
55
-
56
-func (r *ImageRepositoryRegistry) DeleteImageRepository(id string) error {
57
-	r.Lock()
58
-	defer r.Unlock()
59
-
60
-	return r.Err
61
-}
62 1
deleted file mode 100644
... ...
@@ -1,37 +0,0 @@
1
-package image
2
-
3
-import (
4
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
5
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
6
-)
7
-import "github.com/openshift/origin/pkg/image/api"
8
-
9
-// ImageRegistry is an interface for things that know how to store Image objects.
10
-type ImageRegistry interface {
11
-	// ListImages obtains a list of images that match a selector.
12
-	ListImages(selector labels.Selector) (*api.ImageList, error)
13
-	// GetImage retrieves a specific image.
14
-	GetImage(id string) (*api.Image, error)
15
-	// CreateImage creates a new image.
16
-	CreateImage(image *api.Image) error
17
-	// UpdateImage updates an image.
18
-	UpdateImage(image *api.Image) error
19
-	// DeleteImage deletes an image.
20
-	DeleteImage(id string) error
21
-}
22
-
23
-// ImageRepositoryRegistry is an interface for things that know how to store ImageRepository objects.
24
-type ImageRepositoryRegistry interface {
25
-	// ListImageRepositories obtains a list of image repositories that match a selector.
26
-	ListImageRepositories(selector labels.Selector) (*api.ImageRepositoryList, error)
27
-	// GetImageRepository retrieves a specific image repository.
28
-	GetImageRepository(id string) (*api.ImageRepository, error)
29
-	// WatchImageRepositories watches for new/changed/deleted image repositories.
30
-	WatchImageRepositories(resourceVersion uint64, filter func(repo *api.ImageRepository) bool) (watch.Interface, error)
31
-	// CreateImageRepository creates a new image repository.
32
-	CreateImageRepository(repo *api.ImageRepository) error
33
-	// UpdateImageRepository updates an image repository.
34
-	UpdateImageRepository(repo *api.ImageRepository) error
35
-	// DeleteImageRepository deletes an image repository.
36
-	DeleteImageRepository(id string) error
37
-}
38 1
new file mode 100644
... ...
@@ -0,0 +1,152 @@
0
+package etcd
1
+
2
+import (
3
+	"errors"
4
+
5
+	apierrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
10
+	"github.com/golang/glog"
11
+	"github.com/openshift/origin/pkg/image/api"
12
+)
13
+
14
+// Etcd implements ImageRegistry and ImageRepositoryRegistry backed by etcd.
15
+type Etcd struct {
16
+	tools.EtcdHelper
17
+}
18
+
19
+// NewEtcd returns a new Etcd.
20
+func NewEtcd(client tools.EtcdClient) *Etcd {
21
+	registry := &Etcd{
22
+		EtcdHelper: tools.EtcdHelper{
23
+			client,
24
+			runtime.Codec,
25
+			runtime.ResourceVersioner,
26
+		},
27
+	}
28
+
29
+	return registry
30
+}
31
+
32
+// ListImages retrieves a list of images that match selector.
33
+func (r *Etcd) ListImages(selector labels.Selector) (*api.ImageList, error) {
34
+	list := api.ImageList{}
35
+	err := r.ExtractList("/images", &list.Items, &list.ResourceVersion)
36
+	if err != nil {
37
+		return nil, err
38
+	}
39
+	filtered := []api.Image{}
40
+	for _, item := range list.Items {
41
+		if selector.Matches(labels.Set(item.Labels)) {
42
+			filtered = append(filtered, item)
43
+		}
44
+	}
45
+	list.Items = filtered
46
+	return &list, nil
47
+}
48
+
49
+func makeImageKey(id string) string {
50
+	return "/images/" + id
51
+}
52
+
53
+// GetImage retrieves a specific image
54
+func (r *Etcd) GetImage(id string) (*api.Image, error) {
55
+	var image api.Image
56
+	if err := r.ExtractObj(makeImageKey(id), &image, false); err != nil {
57
+		return nil, err
58
+	}
59
+	return &image, nil
60
+}
61
+
62
+// CreateImage creates a new image
63
+func (r *Etcd) CreateImage(image *api.Image) error {
64
+	err := r.CreateObj(makeImageKey(image.ID), image)
65
+	if tools.IsEtcdNodeExist(err) {
66
+		return apierrors.NewAlreadyExists("image", image.ID)
67
+	}
68
+	return err
69
+}
70
+
71
+// UpdateImage updates an existing image
72
+func (r *Etcd) UpdateImage(image *api.Image) error {
73
+	return errors.New("not supported")
74
+}
75
+
76
+// DeleteImage deletes an existing image
77
+func (r *Etcd) DeleteImage(id string) error {
78
+	key := makeImageKey(id)
79
+	err := r.Delete(key, false)
80
+	if tools.IsEtcdNotFound(err) {
81
+		return apierrors.NewNotFound("image", id)
82
+	}
83
+	return err
84
+}
85
+
86
+// ListImageRepositories retrieves a list of ImageRepositories that match selector.
87
+func (r *Etcd) ListImageRepositories(selector labels.Selector) (*api.ImageRepositoryList, error) {
88
+	list := api.ImageRepositoryList{}
89
+	err := r.ExtractList("/imageRepositories", &list.Items, &list.ResourceVersion)
90
+	if err != nil {
91
+		return nil, err
92
+	}
93
+	filtered := []api.ImageRepository{}
94
+	for _, item := range list.Items {
95
+		if selector.Matches(labels.Set(item.Labels)) {
96
+			filtered = append(filtered, item)
97
+		}
98
+	}
99
+	list.Items = filtered
100
+	return &list, nil
101
+}
102
+
103
+func makeImageRepositoryKey(id string) string {
104
+	return "/imageRepositories/" + id
105
+}
106
+
107
+// GetImageRepository retrieves an ImageRepository by id.
108
+func (r *Etcd) GetImageRepository(id string) (*api.ImageRepository, error) {
109
+	var repo api.ImageRepository
110
+	if err := r.ExtractObj(makeImageRepositoryKey(id), &repo, false); err != nil {
111
+		return nil, err
112
+	}
113
+	return &repo, nil
114
+}
115
+
116
+// WatchImageRepositories begins watching for new, changed, or deleted ImageRepositories.
117
+func (r *Etcd) WatchImageRepositories(resourceVersion uint64, filter func(repo *api.ImageRepository) bool) (watch.Interface, error) {
118
+	return r.WatchList("/imageRepositories", resourceVersion, func(obj interface{}) bool {
119
+		repo, ok := obj.(*api.ImageRepository)
120
+		if !ok {
121
+			glog.Errorf("Unexpected object during image repository watch: %#v", obj)
122
+			return false
123
+		}
124
+		return filter(repo)
125
+	})
126
+}
127
+
128
+// CreateImageRepository registers the given ImageRepository.
129
+func (r *Etcd) CreateImageRepository(repo *api.ImageRepository) error {
130
+	err := r.CreateObj(makeImageRepositoryKey(repo.ID), repo)
131
+	if err != nil && tools.IsEtcdNodeExist(err) {
132
+		return apierrors.NewAlreadyExists("imageRepository", repo.ID)
133
+	}
134
+
135
+	return err
136
+}
137
+
138
+// UpdateImageRepository replaces an existing ImageRepository in the registry with the given ImageRepository.
139
+func (r *Etcd) UpdateImageRepository(repo *api.ImageRepository) error {
140
+	return r.SetObj(makeImageRepositoryKey(repo.ID), repo)
141
+}
142
+
143
+// DeleteImageRepository deletes an ImageRepository by id.
144
+func (r *Etcd) DeleteImageRepository(id string) error {
145
+	imageRepositoryKey := makeImageRepositoryKey(id)
146
+	err := r.Delete(imageRepositoryKey, false)
147
+	if err != nil && tools.IsEtcdNotFound(err) {
148
+		return apierrors.NewNotFound("imageRepository", id)
149
+	}
150
+	return err
151
+}
0 152
new file mode 100644
... ...
@@ -0,0 +1,594 @@
0
+package etcd
1
+
2
+import (
3
+	"fmt"
4
+	"reflect"
5
+	"testing"
6
+
7
+	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
11
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
12
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
13
+	"github.com/coreos/go-etcd/etcd"
14
+	"github.com/fsouza/go-dockerclient"
15
+	"github.com/openshift/origin/pkg/image/api"
16
+	_ "github.com/openshift/origin/pkg/image/api/v1beta1"
17
+)
18
+
19
+func NewTestEtcd(client tools.EtcdClient) *Etcd {
20
+	return NewEtcd(client)
21
+}
22
+
23
+func TestEtcdListImagesEmpty(t *testing.T) {
24
+	fakeClient := tools.NewFakeEtcdClient(t)
25
+	key := "/images"
26
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
27
+		R: &etcd.Response{
28
+			Node: &etcd.Node{
29
+				Nodes: []*etcd.Node{},
30
+			},
31
+		},
32
+		E: nil,
33
+	}
34
+	registry := NewTestEtcd(fakeClient)
35
+	images, err := registry.ListImages(labels.Everything())
36
+	if err != nil {
37
+		t.Errorf("unexpected error: %v", err)
38
+	}
39
+
40
+	if len(images.Items) != 0 {
41
+		t.Errorf("Unexpected images list: %#v", images)
42
+	}
43
+}
44
+
45
+func TestEtcdListImagesError(t *testing.T) {
46
+	fakeClient := tools.NewFakeEtcdClient(t)
47
+	key := "/images"
48
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
49
+		R: &etcd.Response{
50
+			Node: nil,
51
+		},
52
+		E: fmt.Errorf("some error"),
53
+	}
54
+	registry := NewTestEtcd(fakeClient)
55
+	images, err := registry.ListImages(labels.Everything())
56
+	if err == nil {
57
+		t.Error("unexpected nil error")
58
+	}
59
+
60
+	if images != nil {
61
+		t.Errorf("Unexpected non-nil images: %#v", images)
62
+	}
63
+}
64
+
65
+func TestEtcdListImagesEverything(t *testing.T) {
66
+	fakeClient := tools.NewFakeEtcdClient(t)
67
+	key := "/images"
68
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
69
+		R: &etcd.Response{
70
+			Node: &etcd.Node{
71
+				Nodes: []*etcd.Node{
72
+					{
73
+						Value: runtime.EncodeOrDie(api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
74
+					},
75
+					{
76
+						Value: runtime.EncodeOrDie(api.Image{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
77
+					},
78
+				},
79
+			},
80
+		},
81
+		E: nil,
82
+	}
83
+	registry := NewTestEtcd(fakeClient)
84
+	images, err := registry.ListImages(labels.Everything())
85
+	if err != nil {
86
+		t.Errorf("unexpected error: %v", err)
87
+	}
88
+
89
+	if len(images.Items) != 2 || images.Items[0].ID != "foo" || images.Items[1].ID != "bar" {
90
+		t.Errorf("Unexpected images list: %#v", images)
91
+	}
92
+}
93
+
94
+func TestEtcdListImagesFiltered(t *testing.T) {
95
+	fakeClient := tools.NewFakeEtcdClient(t)
96
+	key := "/images"
97
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
98
+		R: &etcd.Response{
99
+			Node: &etcd.Node{
100
+				Nodes: []*etcd.Node{
101
+					{
102
+						Value: runtime.EncodeOrDie(api.Image{
103
+							JSONBase: kubeapi.JSONBase{ID: "foo"},
104
+							Labels:   map[string]string{"env": "prod"},
105
+						}),
106
+					},
107
+					{
108
+						Value: runtime.EncodeOrDie(api.Image{
109
+							JSONBase: kubeapi.JSONBase{ID: "bar"},
110
+							Labels:   map[string]string{"env": "dev"},
111
+						}),
112
+					},
113
+				},
114
+			},
115
+		},
116
+		E: nil,
117
+	}
118
+	registry := NewTestEtcd(fakeClient)
119
+	images, err := registry.ListImages(labels.SelectorFromSet(labels.Set{"env": "dev"}))
120
+	if err != nil {
121
+		t.Errorf("unexpected error: %v", err)
122
+	}
123
+
124
+	if len(images.Items) != 1 || images.Items[0].ID != "bar" {
125
+		t.Errorf("Unexpected images list: %#v", images)
126
+	}
127
+}
128
+
129
+func TestEtcdGetImage(t *testing.T) {
130
+	fakeClient := tools.NewFakeEtcdClient(t)
131
+	fakeClient.Set("/images/foo", runtime.EncodeOrDie(api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
132
+	registry := NewTestEtcd(fakeClient)
133
+	image, err := registry.GetImage("foo")
134
+	if err != nil {
135
+		t.Errorf("unexpected error: %v", err)
136
+	}
137
+
138
+	if image.ID != "foo" {
139
+		t.Errorf("Unexpected image: %#v", image)
140
+	}
141
+}
142
+
143
+func TestEtcdGetImageNotFound(t *testing.T) {
144
+	fakeClient := tools.NewFakeEtcdClient(t)
145
+	fakeClient.Data["/images/foo"] = tools.EtcdResponseWithError{
146
+		R: &etcd.Response{
147
+			Node: nil,
148
+		},
149
+		E: tools.EtcdErrorNotFound,
150
+	}
151
+	registry := NewTestEtcd(fakeClient)
152
+	image, err := registry.GetImage("foo")
153
+	if err == nil {
154
+		t.Errorf("Unexpected non-error.")
155
+	}
156
+	if image != nil {
157
+		t.Errorf("Unexpected image: %#v", image)
158
+	}
159
+}
160
+
161
+func TestEtcdCreateImage(t *testing.T) {
162
+	fakeClient := tools.NewFakeEtcdClient(t)
163
+	fakeClient.TestIndex = true
164
+	fakeClient.Data["/images/foo"] = tools.EtcdResponseWithError{
165
+		R: &etcd.Response{
166
+			Node: nil,
167
+		},
168
+		E: tools.EtcdErrorNotFound,
169
+	}
170
+	registry := NewTestEtcd(fakeClient)
171
+	err := registry.CreateImage(&api.Image{
172
+		JSONBase: kubeapi.JSONBase{
173
+			ID: "foo",
174
+		},
175
+		DockerImageReference: "openshift/ruby-19-centos",
176
+		Metadata: docker.Image{
177
+			ID: "abc123",
178
+		},
179
+	})
180
+	if err != nil {
181
+		t.Fatalf("unexpected error: %v", err)
182
+	}
183
+
184
+	resp, err := fakeClient.Get("/images/foo", false, false)
185
+	if err != nil {
186
+		t.Fatalf("Unexpected error %v", err)
187
+	}
188
+	var image api.Image
189
+	err = runtime.DecodeInto([]byte(resp.Node.Value), &image)
190
+	if err != nil {
191
+		t.Errorf("unexpected error: %v", err)
192
+	}
193
+
194
+	if image.ID != "foo" {
195
+		t.Errorf("Unexpected image: %#v %s", image, resp.Node.Value)
196
+	}
197
+
198
+	if e, a := "openshift/ruby-19-centos", image.DockerImageReference; e != a {
199
+		t.Errorf("Expected %v, got %v", e, a)
200
+	}
201
+
202
+	if e, a := "abc123", image.Metadata.ID; e != a {
203
+		t.Errorf("Expected %v, got %v", e, a)
204
+	}
205
+}
206
+
207
+func TestEtcdCreateImageAlreadyExists(t *testing.T) {
208
+	fakeClient := tools.NewFakeEtcdClient(t)
209
+	fakeClient.Data["/images/foo"] = tools.EtcdResponseWithError{
210
+		R: &etcd.Response{
211
+			Node: &etcd.Node{
212
+				Value: runtime.EncodeOrDie(api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
213
+			},
214
+		},
215
+		E: nil,
216
+	}
217
+	registry := NewTestEtcd(fakeClient)
218
+	err := registry.CreateImage(&api.Image{
219
+		JSONBase: kubeapi.JSONBase{
220
+			ID: "foo",
221
+		},
222
+	})
223
+	if err == nil {
224
+		t.Error("Unexpected non-error")
225
+	}
226
+	if !errors.IsAlreadyExists(err) {
227
+		t.Errorf("Expected 'already exists' error, got %#v", err)
228
+	}
229
+}
230
+
231
+func TestEtcdUpdateImage(t *testing.T) {
232
+	fakeClient := tools.NewFakeEtcdClient(t)
233
+	registry := NewTestEtcd(fakeClient)
234
+	err := registry.UpdateImage(&api.Image{})
235
+	if err == nil {
236
+		t.Error("Unexpected non-error")
237
+	}
238
+}
239
+
240
+func TestEtcdDeleteImageNotFound(t *testing.T) {
241
+	fakeClient := tools.NewFakeEtcdClient(t)
242
+	fakeClient.Err = tools.EtcdErrorNotFound
243
+	registry := NewTestEtcd(fakeClient)
244
+	err := registry.DeleteImage("foo")
245
+	if err == nil {
246
+		t.Error("Unexpected non-error")
247
+	}
248
+	if !errors.IsNotFound(err) {
249
+		t.Errorf("Expected 'not found' error, got %#v", err)
250
+	}
251
+}
252
+
253
+func TestEtcdDeleteImageError(t *testing.T) {
254
+	fakeClient := tools.NewFakeEtcdClient(t)
255
+	fakeClient.Err = fmt.Errorf("Some error")
256
+	registry := NewTestEtcd(fakeClient)
257
+	err := registry.DeleteImage("foo")
258
+	if err == nil {
259
+		t.Error("Unexpected non-error")
260
+	}
261
+}
262
+
263
+func TestEtcdDeleteImageOK(t *testing.T) {
264
+	fakeClient := tools.NewFakeEtcdClient(t)
265
+	registry := NewTestEtcd(fakeClient)
266
+	key := "/images/foo"
267
+	err := registry.DeleteImage("foo")
268
+	if err != nil {
269
+		t.Errorf("Unexpected error: %#v", err)
270
+	}
271
+	if len(fakeClient.DeletedKeys) != 1 {
272
+		t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
273
+	} else if fakeClient.DeletedKeys[0] != key {
274
+		t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
275
+	}
276
+}
277
+
278
+func TestEtcdListImagesRepositoriesEmpty(t *testing.T) {
279
+	fakeClient := tools.NewFakeEtcdClient(t)
280
+	key := "/imageRepositories"
281
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
282
+		R: &etcd.Response{
283
+			Node: &etcd.Node{
284
+				Nodes: []*etcd.Node{},
285
+			},
286
+		},
287
+		E: nil,
288
+	}
289
+	registry := NewTestEtcd(fakeClient)
290
+	repos, err := registry.ListImageRepositories(labels.Everything())
291
+	if err != nil {
292
+		t.Errorf("unexpected error: %v", err)
293
+	}
294
+
295
+	if len(repos.Items) != 0 {
296
+		t.Errorf("Unexpected image repositories list: %#v", repos)
297
+	}
298
+}
299
+
300
+func TestEtcdListImageRepositoriesError(t *testing.T) {
301
+	fakeClient := tools.NewFakeEtcdClient(t)
302
+	key := "/imageRepositories"
303
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
304
+		R: &etcd.Response{
305
+			Node: nil,
306
+		},
307
+		E: fmt.Errorf("some error"),
308
+	}
309
+	registry := NewTestEtcd(fakeClient)
310
+	repos, err := registry.ListImageRepositories(labels.Everything())
311
+	if err == nil {
312
+		t.Error("unexpected nil error")
313
+	}
314
+
315
+	if repos != nil {
316
+		t.Errorf("Unexpected non-nil repos: %#v", repos)
317
+	}
318
+}
319
+
320
+func TestEtcdListImageRepositoriesEverything(t *testing.T) {
321
+	fakeClient := tools.NewFakeEtcdClient(t)
322
+	key := "/imageRepositories"
323
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
324
+		R: &etcd.Response{
325
+			Node: &etcd.Node{
326
+				Nodes: []*etcd.Node{
327
+					{
328
+						Value: runtime.EncodeOrDie(api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
329
+					},
330
+					{
331
+						Value: runtime.EncodeOrDie(api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
332
+					},
333
+				},
334
+			},
335
+		},
336
+		E: nil,
337
+	}
338
+	registry := NewTestEtcd(fakeClient)
339
+	repos, err := registry.ListImageRepositories(labels.Everything())
340
+	if err != nil {
341
+		t.Errorf("unexpected error: %v", err)
342
+	}
343
+
344
+	if len(repos.Items) != 2 || repos.Items[0].ID != "foo" || repos.Items[1].ID != "bar" {
345
+		t.Errorf("Unexpected images list: %#v", repos)
346
+	}
347
+}
348
+
349
+func TestEtcdListImageRepositoriesFiltered(t *testing.T) {
350
+	fakeClient := tools.NewFakeEtcdClient(t)
351
+	key := "/imageRepositories"
352
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
353
+		R: &etcd.Response{
354
+			Node: &etcd.Node{
355
+				Nodes: []*etcd.Node{
356
+					{
357
+						Value: runtime.EncodeOrDie(api.ImageRepository{
358
+							JSONBase: kubeapi.JSONBase{ID: "foo"},
359
+							Labels:   map[string]string{"env": "prod"},
360
+						}),
361
+					},
362
+					{
363
+						Value: runtime.EncodeOrDie(api.ImageRepository{
364
+							JSONBase: kubeapi.JSONBase{ID: "bar"},
365
+							Labels:   map[string]string{"env": "dev"},
366
+						}),
367
+					},
368
+				},
369
+			},
370
+		},
371
+		E: nil,
372
+	}
373
+	registry := NewTestEtcd(fakeClient)
374
+	repos, err := registry.ListImageRepositories(labels.SelectorFromSet(labels.Set{"env": "dev"}))
375
+	if err != nil {
376
+		t.Errorf("unexpected error: %v", err)
377
+	}
378
+
379
+	if len(repos.Items) != 1 || repos.Items[0].ID != "bar" {
380
+		t.Errorf("Unexpected repos list: %#v", repos)
381
+	}
382
+}
383
+
384
+func TestEtcdGetImageRepository(t *testing.T) {
385
+	fakeClient := tools.NewFakeEtcdClient(t)
386
+	fakeClient.Set("/imageRepositories/foo", runtime.EncodeOrDie(api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
387
+	registry := NewTestEtcd(fakeClient)
388
+	repo, err := registry.GetImageRepository("foo")
389
+	if err != nil {
390
+		t.Errorf("unexpected error: %v", err)
391
+	}
392
+
393
+	if repo.ID != "foo" {
394
+		t.Errorf("Unexpected repo: %#v", repo)
395
+	}
396
+}
397
+
398
+func TestEtcdGetImageRepositoryNotFound(t *testing.T) {
399
+	fakeClient := tools.NewFakeEtcdClient(t)
400
+	fakeClient.Data["/imageRepositories/foo"] = tools.EtcdResponseWithError{
401
+		R: &etcd.Response{
402
+			Node: nil,
403
+		},
404
+		E: tools.EtcdErrorNotFound,
405
+	}
406
+	registry := NewTestEtcd(fakeClient)
407
+	repo, err := registry.GetImageRepository("foo")
408
+	if err == nil {
409
+		t.Errorf("Unexpected non-error.")
410
+	}
411
+	if repo != nil {
412
+		t.Errorf("Unexpected non-nil repo: %#v", repo)
413
+	}
414
+}
415
+
416
+func TestEtcdCreateImageRepository(t *testing.T) {
417
+	fakeClient := tools.NewFakeEtcdClient(t)
418
+	fakeClient.TestIndex = true
419
+	fakeClient.Data["/imageRepositories/foo"] = tools.EtcdResponseWithError{
420
+		R: &etcd.Response{
421
+			Node: nil,
422
+		},
423
+		E: tools.EtcdErrorNotFound,
424
+	}
425
+	registry := NewTestEtcd(fakeClient)
426
+	err := registry.CreateImageRepository(&api.ImageRepository{
427
+		JSONBase: kubeapi.JSONBase{
428
+			ID: "foo",
429
+		},
430
+		Labels:                map[string]string{"a": "b"},
431
+		DockerImageRepository: "c/d",
432
+		Tags: map[string]string{"t1": "v1"},
433
+	})
434
+	if err != nil {
435
+		t.Fatalf("unexpected error: %v", err)
436
+	}
437
+
438
+	resp, err := fakeClient.Get("/imageRepositories/foo", false, false)
439
+	if err != nil {
440
+		t.Fatalf("Unexpected error %v", err)
441
+	}
442
+	var repo api.ImageRepository
443
+	err = runtime.DecodeInto([]byte(resp.Node.Value), &repo)
444
+	if err != nil {
445
+		t.Errorf("unexpected error: %v", err)
446
+	}
447
+
448
+	if repo.ID != "foo" {
449
+		t.Errorf("Unexpected repo: %#v %s", repo, resp.Node.Value)
450
+	}
451
+
452
+	if len(repo.Labels) != 1 || repo.Labels["a"] != "b" {
453
+		t.Errorf("Unexpected labels: %#v", repo.Labels)
454
+	}
455
+
456
+	if repo.DockerImageRepository != "c/d" {
457
+		t.Errorf("Unexpected docker image repo: %s", repo.DockerImageRepository)
458
+	}
459
+
460
+	if len(repo.Tags) != 1 || repo.Tags["t1"] != "v1" {
461
+		t.Errorf("Unexpected tags: %#v", repo.Tags)
462
+	}
463
+}
464
+
465
+func TestEtcdCreateImageRepositoryAlreadyExists(t *testing.T) {
466
+	fakeClient := tools.NewFakeEtcdClient(t)
467
+	fakeClient.Data["/imageRepositories/foo"] = tools.EtcdResponseWithError{
468
+		R: &etcd.Response{
469
+			Node: &etcd.Node{
470
+				Value: runtime.EncodeOrDie(api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
471
+			},
472
+		},
473
+		E: nil,
474
+	}
475
+	registry := NewTestEtcd(fakeClient)
476
+	err := registry.CreateImageRepository(&api.ImageRepository{
477
+		JSONBase: kubeapi.JSONBase{
478
+			ID: "foo",
479
+		},
480
+	})
481
+	if err == nil {
482
+		t.Error("Unexpected non-error")
483
+	}
484
+	if !errors.IsAlreadyExists(err) {
485
+		t.Errorf("Expected 'already exists' error, got %#v", err)
486
+	}
487
+}
488
+
489
+func TestEtcdUpdateImageRepository(t *testing.T) {
490
+	fakeClient := tools.NewFakeEtcdClient(t)
491
+	fakeClient.TestIndex = true
492
+
493
+	resp, _ := fakeClient.Set("/imageRepositories/foo", runtime.EncodeOrDie(api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
494
+	registry := NewTestEtcd(fakeClient)
495
+	err := registry.UpdateImageRepository(&api.ImageRepository{
496
+		JSONBase:              kubeapi.JSONBase{ID: "foo", ResourceVersion: resp.Node.ModifiedIndex},
497
+		DockerImageRepository: "some/repo",
498
+	})
499
+	if err != nil {
500
+		t.Errorf("unexpected error: %v", err)
501
+	}
502
+
503
+	repo, err := registry.GetImageRepository("foo")
504
+	if repo.DockerImageRepository != "some/repo" {
505
+		t.Errorf("Unexpected repo: %#v", repo)
506
+	}
507
+}
508
+
509
+func TestEtcdDeleteImageRepositoryNotFound(t *testing.T) {
510
+	fakeClient := tools.NewFakeEtcdClient(t)
511
+	fakeClient.Err = tools.EtcdErrorNotFound
512
+	registry := NewTestEtcd(fakeClient)
513
+	err := registry.DeleteImageRepository("foo")
514
+	if err == nil {
515
+		t.Error("Unexpected non-error")
516
+	}
517
+	if !errors.IsNotFound(err) {
518
+		t.Errorf("Expected 'not found' error, got %#v", err)
519
+	}
520
+}
521
+
522
+func TestEtcdDeleteImageRepositoryError(t *testing.T) {
523
+	fakeClient := tools.NewFakeEtcdClient(t)
524
+	fakeClient.Err = fmt.Errorf("Some error")
525
+	registry := NewTestEtcd(fakeClient)
526
+	err := registry.DeleteImageRepository("foo")
527
+	if err == nil {
528
+		t.Error("Unexpected non-error")
529
+	}
530
+}
531
+
532
+func TestEtcdDeleteImageRepositoryOK(t *testing.T) {
533
+	fakeClient := tools.NewFakeEtcdClient(t)
534
+	registry := NewTestEtcd(fakeClient)
535
+	key := "/imageRepositories/foo"
536
+	err := registry.DeleteImageRepository("foo")
537
+	if err != nil {
538
+		t.Errorf("Unexpected error: %#v", err)
539
+	}
540
+	if len(fakeClient.DeletedKeys) != 1 {
541
+		t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
542
+	} else if fakeClient.DeletedKeys[0] != key {
543
+		t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
544
+	}
545
+}
546
+
547
+func TestEtcdWatchImageRepositories(t *testing.T) {
548
+	fakeClient := tools.NewFakeEtcdClient(t)
549
+	registry := NewTestEtcd(fakeClient)
550
+	filterFields := labels.SelectorFromSet(labels.Set{"ID": "foo"})
551
+
552
+	watching, err := registry.WatchImageRepositories(1, func(repo *api.ImageRepository) bool {
553
+		fields := labels.Set{
554
+			"ID": repo.ID,
555
+		}
556
+		return filterFields.Matches(fields)
557
+	})
558
+	if err != nil {
559
+		t.Fatalf("unexpected error: %v", err)
560
+	}
561
+	fakeClient.WaitForWatchCompletion()
562
+
563
+	repo := &api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}
564
+	repoBytes, _ := runtime.Codec.Encode(repo)
565
+	fakeClient.WatchResponse <- &etcd.Response{
566
+		Action: "set",
567
+		Node: &etcd.Node{
568
+			Value: string(repoBytes),
569
+		},
570
+	}
571
+
572
+	event := <-watching.ResultChan()
573
+	if e, a := watch.Added, event.Type; e != a {
574
+		t.Errorf("Expected %v, got %v", e, a)
575
+	}
576
+	if e, a := repo, event.Object; !reflect.DeepEqual(e, a) {
577
+		t.Errorf("Expected %v, got %v", e, a)
578
+	}
579
+
580
+	select {
581
+	case _, ok := <-watching.ResultChan():
582
+		if !ok {
583
+			t.Errorf("watching channel should be open")
584
+		}
585
+	default:
586
+	}
587
+
588
+	fakeClient.WatchInjectError <- nil
589
+	if _, ok := <-watching.ResultChan(); ok {
590
+		t.Errorf("watching channel should be closed")
591
+	}
592
+	watching.Stop()
593
+}
0 594
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+package image
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
4
+	"github.com/openshift/origin/pkg/image/api"
5
+)
6
+
7
+// Registry is an interface for things that know how to store Image objects.
8
+type Registry interface {
9
+	// ListImages obtains a list of images that match a selector.
10
+	ListImages(selector labels.Selector) (*api.ImageList, error)
11
+	// GetImage retrieves a specific image.
12
+	GetImage(id string) (*api.Image, error)
13
+	// CreateImage creates a new image.
14
+	CreateImage(image *api.Image) error
15
+	// UpdateImage updates an image.
16
+	UpdateImage(image *api.Image) error
17
+	// DeleteImage deletes an image.
18
+	DeleteImage(id string) error
19
+}
0 20
new file mode 100644
... ...
@@ -0,0 +1,80 @@
0
+package image
1
+
2
+import (
3
+	"fmt"
4
+
5
+	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
10
+	"github.com/openshift/origin/pkg/image/api"
11
+	"github.com/openshift/origin/pkg/image/api/validation"
12
+)
13
+
14
+// REST implements the RESTStorage interface in terms of an Registry.
15
+type REST struct {
16
+	registry Registry
17
+}
18
+
19
+// NewStorage returns a new REST.
20
+func NewREST(registry Registry) apiserver.RESTStorage {
21
+	return &REST{registry}
22
+}
23
+
24
+// New returns a new Image for use with Create and Update.
25
+func (s *REST) New() interface{} {
26
+	return &api.Image{}
27
+}
28
+
29
+// Get retrieves an Image by id.
30
+func (s *REST) Get(id string) (interface{}, error) {
31
+	image, err := s.registry.GetImage(id)
32
+	if err != nil {
33
+		return nil, err
34
+	}
35
+	return image, nil
36
+}
37
+
38
+// List retrieves a list of Images that match selector.
39
+func (s *REST) List(selector labels.Selector) (interface{}, error) {
40
+	images, err := s.registry.ListImages(selector)
41
+	if err != nil {
42
+		return nil, err
43
+	}
44
+
45
+	return images, nil
46
+}
47
+
48
+// Create registers the given Image.
49
+func (s *REST) Create(obj interface{}) (<-chan interface{}, error) {
50
+	image, ok := obj.(*api.Image)
51
+	if !ok {
52
+		return nil, fmt.Errorf("not an image: %#v", obj)
53
+	}
54
+
55
+	image.CreationTimestamp = util.Now()
56
+
57
+	if errs := validation.ValidateImage(image); len(errs) > 0 {
58
+		return nil, errors.NewInvalid("image", image.ID, errs)
59
+	}
60
+
61
+	return apiserver.MakeAsync(func() (interface{}, error) {
62
+		if err := s.registry.CreateImage(image); err != nil {
63
+			return nil, err
64
+		}
65
+		return s.Get(image.ID)
66
+	}), nil
67
+}
68
+
69
+// Update is not supported for Images, as they are immutable.
70
+func (s *REST) Update(obj interface{}) (<-chan interface{}, error) {
71
+	return nil, fmt.Errorf("Images may not be changed.")
72
+}
73
+
74
+// Delete asynchronously deletes an Image specified by its id.
75
+func (s *REST) Delete(id string) (<-chan interface{}, error) {
76
+	return apiserver.MakeAsync(func() (interface{}, error) {
77
+		return &kubeapi.Status{Status: kubeapi.StatusSuccess}, s.registry.DeleteImage(id)
78
+	}), nil
79
+}
0 80
new file mode 100644
... ...
@@ -0,0 +1,244 @@
0
+package image
1
+
2
+import (
3
+	"fmt"
4
+	"strings"
5
+	"testing"
6
+	"time"
7
+
8
+	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
11
+	"github.com/openshift/origin/pkg/image/api"
12
+	"github.com/openshift/origin/pkg/image/registry/test"
13
+)
14
+
15
+func TestListImagesError(t *testing.T) {
16
+	mockRegistry := test.NewImageRegistry()
17
+	mockRegistry.Err = fmt.Errorf("test error")
18
+
19
+	storage := REST{
20
+		registry: mockRegistry,
21
+	}
22
+
23
+	images, err := storage.List(nil)
24
+	if err != mockRegistry.Err {
25
+		t.Errorf("Expected %#v, Got %#v", mockRegistry.Err, err)
26
+	}
27
+
28
+	if images != nil {
29
+		t.Errorf("Unexpected non-nil images list: %#v", images)
30
+	}
31
+}
32
+
33
+func TestListImagesEmptyList(t *testing.T) {
34
+	mockRegistry := test.NewImageRegistry()
35
+	mockRegistry.Images = &api.ImageList{
36
+		Items: []api.Image{},
37
+	}
38
+
39
+	storage := REST{
40
+		registry: mockRegistry,
41
+	}
42
+
43
+	images, err := storage.List(labels.Everything())
44
+	if err != nil {
45
+		t.Errorf("Unexpected non-nil error: %#v", err)
46
+	}
47
+
48
+	if len(images.(*api.ImageList).Items) != 0 {
49
+		t.Errorf("Unexpected non-zero images list: %#v", images)
50
+	}
51
+}
52
+
53
+func TestListImagesPopulatedList(t *testing.T) {
54
+	mockRegistry := test.NewImageRegistry()
55
+	mockRegistry.Images = &api.ImageList{
56
+		Items: []api.Image{
57
+			{
58
+				JSONBase: kubeapi.JSONBase{
59
+					ID: "foo",
60
+				},
61
+			},
62
+			{
63
+				JSONBase: kubeapi.JSONBase{
64
+					ID: "bar",
65
+				},
66
+			},
67
+		},
68
+	}
69
+
70
+	storage := REST{
71
+		registry: mockRegistry,
72
+	}
73
+
74
+	list, err := storage.List(labels.Everything())
75
+	if err != nil {
76
+		t.Errorf("Unexpected non-nil error: %#v", err)
77
+	}
78
+
79
+	images := list.(*api.ImageList)
80
+
81
+	if e, a := 2, len(images.Items); e != a {
82
+		t.Errorf("Expected %v, got %v", e, a)
83
+	}
84
+}
85
+
86
+func TestCreateImageBadObject(t *testing.T) {
87
+	storage := REST{}
88
+
89
+	channel, err := storage.Create("hello")
90
+	if channel != nil {
91
+		t.Errorf("Expected nil, got %v", channel)
92
+	}
93
+	if strings.Index(err.Error(), "not an image:") == -1 {
94
+		t.Errorf("Expected 'not an image' error, got %v", err)
95
+	}
96
+}
97
+
98
+func TestCreateImageMissingID(t *testing.T) {
99
+	storage := REST{}
100
+
101
+	channel, err := storage.Create(&api.Image{})
102
+	if channel != nil {
103
+		t.Errorf("Expected nil channel, got %v", channel)
104
+	}
105
+	if !errors.IsInvalid(err) {
106
+		t.Errorf("Expected 'invalid' error, got %v", err)
107
+	}
108
+}
109
+
110
+func TestCreateRegistrySaveError(t *testing.T) {
111
+	mockRegistry := test.NewImageRegistry()
112
+	mockRegistry.Err = fmt.Errorf("test error")
113
+	storage := REST{registry: mockRegistry}
114
+
115
+	channel, err := storage.Create(&api.Image{
116
+		JSONBase:             kubeapi.JSONBase{ID: "foo"},
117
+		DockerImageReference: "openshift/ruby-19-centos",
118
+	})
119
+	if channel == nil {
120
+		t.Errorf("Expected nil channel, got %v", channel)
121
+	}
122
+	if err != nil {
123
+		t.Errorf("Unexpected non-nil error: %#v", err)
124
+	}
125
+
126
+	select {
127
+	case result := <-channel:
128
+		status, ok := result.(*kubeapi.Status)
129
+		if !ok {
130
+			t.Errorf("Expected status type, got: %#v", result)
131
+		}
132
+		if status.Status != "failure" || status.Message != "foo" {
133
+			t.Errorf("Expected failure status, got %#V", status)
134
+		}
135
+	case <-time.After(50 * time.Millisecond):
136
+		t.Errorf("Timed out waiting for result")
137
+	default:
138
+	}
139
+}
140
+
141
+func TestCreateImageOK(t *testing.T) {
142
+	mockRegistry := test.NewImageRegistry()
143
+	storage := REST{registry: mockRegistry}
144
+
145
+	channel, err := storage.Create(&api.Image{
146
+		JSONBase:             kubeapi.JSONBase{ID: "foo"},
147
+		DockerImageReference: "openshift/ruby-19-centos",
148
+	})
149
+	if channel == nil {
150
+		t.Errorf("Expected nil channel, got %v", channel)
151
+	}
152
+	if err != nil {
153
+		t.Errorf("Unexpected non-nil error: %#v", err)
154
+	}
155
+
156
+	select {
157
+	case result := <-channel:
158
+		image, ok := result.(*api.Image)
159
+		if !ok {
160
+			t.Errorf("Expected image type, got: %#v", result)
161
+		}
162
+		if image.ID != "foo" {
163
+			t.Errorf("Unexpected image: %#v", image)
164
+		}
165
+	case <-time.After(50 * time.Millisecond):
166
+		t.Errorf("Timed out waiting for result")
167
+	default:
168
+	}
169
+}
170
+
171
+func TestGetImageError(t *testing.T) {
172
+	mockRegistry := test.NewImageRegistry()
173
+	mockRegistry.Err = fmt.Errorf("bad")
174
+	storage := REST{registry: mockRegistry}
175
+
176
+	image, err := storage.Get("foo")
177
+	if image != nil {
178
+		t.Errorf("Unexpected non-nil image: %#v", image)
179
+	}
180
+	if err != mockRegistry.Err {
181
+		t.Errorf("Expected %#v, got %#v", mockRegistry.Err, err)
182
+	}
183
+}
184
+
185
+func TestGetImageOK(t *testing.T) {
186
+	mockRegistry := test.NewImageRegistry()
187
+	mockRegistry.Image = &api.Image{
188
+		JSONBase:             kubeapi.JSONBase{ID: "foo"},
189
+		DockerImageReference: "openshift/ruby-19-centos",
190
+	}
191
+	storage := REST{registry: mockRegistry}
192
+
193
+	image, err := storage.Get("foo")
194
+	if image == nil {
195
+		t.Error("Unexpected nil image")
196
+	}
197
+	if err != nil {
198
+		t.Errorf("Unexpected non-nil error", err)
199
+	}
200
+	if image.(*api.Image).ID != "foo" {
201
+		t.Errorf("Unexpected image: %#v", image)
202
+	}
203
+}
204
+
205
+func TestUpdateImage(t *testing.T) {
206
+	storage := REST{}
207
+	channel, err := storage.Update(&api.Image{})
208
+	if channel != nil {
209
+		t.Errorf("Unexpected non-nil channel: %#v", channel)
210
+	}
211
+	if err == nil {
212
+		t.Fatal("Unexpected nil err")
213
+	}
214
+	if strings.Index(err.Error(), "Images may not be changed.") == -1 {
215
+		t.Errorf("Expected 'may not be changed' error, got: %#v", err)
216
+	}
217
+}
218
+
219
+func TestDeleteImage(t *testing.T) {
220
+	mockRegistry := test.NewImageRegistry()
221
+	storage := REST{registry: mockRegistry}
222
+	channel, err := storage.Delete("foo")
223
+	if channel == nil {
224
+		t.Error("Unexpected nil channel")
225
+	}
226
+	if err != nil {
227
+		t.Errorf("Unexpected non-nil error: %#v", err)
228
+	}
229
+
230
+	select {
231
+	case result := <-channel:
232
+		status, ok := result.(*kubeapi.Status)
233
+		if !ok {
234
+			t.Errorf("Expected status type, got: %#v", result)
235
+		}
236
+		if status.Status != "success" {
237
+			t.Errorf("Expected status=success, got: %#v", status)
238
+		}
239
+	case <-time.After(50 * time.Millisecond):
240
+		t.Errorf("Timed out waiting for result")
241
+	default:
242
+	}
243
+}
0 244
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+package imagerepository
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
5
+	"github.com/openshift/origin/pkg/image/api"
6
+)
7
+
8
+// Registry is an interface for things that know how to store ImageRepository objects.
9
+type Registry interface {
10
+	// ListImageRepositories obtains a list of image repositories that match a selector.
11
+	ListImageRepositories(selector labels.Selector) (*api.ImageRepositoryList, error)
12
+	// GetImageRepository retrieves a specific image repository.
13
+	GetImageRepository(id string) (*api.ImageRepository, error)
14
+	// WatchImageRepositories watches for new/changed/deleted image repositories.
15
+	WatchImageRepositories(resourceVersion uint64, filter func(repo *api.ImageRepository) bool) (watch.Interface, error)
16
+	// CreateImageRepository creates a new image repository.
17
+	CreateImageRepository(repo *api.ImageRepository) error
18
+	// UpdateImageRepository updates an image repository.
19
+	UpdateImageRepository(repo *api.ImageRepository) error
20
+	// DeleteImageRepository deletes an image repository.
21
+	DeleteImageRepository(id string) error
22
+}
0 23
new file mode 100644
... ...
@@ -0,0 +1,109 @@
0
+package imagerepository
1
+
2
+import (
3
+	"fmt"
4
+
5
+	"code.google.com/p/go-uuid/uuid"
6
+
7
+	baseapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
11
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
12
+	"github.com/openshift/origin/pkg/image/api"
13
+)
14
+
15
+// REST implements the RESTStorage interface in terms of an Registry.
16
+type REST struct {
17
+	registry Registry
18
+}
19
+
20
+// NewREST returns a new REST.
21
+func NewREST(registry Registry) apiserver.RESTStorage {
22
+	return &REST{registry}
23
+}
24
+
25
+// New returns a new ImageRepository for use with Create and Update.
26
+func (s *REST) New() interface{} {
27
+	return &api.ImageRepository{}
28
+}
29
+
30
+// Get retrieves an ImageRepository by id.
31
+func (s *REST) Get(id string) (interface{}, error) {
32
+	repo, err := s.registry.GetImageRepository(id)
33
+	if err != nil {
34
+		return nil, err
35
+	}
36
+	return repo, nil
37
+}
38
+
39
+// List retrieves a list of ImageRepositories that match selector.
40
+func (s *REST) List(selector labels.Selector) (interface{}, error) {
41
+	imageRepositories, err := s.registry.ListImageRepositories(selector)
42
+	if err != nil {
43
+		return nil, err
44
+	}
45
+	return imageRepositories, err
46
+}
47
+
48
+// Watch begins watching for new, changed, or deleted ImageRepositories.
49
+func (s *REST) Watch(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) {
50
+	return s.registry.WatchImageRepositories(resourceVersion, func(repo *api.ImageRepository) bool {
51
+		fields := labels.Set{
52
+			"ID": repo.ID,
53
+			"DockerImageRepository": repo.DockerImageRepository,
54
+		}
55
+		return label.Matches(labels.Set(repo.Labels)) && field.Matches(fields)
56
+	})
57
+}
58
+
59
+// Create registers the given ImageRepository.
60
+func (s *REST) Create(obj interface{}) (<-chan interface{}, error) {
61
+	repo, ok := obj.(*api.ImageRepository)
62
+	if !ok {
63
+		return nil, fmt.Errorf("not an image repository: %#v", obj)
64
+	}
65
+
66
+	if len(repo.ID) == 0 {
67
+		repo.ID = uuid.NewUUID().String()
68
+	}
69
+
70
+	if repo.Tags == nil {
71
+		repo.Tags = make(map[string]string)
72
+	}
73
+
74
+	repo.CreationTimestamp = util.Now()
75
+
76
+	return apiserver.MakeAsync(func() (interface{}, error) {
77
+		if err := s.registry.CreateImageRepository(repo); err != nil {
78
+			return nil, err
79
+		}
80
+		return s.Get(repo.ID)
81
+	}), nil
82
+}
83
+
84
+// Update replaces an existing ImageRepository in the registry with the given ImageRepository.
85
+func (s *REST) Update(obj interface{}) (<-chan interface{}, error) {
86
+	repo, ok := obj.(*api.ImageRepository)
87
+	if !ok {
88
+		return nil, fmt.Errorf("not an image repository: %#v", obj)
89
+	}
90
+	if len(repo.ID) == 0 {
91
+		return nil, fmt.Errorf("id is unspecified: %#v", repo)
92
+	}
93
+
94
+	return apiserver.MakeAsync(func() (interface{}, error) {
95
+		err := s.registry.UpdateImageRepository(repo)
96
+		if err != nil {
97
+			return nil, err
98
+		}
99
+		return s.Get(repo.ID)
100
+	}), nil
101
+}
102
+
103
+// Delete asynchronously deletes an ImageRepository specified by its id.
104
+func (s *REST) Delete(id string) (<-chan interface{}, error) {
105
+	return apiserver.MakeAsync(func() (interface{}, error) {
106
+		return &baseapi.Status{Status: baseapi.StatusSuccess}, s.registry.DeleteImageRepository(id)
107
+	}), nil
108
+}
0 109
new file mode 100644
... ...
@@ -0,0 +1,254 @@
0
+package imagerepository
1
+
2
+import (
3
+	"fmt"
4
+	"reflect"
5
+	"strings"
6
+	"testing"
7
+
8
+	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
10
+	"github.com/openshift/origin/pkg/image/api"
11
+	"github.com/openshift/origin/pkg/image/registry/test"
12
+)
13
+
14
+func TestGetImageRepositoryError(t *testing.T) {
15
+	mockRepositoryRegistry := test.NewImageRepositoryRegistry()
16
+	mockRepositoryRegistry.Err = fmt.Errorf("test error")
17
+	storage := REST{registry: mockRepositoryRegistry}
18
+
19
+	image, err := storage.Get("image1")
20
+	if image != nil {
21
+		t.Errorf("Unexpected non-nil image: %#v", image)
22
+	}
23
+	if err != mockRepositoryRegistry.Err {
24
+		t.Errorf("Expected %#v, got %#v", mockRepositoryRegistry.Err, err)
25
+	}
26
+}
27
+
28
+func TestGetImageRepositoryOK(t *testing.T) {
29
+	mockRepositoryRegistry := test.NewImageRepositoryRegistry()
30
+	mockRepositoryRegistry.ImageRepository = &api.ImageRepository{
31
+		JSONBase:              kubeapi.JSONBase{ID: "foo"},
32
+		DockerImageRepository: "openshift/ruby-19-centos",
33
+	}
34
+	storage := REST{registry: mockRepositoryRegistry}
35
+
36
+	repo, err := storage.Get("foo")
37
+	if repo == nil {
38
+		t.Errorf("Unexpected nil repo: %#v", repo)
39
+	}
40
+	if err != nil {
41
+		t.Errorf("Unexpected non-nil error: %#v", err)
42
+	}
43
+	if e, a := mockRepositoryRegistry.ImageRepository, repo; !reflect.DeepEqual(e, a) {
44
+		t.Errorf("Expected %#v, got %#v", e, a)
45
+	}
46
+}
47
+
48
+func TestListImageRepositoriesError(t *testing.T) {
49
+	mockRepositoryRegistry := test.NewImageRepositoryRegistry()
50
+	mockRepositoryRegistry.Err = fmt.Errorf("test error")
51
+
52
+	storage := REST{
53
+		registry: mockRepositoryRegistry,
54
+	}
55
+
56
+	imageRepositories, err := storage.List(nil)
57
+	if err != mockRepositoryRegistry.Err {
58
+		t.Errorf("Expected %#v, Got %#v", mockRepositoryRegistry.Err, err)
59
+	}
60
+
61
+	if imageRepositories != nil {
62
+		t.Errorf("Unexpected non-nil imageRepositories list: %#v", imageRepositories)
63
+	}
64
+}
65
+
66
+func TestListImageRepositoriesEmptyList(t *testing.T) {
67
+	mockRepositoryRegistry := test.NewImageRepositoryRegistry()
68
+	mockRepositoryRegistry.ImageRepositories = &api.ImageRepositoryList{
69
+		Items: []api.ImageRepository{},
70
+	}
71
+
72
+	storage := REST{
73
+		registry: mockRepositoryRegistry,
74
+	}
75
+
76
+	imageRepositories, err := storage.List(labels.Everything())
77
+	if err != nil {
78
+		t.Errorf("Unexpected non-nil error: %#v", err)
79
+	}
80
+
81
+	if len(imageRepositories.(*api.ImageRepositoryList).Items) != 0 {
82
+		t.Errorf("Unexpected non-zero imageRepositories list: %#v", imageRepositories)
83
+	}
84
+}
85
+
86
+func TestListImageRepositoriesPopulatedList(t *testing.T) {
87
+	mockRepositoryRegistry := test.NewImageRepositoryRegistry()
88
+	mockRepositoryRegistry.ImageRepositories = &api.ImageRepositoryList{
89
+		Items: []api.ImageRepository{
90
+			{
91
+				JSONBase: kubeapi.JSONBase{
92
+					ID: "foo",
93
+				},
94
+			},
95
+			{
96
+				JSONBase: kubeapi.JSONBase{
97
+					ID: "bar",
98
+				},
99
+			},
100
+		},
101
+	}
102
+
103
+	storage := REST{
104
+		registry: mockRepositoryRegistry,
105
+	}
106
+
107
+	list, err := storage.List(labels.Everything())
108
+	if err != nil {
109
+		t.Errorf("Unexpected non-nil error: %#v", err)
110
+	}
111
+
112
+	imageRepositories := list.(*api.ImageRepositoryList)
113
+
114
+	if e, a := 2, len(imageRepositories.Items); e != a {
115
+		t.Errorf("Expected %v, got %v", e, a)
116
+	}
117
+}
118
+
119
+func TestCreateImageRepositoryBadObject(t *testing.T) {
120
+	storage := REST{}
121
+
122
+	channel, err := storage.Create("hello")
123
+	if channel != nil {
124
+		t.Errorf("Expected nil, got %v", channel)
125
+	}
126
+	if strings.Index(err.Error(), "not an image repository:") == -1 {
127
+		t.Errorf("Expected 'not an image repository' error, got %v", err)
128
+	}
129
+}
130
+
131
+func TestCreateImageRepositoryOK(t *testing.T) {
132
+	mockRepositoryRegistry := test.NewImageRepositoryRegistry()
133
+	storage := REST{registry: mockRepositoryRegistry}
134
+
135
+	channel, err := storage.Create(&api.ImageRepository{})
136
+	if err != nil {
137
+		t.Errorf("Unexpected non-nil error: %#v", err)
138
+	}
139
+
140
+	result := <-channel
141
+	repo, ok := result.(*api.ImageRepository)
142
+	if !ok {
143
+		t.Errorf("Unexpected result: %#v", result)
144
+	}
145
+	if len(repo.ID) == 0 {
146
+		t.Errorf("Expected repo's ID to be set: %#v", repo)
147
+	}
148
+	if repo.CreationTimestamp.IsZero() {
149
+		t.Error("Unexpected zero CreationTimestamp")
150
+	}
151
+}
152
+
153
+func TestCreateRegistryErrorSaving(t *testing.T) {
154
+	mockRepositoryRegistry := test.NewImageRepositoryRegistry()
155
+	mockRepositoryRegistry.Err = fmt.Errorf("foo")
156
+	storage := REST{registry: mockRepositoryRegistry}
157
+
158
+	channel, err := storage.Create(&api.ImageRepository{})
159
+	if err != nil {
160
+		t.Errorf("Unexpected non-nil error: %#v", err)
161
+	}
162
+	result := <-channel
163
+	status, ok := result.(*kubeapi.Status)
164
+	if !ok {
165
+		t.Errorf("Expected status, got %#v", result)
166
+	}
167
+	if status.Status != "failure" || status.Message != "foo" {
168
+		t.Errorf("Expected status=failure, message=foo, got %#v", status)
169
+	}
170
+}
171
+
172
+func TestUpdateImageRepositoryBadObject(t *testing.T) {
173
+	storage := REST{}
174
+
175
+	channel, err := storage.Update("hello")
176
+	if channel != nil {
177
+		t.Errorf("Expected nil, got %v", channel)
178
+	}
179
+	if strings.Index(err.Error(), "not an image repository:") == -1 {
180
+		t.Errorf("Expected 'not an image repository' error, got %v", err)
181
+	}
182
+}
183
+
184
+func TestUpdateImageRepositoryMissingID(t *testing.T) {
185
+	storage := REST{}
186
+
187
+	channel, err := storage.Update(&api.ImageRepository{})
188
+	if channel != nil {
189
+		t.Errorf("Expected nil, got %v", channel)
190
+	}
191
+	if strings.Index(err.Error(), "id is unspecified:") == -1 {
192
+		t.Errorf("Expected 'id is unspecified' error, got %v", err)
193
+	}
194
+}
195
+
196
+func TestUpdateRegistryErrorSaving(t *testing.T) {
197
+	mockRepositoryRegistry := test.NewImageRepositoryRegistry()
198
+	mockRepositoryRegistry.Err = fmt.Errorf("foo")
199
+	storage := REST{registry: mockRepositoryRegistry}
200
+
201
+	channel, err := storage.Update(&api.ImageRepository{
202
+		JSONBase: kubeapi.JSONBase{ID: "bar"},
203
+	})
204
+	if err != nil {
205
+		t.Errorf("Unexpected non-nil error: %#v", err)
206
+	}
207
+	result := <-channel
208
+	status, ok := result.(*kubeapi.Status)
209
+	if !ok {
210
+		t.Errorf("Expected status, got %#v", result)
211
+	}
212
+	if status.Status != "failure" || status.Message != "foo" {
213
+		t.Errorf("Expected status=failure, message=foo, got %#v", status)
214
+	}
215
+}
216
+
217
+func TestUpdateImageRepositoryOK(t *testing.T) {
218
+	mockRepositoryRegistry := test.NewImageRepositoryRegistry()
219
+	storage := REST{registry: mockRepositoryRegistry}
220
+
221
+	channel, err := storage.Update(&api.ImageRepository{
222
+		JSONBase: kubeapi.JSONBase{ID: "bar"},
223
+	})
224
+	if err != nil {
225
+		t.Errorf("Unexpected non-nil error: %#v", err)
226
+	}
227
+	result := <-channel
228
+	repo, ok := result.(*api.ImageRepository)
229
+	if !ok {
230
+		t.Errorf("Expected image repository, got %#v", result)
231
+	}
232
+	if repo.ID != "bar" {
233
+		t.Errorf("Unexpected repo returned: %#v", repo)
234
+	}
235
+}
236
+
237
+func TestDeleteImageRepository(t *testing.T) {
238
+	mockRepositoryRegistry := test.NewImageRepositoryRegistry()
239
+	storage := REST{registry: mockRepositoryRegistry}
240
+
241
+	channel, err := storage.Delete("foo")
242
+	if err != nil {
243
+		t.Errorf("Unexpected non-nil error: %#v", err)
244
+	}
245
+	result := <-channel
246
+	status, ok := result.(*kubeapi.Status)
247
+	if !ok {
248
+		t.Errorf("Expected status, got %#v", result)
249
+	}
250
+	if status.Status != "success" {
251
+		t.Errorf("Expected status=success, got %#v", status)
252
+	}
253
+}
0 254
new file mode 100644
... ...
@@ -0,0 +1,118 @@
0
+package imagerepositorymapping
1
+
2
+import (
3
+	"fmt"
4
+
5
+	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
10
+	"github.com/openshift/origin/pkg/image/api"
11
+	"github.com/openshift/origin/pkg/image/api/validation"
12
+	"github.com/openshift/origin/pkg/image/registry/image"
13
+	"github.com/openshift/origin/pkg/image/registry/imagerepository"
14
+)
15
+
16
+// REST implements the RESTStorage interface in terms of an Registry and Registry.
17
+// It Only supports the Create method and is used to simply adding a new Image and tag to an ImageRepository.
18
+type REST struct {
19
+	imageRegistry           image.Registry
20
+	imageRepositoryRegistry imagerepository.Registry
21
+}
22
+
23
+// NewREST returns a new REST.
24
+func NewREST(imageRegistry image.Registry, imageRepositoryRegistry imagerepository.Registry) apiserver.RESTStorage {
25
+	return &REST{imageRegistry, imageRepositoryRegistry}
26
+}
27
+
28
+// New returns a new ImageRepositoryMapping for use with Create.
29
+func (s *REST) New() interface{} {
30
+	return &api.ImageRepositoryMapping{}
31
+}
32
+
33
+// Get is not supported.
34
+func (s *REST) Get(id string) (interface{}, error) {
35
+	return nil, errors.NewNotFound("imageRepositoryMapping", id)
36
+}
37
+
38
+// List is not supported.
39
+func (s *REST) List(selector labels.Selector) (interface{}, error) {
40
+	return nil, errors.NewNotFound("imageRepositoryMapping", "list")
41
+}
42
+
43
+// Create registers a new image (if it doesn't exist) and updates the specified ImageRepository's tags.
44
+func (s *REST) Create(obj interface{}) (<-chan interface{}, error) {
45
+	mapping, ok := obj.(*api.ImageRepositoryMapping)
46
+	if !ok {
47
+		return nil, fmt.Errorf("not an image repository mapping: %#v", obj)
48
+	}
49
+
50
+	repo, err := s.findImageRepository(mapping.DockerImageRepository)
51
+	if err != nil {
52
+		return nil, err
53
+	}
54
+	if repo == nil {
55
+		return nil, errors.NewInvalid("imageRepositoryMapping", mapping.ID, errors.ErrorList{
56
+			errors.NewFieldNotFound("DockerImageRepository", mapping.DockerImageRepository),
57
+		})
58
+	}
59
+
60
+	if errs := validation.ValidateImageRepositoryMapping(mapping); len(errs) > 0 {
61
+		return nil, errors.NewInvalid("imageRepositoryMapping", mapping.ID, errs)
62
+	}
63
+
64
+	image := mapping.Image
65
+
66
+	image.CreationTimestamp = util.Now()
67
+
68
+	//TODO apply metadata overrides
69
+
70
+	if repo.Tags == nil {
71
+		repo.Tags = make(map[string]string)
72
+	}
73
+	repo.Tags[mapping.Tag] = image.ID
74
+
75
+	return apiserver.MakeAsync(func() (interface{}, error) {
76
+		err = s.imageRegistry.CreateImage(&image)
77
+		if err != nil && !errors.IsAlreadyExists(err) {
78
+			return nil, err
79
+		}
80
+
81
+		err = s.imageRepositoryRegistry.UpdateImageRepository(repo)
82
+		if err != nil {
83
+			return nil, err
84
+		}
85
+
86
+		return &kubeapi.Status{Status: kubeapi.StatusSuccess}, nil
87
+	}), nil
88
+}
89
+
90
+// findImageRepository retrieves an ImageRepository whose DockerImageRepository matches dockerRepo.
91
+func (s *REST) findImageRepository(dockerRepo string) (*api.ImageRepository, error) {
92
+	//TODO make this more efficient
93
+	list, err := s.imageRepositoryRegistry.ListImageRepositories(labels.Everything())
94
+	if err != nil {
95
+		return nil, err
96
+	}
97
+
98
+	var repo *api.ImageRepository
99
+	for _, r := range list.Items {
100
+		if dockerRepo == r.DockerImageRepository {
101
+			repo = &r
102
+			break
103
+		}
104
+	}
105
+
106
+	return repo, nil
107
+}
108
+
109
+// Update is not supported.
110
+func (s *REST) Update(obj interface{}) (<-chan interface{}, error) {
111
+	return nil, fmt.Errorf("ImageRepositoryMappings may not be changed.")
112
+}
113
+
114
+// Delete is not supported.
115
+func (s *REST) Delete(id string) (<-chan interface{}, error) {
116
+	return nil, errors.NewNotFound("imageRepositoryMapping", id)
117
+}
0 118
new file mode 100644
... ...
@@ -0,0 +1,230 @@
0
+package imagerepositorymapping
1
+
2
+import (
3
+	"fmt"
4
+	"reflect"
5
+	"strings"
6
+	"testing"
7
+
8
+	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
11
+	"github.com/fsouza/go-dockerclient"
12
+	"github.com/openshift/origin/pkg/image/api"
13
+	"github.com/openshift/origin/pkg/image/registry/test"
14
+)
15
+
16
+func TestGetImageRepositoryMapping(t *testing.T) {
17
+	imageRegistry := test.NewImageRegistry()
18
+	imageRepositoryRegistry := test.NewImageRepositoryRegistry()
19
+	storage := &REST{imageRegistry, imageRepositoryRegistry}
20
+
21
+	obj, err := storage.Get("foo")
22
+	if obj != nil {
23
+		t.Errorf("Unexpected non-nil object %#v", obj)
24
+	}
25
+	if err == nil {
26
+		t.Fatal("Unexpected nil err")
27
+	}
28
+	if !errors.IsNotFound(err) {
29
+		t.Errorf("Expected 'not found' error, got %#v", err)
30
+	}
31
+}
32
+
33
+func TestListImageRepositoryMappings(t *testing.T) {
34
+	imageRegistry := test.NewImageRegistry()
35
+	imageRepositoryRegistry := test.NewImageRepositoryRegistry()
36
+	storage := &REST{imageRegistry, imageRepositoryRegistry}
37
+
38
+	list, err := storage.List(labels.Everything())
39
+	if list != nil {
40
+		t.Errorf("Unexpected non-nil list %#v", list)
41
+	}
42
+	if err == nil {
43
+		t.Fatal("Unexpected nil err")
44
+	}
45
+	if !errors.IsNotFound(err) {
46
+		t.Errorf("Expected 'not found' error, got %#v", err)
47
+	}
48
+}
49
+
50
+func TestDeleteImageRepositoryMapping(t *testing.T) {
51
+	imageRegistry := test.NewImageRegistry()
52
+	imageRepositoryRegistry := test.NewImageRepositoryRegistry()
53
+	storage := &REST{imageRegistry, imageRepositoryRegistry}
54
+
55
+	channel, err := storage.Delete("repo1")
56
+	if channel != nil {
57
+		t.Errorf("Unexpected non-nil channel %#v", channel)
58
+	}
59
+	if err == nil {
60
+		t.Fatal("Unexpected nil err")
61
+	}
62
+	if !errors.IsNotFound(err) {
63
+		t.Errorf("Expected 'not found' error, got %#v", err)
64
+	}
65
+}
66
+
67
+func TestUpdateImageRepositoryMapping(t *testing.T) {
68
+	imageRegistry := test.NewImageRegistry()
69
+	imageRepositoryRegistry := test.NewImageRepositoryRegistry()
70
+	storage := &REST{imageRegistry, imageRepositoryRegistry}
71
+
72
+	channel, err := storage.Update("repo1")
73
+	if channel != nil {
74
+		t.Errorf("Unexpected non-nil channel %#v", channel)
75
+	}
76
+	if err == nil {
77
+		t.Fatal("Unexpected nil err")
78
+	}
79
+	if strings.Index(err.Error(), "ImageRepositoryMappings may not be changed.") == -1 {
80
+		t.Errorf("Expected 'may not be changed' error, got %#v", err)
81
+	}
82
+}
83
+
84
+func TestCreateImageRepositoryMappingBadObject(t *testing.T) {
85
+	imageRegistry := test.NewImageRegistry()
86
+	imageRepositoryRegistry := test.NewImageRepositoryRegistry()
87
+	storage := &REST{imageRegistry, imageRepositoryRegistry}
88
+
89
+	channel, err := storage.Create("bad object")
90
+	if channel != nil {
91
+		t.Errorf("Unexpected non-nil channel %#v", channel)
92
+	}
93
+	if err == nil {
94
+		t.Fatal("Unexpected nil err")
95
+	}
96
+	if strings.Index(err.Error(), "not an image repository mapping") == -1 {
97
+		t.Errorf("Expected 'not an image repository mapping' error, got %#v", err)
98
+	}
99
+}
100
+
101
+func TestCreateImageRepositoryMappingFindError(t *testing.T) {
102
+	imageRegistry := test.NewImageRegistry()
103
+	imageRepositoryRegistry := test.NewImageRepositoryRegistry()
104
+	imageRepositoryRegistry.Err = fmt.Errorf("123")
105
+	storage := &REST{imageRegistry, imageRepositoryRegistry}
106
+
107
+	mapping := api.ImageRepositoryMapping{
108
+		DockerImageRepository: "localhost:5000/someproject/somerepo",
109
+		Image: api.Image{
110
+			JSONBase: kubeapi.JSONBase{
111
+				ID: "imageID1",
112
+			},
113
+			DockerImageReference: "localhost:5000/someproject/somerepo:imageID1",
114
+		},
115
+		Tag: "latest",
116
+	}
117
+
118
+	channel, err := storage.Create(&mapping)
119
+	if channel != nil {
120
+		t.Errorf("Unexpected non-nil channel %#v", channel)
121
+	}
122
+	if err == nil {
123
+		t.Fatal("Unexpected nil err")
124
+	}
125
+	if err.Error() != "123" {
126
+		t.Errorf("Expected 'unable to locate' error, got %#v", err)
127
+	}
128
+}
129
+
130
+func TestCreateImageRepositoryMappingNotFound(t *testing.T) {
131
+	imageRegistry := test.NewImageRegistry()
132
+	imageRepositoryRegistry := test.NewImageRepositoryRegistry()
133
+	imageRepositoryRegistry.ImageRepositories = &api.ImageRepositoryList{
134
+		Items: []api.ImageRepository{
135
+			{
136
+				JSONBase: kubeapi.JSONBase{
137
+					ID: "repo1",
138
+				},
139
+				DockerImageRepository: "localhost:5000/test/repo",
140
+			},
141
+		},
142
+	}
143
+	storage := &REST{imageRegistry, imageRepositoryRegistry}
144
+
145
+	mapping := api.ImageRepositoryMapping{
146
+		DockerImageRepository: "localhost:5000/someproject/somerepo",
147
+		Image: api.Image{
148
+			JSONBase: kubeapi.JSONBase{
149
+				ID: "imageID1",
150
+			},
151
+			DockerImageReference: "localhost:5000/someproject/somerepo:imageID1",
152
+		},
153
+		Tag: "latest",
154
+	}
155
+
156
+	channel, err := storage.Create(&mapping)
157
+	if channel != nil {
158
+		t.Errorf("Unexpected non-nil channel %#v", channel)
159
+	}
160
+	if err == nil {
161
+		t.Fatal("Unexpected nil err")
162
+	}
163
+	if !errors.IsInvalid(err) {
164
+		t.Fatalf("Expected 'invalid' err, got: %#v", err)
165
+	}
166
+}
167
+
168
+func TestCreateImageRepositoryMapping(t *testing.T) {
169
+	imageRegistry := test.NewImageRegistry()
170
+	imageRepositoryRegistry := test.NewImageRepositoryRegistry()
171
+	imageRepositoryRegistry.ImageRepositories = &api.ImageRepositoryList{
172
+		Items: []api.ImageRepository{
173
+			{
174
+				JSONBase: kubeapi.JSONBase{
175
+					ID: "repo1",
176
+				},
177
+				DockerImageRepository: "localhost:5000/someproject/somerepo",
178
+			},
179
+		},
180
+	}
181
+	storage := &REST{imageRegistry, imageRepositoryRegistry}
182
+
183
+	mapping := api.ImageRepositoryMapping{
184
+		DockerImageRepository: "localhost:5000/someproject/somerepo",
185
+		Image: api.Image{
186
+			JSONBase: kubeapi.JSONBase{
187
+				ID: "imageID1",
188
+			},
189
+			DockerImageReference: "localhost:5000/someproject/somerepo:imageID1",
190
+			Metadata: docker.Image{
191
+				Config: &docker.Config{
192
+					Cmd:          []string{"ls", "/"},
193
+					Env:          []string{"a=1"},
194
+					ExposedPorts: map[docker.Port]struct{}{"1234/tcp": {}},
195
+					Memory:       1234,
196
+					CpuShares:    99,
197
+					WorkingDir:   "/workingDir",
198
+				},
199
+			},
200
+		},
201
+		Tag: "latest",
202
+	}
203
+	ch, err := storage.Create(&mapping)
204
+	if err != nil {
205
+		t.Errorf("Unexpected error creating mapping: %#v", err)
206
+	}
207
+
208
+	out := <-ch
209
+	t.Logf("out = '%#v'", out)
210
+
211
+	image, err := imageRegistry.GetImage("imageID1")
212
+	if err != nil {
213
+		t.Errorf("Unexpected error retrieving image: %#v", err)
214
+	}
215
+	if e, a := mapping.Image.DockerImageReference, image.DockerImageReference; e != a {
216
+		t.Errorf("Expected %s, got %s", e, a)
217
+	}
218
+	if !reflect.DeepEqual(mapping.Image.Metadata, image.Metadata) {
219
+		t.Errorf("Expected %#v, got %#v", mapping.Image, image)
220
+	}
221
+
222
+	repo, err := imageRepositoryRegistry.GetImageRepository("repo1")
223
+	if err != nil {
224
+		t.Errorf("Unexpected non-nil err: %#v", err)
225
+	}
226
+	if e, a := "imageID1", repo.Tags["latest"]; e != a {
227
+		t.Errorf("Expected %s, got %s", e, a)
228
+	}
229
+}
0 230
new file mode 100644
... ...
@@ -0,0 +1,56 @@
0
+package test
1
+
2
+import (
3
+	"sync"
4
+
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
6
+	"github.com/openshift/origin/pkg/image/api"
7
+)
8
+
9
+type ImageRegistry struct {
10
+	Err    error
11
+	Image  *api.Image
12
+	Images *api.ImageList
13
+	sync.Mutex
14
+}
15
+
16
+func NewImageRegistry() *ImageRegistry {
17
+	return &ImageRegistry{}
18
+}
19
+
20
+func (r *ImageRegistry) ListImages(selector labels.Selector) (*api.ImageList, error) {
21
+	r.Lock()
22
+	defer r.Unlock()
23
+
24
+	return r.Images, r.Err
25
+}
26
+
27
+func (r *ImageRegistry) GetImage(id string) (*api.Image, error) {
28
+	r.Lock()
29
+	defer r.Unlock()
30
+
31
+	return r.Image, r.Err
32
+}
33
+
34
+func (r *ImageRegistry) CreateImage(image *api.Image) error {
35
+	r.Lock()
36
+	defer r.Unlock()
37
+
38
+	r.Image = image
39
+	return r.Err
40
+}
41
+
42
+func (r *ImageRegistry) UpdateImage(image *api.Image) error {
43
+	r.Lock()
44
+	defer r.Unlock()
45
+
46
+	r.Image = image
47
+	return r.Err
48
+}
49
+
50
+func (r *ImageRegistry) DeleteImage(id string) error {
51
+	r.Lock()
52
+	defer r.Unlock()
53
+
54
+	return r.Err
55
+}
0 56
new file mode 100644
... ...
@@ -0,0 +1,61 @@
0
+package test
1
+
2
+import (
3
+	"sync"
4
+
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
7
+	"github.com/openshift/origin/pkg/image/api"
8
+)
9
+
10
+type ImageRepositoryRegistry struct {
11
+	Err               error
12
+	ImageRepository   *api.ImageRepository
13
+	ImageRepositories *api.ImageRepositoryList
14
+	sync.Mutex
15
+}
16
+
17
+func NewImageRepositoryRegistry() *ImageRepositoryRegistry {
18
+	return &ImageRepositoryRegistry{}
19
+}
20
+
21
+func (r *ImageRepositoryRegistry) ListImageRepositories(selector labels.Selector) (*api.ImageRepositoryList, error) {
22
+	r.Lock()
23
+	defer r.Unlock()
24
+
25
+	return r.ImageRepositories, r.Err
26
+}
27
+
28
+func (r *ImageRepositoryRegistry) GetImageRepository(id string) (*api.ImageRepository, error) {
29
+	r.Lock()
30
+	defer r.Unlock()
31
+
32
+	return r.ImageRepository, r.Err
33
+}
34
+
35
+func (r *ImageRepositoryRegistry) WatchImageRepositories(resourceVersion uint64, filter func(repo *api.ImageRepository) bool) (watch.Interface, error) {
36
+	return nil, r.Err
37
+}
38
+
39
+func (r *ImageRepositoryRegistry) CreateImageRepository(repo *api.ImageRepository) error {
40
+	r.Lock()
41
+	defer r.Unlock()
42
+
43
+	r.ImageRepository = repo
44
+	return r.Err
45
+}
46
+
47
+func (r *ImageRepositoryRegistry) UpdateImageRepository(repo *api.ImageRepository) error {
48
+	r.Lock()
49
+	defer r.Unlock()
50
+
51
+	r.ImageRepository = repo
52
+	return r.Err
53
+}
54
+
55
+func (r *ImageRepositoryRegistry) DeleteImageRepository(id string) error {
56
+	r.Lock()
57
+	defer r.Unlock()
58
+
59
+	return r.Err
60
+}
0 61
deleted file mode 100644
... ...
@@ -1,40 +0,0 @@
1
-package image
2
-
3
-import (
4
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
5
-	"github.com/openshift/origin/pkg/image/api"
6
-)
7
-
8
-// ValidateImage tests required fields for an Image.
9
-func ValidateImage(image *api.Image) errors.ErrorList {
10
-	result := errors.ErrorList{}
11
-
12
-	if len(image.ID) == 0 {
13
-		result = append(result, errors.NewFieldRequired("ID", image.ID))
14
-	}
15
-
16
-	if len(image.DockerImageReference) == 0 {
17
-		result = append(result, errors.NewFieldRequired("DockerImageReference", image.DockerImageReference))
18
-	}
19
-
20
-	return result
21
-}
22
-
23
-// ValidateImageRepositoryMapping tests required fields for an ImageRepositoryMapping.
24
-func ValidateImageRepositoryMapping(mapping *api.ImageRepositoryMapping) errors.ErrorList {
25
-	result := errors.ErrorList{}
26
-
27
-	if len(mapping.DockerImageRepository) == 0 {
28
-		result = append(result, errors.NewFieldRequired("DockerImageRepository", mapping.DockerImageRepository))
29
-	}
30
-
31
-	if len(mapping.Tag) == 0 {
32
-		result = append(result, errors.NewFieldRequired("Tag", mapping.Tag))
33
-	}
34
-
35
-	for _, err := range ValidateImage(&mapping.Image).Prefix("image") {
36
-		result = append(result, err)
37
-	}
38
-
39
-	return result
40
-}
41 1
deleted file mode 100644
... ...
@@ -1,108 +0,0 @@
1
-package image
2
-
3
-import (
4
-	"testing"
5
-
6
-	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
8
-	"github.com/openshift/origin/pkg/image/api"
9
-)
10
-
11
-func TestValidateImageOK(t *testing.T) {
12
-	errs := ValidateImage(&api.Image{
13
-		JSONBase:             kubeapi.JSONBase{ID: "foo"},
14
-		DockerImageReference: "openshift/ruby-19-centos",
15
-	})
16
-	if len(errs) > 0 {
17
-		t.Errorf("Unexpected non-empty error list: %#v", errs)
18
-	}
19
-}
20
-
21
-func TestValidateImageMissingFields(t *testing.T) {
22
-	errorCases := map[string]struct {
23
-		I api.Image
24
-		T errors.ValidationErrorType
25
-		F string
26
-	}{
27
-		"missing ID":                   {api.Image{DockerImageReference: "ref"}, errors.ValidationErrorTypeRequired, "ID"},
28
-		"missing DockerImageReference": {api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}, errors.ValidationErrorTypeRequired, "DockerImageReference"},
29
-	}
30
-
31
-	for k, v := range errorCases {
32
-		errs := ValidateImage(&v.I)
33
-		if len(errs) == 0 {
34
-			t.Errorf("Expected failure for %s", k)
35
-			continue
36
-		}
37
-		for i := range errs {
38
-			if errs[i].(errors.ValidationError).Type != v.T {
39
-				t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
40
-			}
41
-			if errs[i].(errors.ValidationError).Field != v.F {
42
-				t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
43
-			}
44
-		}
45
-	}
46
-}
47
-
48
-func TestValidateImageRepositoryMappingNotOK(t *testing.T) {
49
-	errorCases := map[string]struct {
50
-		I api.ImageRepositoryMapping
51
-		T errors.ValidationErrorType
52
-		F string
53
-	}{
54
-		"missing DockerImageRepository": {
55
-			api.ImageRepositoryMapping{
56
-				Tag: "latest",
57
-				Image: api.Image{
58
-					JSONBase: kubeapi.JSONBase{
59
-						ID: "foo",
60
-					},
61
-					DockerImageReference: "openshift/ruby-19-centos",
62
-				},
63
-			},
64
-			errors.ValidationErrorTypeRequired,
65
-			"DockerImageRepository",
66
-		},
67
-		"missing Tag": {
68
-			api.ImageRepositoryMapping{
69
-				DockerImageRepository: "openshift/ruby-19-centos",
70
-				Image: api.Image{
71
-					JSONBase: kubeapi.JSONBase{
72
-						ID: "foo",
73
-					},
74
-					DockerImageReference: "openshift/ruby-19-centos",
75
-				},
76
-			},
77
-			errors.ValidationErrorTypeRequired,
78
-			"Tag",
79
-		},
80
-		"missing image attributes": {
81
-			api.ImageRepositoryMapping{
82
-				Tag: "latest",
83
-				DockerImageRepository: "openshift/ruby-19-centos",
84
-				Image: api.Image{
85
-					DockerImageReference: "openshift/ruby-19-centos",
86
-				},
87
-			},
88
-			errors.ValidationErrorTypeRequired,
89
-			"image.ID",
90
-		},
91
-	}
92
-
93
-	for k, v := range errorCases {
94
-		errs := ValidateImageRepositoryMapping(&v.I)
95
-		if len(errs) == 0 {
96
-			t.Errorf("Expected failure for %s", k)
97
-			continue
98
-		}
99
-		for i := range errs {
100
-			if errs[i].(errors.ValidationError).Type != v.T {
101
-				t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
102
-			}
103
-			if errs[i].(errors.ValidationError).Field != v.F {
104
-				t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
105
-			}
106
-		}
107
-	}
108
-}