Browse code

fix origin to run on new kubernetes

deads2k authored on 2014/10/22 05:35:50
Showing 93 changed files
... ...
@@ -19,7 +19,7 @@ The OpenShift team periodically publishes binaries to Github on https://github.c
19 19
 
20 20
 The tar file for each platform contains a single binary `openshift` which is the all-in-one OpenShift installation.
21 21
 
22
-* Use `openshift start` to launch the server
22
+* Use `sudo openshift start` to launch the server.  Root access is required to create services due to the need to modify IPTables.  See issue: https://github.com/GoogleCloudPlatform/kubernetes/issues/1859.
23 23
 * Use `openshift kube -h <server> ...` to connect to an OpenShift server
24 24
 * Use `openshift help` to see more about the commands in the binary
25 25
 
... ...
@@ -7,7 +7,7 @@
7 7
         "type": "docker",
8 8
         "sourceURI": "git://github.com/openshift/ruby-hello-world.git",
9 9
         "imageTag":  "openshift/origin-ruby-sample",
10
-        "registry":  "127.0.0.1:5001"
10
+        "registry":  "172.17.17.1:5001"
11 11
     },
12 12
     "secret": "secret101"
13 13
 }
... ...
@@ -54,7 +54,7 @@
54 54
               "version": "v1beta1",
55 55
               "containers": [{
56 56
                 "name": "ruby-helloworld",
57
-                "image": "127.0.0.1:5001/openshift/origin-ruby-sample",
57
+                "image": "172.17.17.1:5001/openshift/origin-ruby-sample",
58 58
                 "env": [
59 59
                   {
60 60
                     "name": "ADMIN_USERNAME",
... ...
@@ -41,7 +41,7 @@ OS_PID=$!
41 41
 wait_for_url "http://localhost:${KUBELET_PORT}/healthz" "kubelet: " 1 30
42 42
 wait_for_url "http://${API_HOST}:${API_PORT}/healthz" "apiserver: "
43 43
 
44
-KUBE_CMD="${GO_OUT}/openshift kube -h http://${API_HOST}:${API_PORT} --expect_version_match"
44
+KUBE_CMD="${GO_OUT}/openshift kube --host=http://${API_HOST}:${API_PORT} --expect_version_match"
45 45
 
46 46
 ${KUBE_CMD} list pods
47 47
 ${KUBE_CMD} -c examples/hello-openshift/hello-pod.json create pods
... ...
@@ -16,6 +16,10 @@ TMPDIR=${TMPDIR:-"/tmp"}
16 16
 ETCD_DATA_DIR=$(mktemp -d ${TMPDIR}/openshift.local.etcd.XXXX)
17 17
 VOLUME_DIR=$(mktemp -d ${TMPDIR}/openshift.local.volumes.XXXX)
18 18
 LOG_DIR=${LOG_DIR:-$(mktemp -d ${TMPDIR}/openshift.local.logs.XXXX)}
19
+API_PORT=${API_PORT:-8080}
20
+API_HOST=${API_HOST:-127.0.0.1}
21
+KUBELET_PORT=${KUBELET_PORT:-10250}
22
+
19 23
 CONFIG_FILE=${LOG_DIR}/appConfig.json
20 24
 FIXTURE_DIR=${HACKDIR}/../examples/sample-app
21 25
 GO_OUT=${HACKDIR}/../_output/go/bin
... ...
@@ -75,7 +79,7 @@ wait_for_command "$openshift kube list pods | grep registrypod | grep Running" $
75 75
 echo "[INFO] Waiting for Docker registry service to start"
76 76
 wait_for_command "$openshift kube list services | grep registrypod"
77 77
 echo "[INFO] Probing the docker-registry"
78
-wait_for_url_timed "http://localhost:5001" "[INFO] Docker registry says: " $((2*TIME_MIN))
78
+wait_for_url_timed "http://172.17.17.1:5001" "[INFO] Docker registry says: " $((2*TIME_MIN))
79 79
 
80 80
 # Define a build configuration
81 81
 echo "[INFO] Create a build config"
... ...
@@ -95,7 +99,7 @@ echo "[INFO] Submitting application template json for processing..."
95 95
 $openshift kube process -c ${FIXTURE_DIR}/application-template.json > $CONFIG_FILE
96 96
 
97 97
 echo "[INFO] Applying application config"
98
-$openshift kube  apply -c $CONFIG_FILE
98
+$openshift kube --host=http://127.0.0.1:8080 apply -c $CONFIG_FILE
99 99
 
100 100
 echo "[INFO] Waiting for frontend pod to start"
101 101
 wait_for_command "$openshift kube list pods | grep frontend | grep Running" $((120*TIME_SEC))
... ...
@@ -104,4 +108,4 @@ echo "[INFO] Waiting for frontend service to start"
104 104
 wait_for_command "$openshift kube list services | grep frontend" $((20*TIME_SEC))
105 105
 
106 106
 echo "[INFO] Waiting for app to start..."
107
-wait_for_url_timed "http://localhost:5432" "[INFO] Frontend says: " $((2*TIME_MIN))
107
+wait_for_url_timed "http://172.17.17.2:5432" "[INFO] Frontend says: " $((2*TIME_MIN))
... ...
@@ -32,13 +32,13 @@ var Codec = v1beta1.Codec
32 32
 // ResourceVersioner describes a default versioner that can handle all types
33 33
 // of versioning.
34 34
 // TODO: when versioning changes, make this part of each API definition.
35
-var ResourceVersioner = runtime.NewJSONBaseResourceVersioner()
35
+var ResourceVersioner = runtime.NewTypeMetaResourceVersioner()
36 36
 
37 37
 // SelfLinker can set or get the SelfLink field of all API types.
38 38
 // TODO: when versioning changes, make this part of each API definition.
39 39
 // TODO(lavalamp): Combine SelfLinker & ResourceVersioner interfaces, force all uses
40 40
 // to go through the InterfacesFor method below.
41
-var SelfLinker = runtime.NewJSONBaseSelfLinker()
41
+var SelfLinker = runtime.NewTypeMetaSelfLinker()
42 42
 
43 43
 // VersionInterfaces contains the interfaces one should use for dealing with types of a particular version.
44 44
 type VersionInterfaces struct {
... ...
@@ -7,7 +7,7 @@ import (
7 7
 // Build encapsulates the inputs needed to produce a new deployable image, as well as
8 8
 // the status of the operation and a reference to the Pod which runs the build.
9 9
 type Build struct {
10
-	api.JSONBase `json:",inline" yaml:",inline"`
10
+	api.TypeMeta `json:",inline" yaml:",inline"`
11 11
 	Labels       map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
12 12
 
13 13
 	// Input is the set of inputs used to configure the build
... ...
@@ -57,7 +57,7 @@ type STIBuildInput struct {
57 57
 
58 58
 // BuildConfig contains the inputs needed to produce a new deployable image
59 59
 type BuildConfig struct {
60
-	api.JSONBase `json:",inline" yaml:",inline"`
60
+	api.TypeMeta `json:",inline" yaml:",inline"`
61 61
 	Labels       map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
62 62
 
63 63
 	// DesiredInput is the input used to create builds from this configuration
... ...
@@ -108,12 +108,12 @@ const (
108 108
 
109 109
 // BuildList is a collection of Builds.
110 110
 type BuildList struct {
111
-	api.JSONBase `json:",inline" yaml:",inline"`
111
+	api.TypeMeta `json:",inline" yaml:",inline"`
112 112
 	Items        []Build `json:"items,omitempty" yaml:"items,omitempty"`
113 113
 }
114 114
 
115 115
 // BuildConfigList is a collection of BuildConfigs.
116 116
 type BuildConfigList struct {
117
-	api.JSONBase `json:",inline" yaml:",inline"`
117
+	api.TypeMeta `json:",inline" yaml:",inline"`
118 118
 	Items        []BuildConfig `json:"items,omitempty" yaml:"items,omitempty"`
119 119
 }
... ...
@@ -7,7 +7,7 @@ import (
7 7
 // Build encapsulates the inputs needed to produce a new deployable image, as well as
8 8
 // the status of the operation and a reference to the Pod which runs the build.
9 9
 type Build struct {
10
-	api.JSONBase `json:",inline" yaml:",inline"`
10
+	api.TypeMeta `json:",inline" yaml:",inline"`
11 11
 	Labels       map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
12 12
 
13 13
 	// Input is the set of inputs used to configure the build
... ...
@@ -57,7 +57,7 @@ type STIBuildInput struct {
57 57
 
58 58
 // BuildConfig contains the inputs needed to produce a new deployable image
59 59
 type BuildConfig struct {
60
-	api.JSONBase `json:",inline" yaml:",inline"`
60
+	api.TypeMeta `json:",inline" yaml:",inline"`
61 61
 	Labels       map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
62 62
 
63 63
 	// DesiredInput is the input used to create builds from this configuration
... ...
@@ -108,12 +108,12 @@ const (
108 108
 
109 109
 // BuildList is a collection of Builds.
110 110
 type BuildList struct {
111
-	api.JSONBase `json:",inline" yaml:",inline"`
111
+	api.TypeMeta `json:",inline" yaml:",inline"`
112 112
 	Items        []Build `json:"items,omitempty" yaml:"items,omitempty"`
113 113
 }
114 114
 
115 115
 // BuildConfigList is a collection of BuildConfigs.
116 116
 type BuildConfigList struct {
117
-	api.JSONBase `json:",inline" yaml:",inline"`
117
+	api.TypeMeta `json:",inline" yaml:",inline"`
118 118
 	Items        []BuildConfig `json:"items,omitempty" yaml:"items,omitempty"`
119 119
 }
... ...
@@ -10,7 +10,7 @@ import (
10 10
 
11 11
 func TestBuildValdationSuccess(t *testing.T) {
12 12
 	build := &api.Build{
13
-		JSONBase: kubeapi.JSONBase{ID: "buildID"},
13
+		TypeMeta: kubeapi.TypeMeta{ID: "buildId"},
14 14
 		Input: api.BuildInput{
15 15
 			SourceURI: "http://github.com/my/repository",
16 16
 			ImageTag:  "repository/data",
... ...
@@ -24,7 +24,7 @@ func TestBuildValdationSuccess(t *testing.T) {
24 24
 
25 25
 func TestBuildValidationFailure(t *testing.T) {
26 26
 	build := &api.Build{
27
-		JSONBase: kubeapi.JSONBase{ID: ""},
27
+		TypeMeta: kubeapi.TypeMeta{ID: ""},
28 28
 		Input: api.BuildInput{
29 29
 			SourceURI: "http://github.com/my/repository",
30 30
 			ImageTag:  "repository/data",
... ...
@@ -38,7 +38,7 @@ func TestBuildValidationFailure(t *testing.T) {
38 38
 
39 39
 func TestBuildConfigValidationSuccess(t *testing.T) {
40 40
 	buildConfig := &api.BuildConfig{
41
-		JSONBase: kubeapi.JSONBase{ID: "configID"},
41
+		TypeMeta: kubeapi.TypeMeta{ID: "configId"},
42 42
 		DesiredInput: api.BuildInput{
43 43
 			SourceURI: "http://github.com/my/repository",
44 44
 			ImageTag:  "repository/data",
... ...
@@ -51,7 +51,7 @@ func TestBuildConfigValidationSuccess(t *testing.T) {
51 51
 
52 52
 func TestBuildConfigValidationFailure(t *testing.T) {
53 53
 	buildConfig := &api.BuildConfig{
54
-		JSONBase: kubeapi.JSONBase{ID: ""},
54
+		TypeMeta: kubeapi.TypeMeta{ID: ""},
55 55
 		DesiredInput: api.BuildInput{
56 56
 			SourceURI: "http://github.com/my/repository",
57 57
 			ImageTag:  "repository/data",
... ...
@@ -92,7 +92,7 @@ func hasTimeoutElapsed(build *api.Build, timeout int) bool {
92 92
 // of its associated pod.
93 93
 // TODO: improve handling of illegal state transitions
94 94
 func (bc *BuildController) synchronize(ctx kapi.Context, build *api.Build) (api.BuildStatus, error) {
95
-	glog.Infof("Syncing build %s", build.ID)
95
+	glog.Infof("Syncing build %s: %v", build.ID, build.Status)
96 96
 
97 97
 	buildType := api.DockerBuildType
98 98
 	if build.Input.STIInput != nil {
... ...
@@ -281,7 +281,7 @@ func setup() (buildController *BuildController, build *api.Build, ctx kapi.Conte
281 281
 		timeout:    1000,
282 282
 	}
283 283
 	build = &api.Build{
284
-		JSONBase: kapi.JSONBase{
284
+		TypeMeta: kapi.TypeMeta{
285 285
 			ID: "dataBuild",
286 286
 		},
287 287
 		Input: api.BuildInput{
... ...
@@ -117,7 +117,7 @@ func TestListBuildsError(t *testing.T) {
117 117
 }
118 118
 
119 119
 func TestListEmptyBuildList(t *testing.T) {
120
-	mockRegistry := test.BuildRegistry{Builds: &api.BuildList{JSONBase: kubeapi.JSONBase{ResourceVersion: 1}}}
120
+	mockRegistry := test.BuildRegistry{Builds: &api.BuildList{TypeMeta: kubeapi.TypeMeta{ResourceVersion: "1"}}}
121 121
 	storage := REST{&mockRegistry}
122 122
 	builds, err := storage.List(nil, labels.Everything(), labels.Everything())
123 123
 	if err != nil {
... ...
@@ -127,7 +127,7 @@ func TestListEmptyBuildList(t *testing.T) {
127 127
 	if len(builds.(*api.BuildList).Items) != 0 {
128 128
 		t.Errorf("Unexpected non-zero ctrl list: %#v", builds)
129 129
 	}
130
-	if builds.(*api.BuildList).ResourceVersion != 1 {
130
+	if builds.(*api.BuildList).ResourceVersion != "1" {
131 131
 		t.Errorf("Unexpected resource version: %#v", builds)
132 132
 	}
133 133
 }
... ...
@@ -137,12 +137,12 @@ func TestListBuilds(t *testing.T) {
137 137
 		Builds: &api.BuildList{
138 138
 			Items: []api.Build{
139 139
 				{
140
-					JSONBase: kubeapi.JSONBase{
140
+					TypeMeta: kubeapi.TypeMeta{
141 141
 						ID: "foo",
142 142
 					},
143 143
 				},
144 144
 				{
145
-					JSONBase: kubeapi.JSONBase{
145
+					TypeMeta: kubeapi.TypeMeta{
146 146
 						ID: "bar",
147 147
 					},
148 148
 				},
... ...
@@ -171,7 +171,7 @@ func TestBuildDecode(t *testing.T) {
171 171
 	mockRegistry := test.BuildRegistry{}
172 172
 	storage := REST{&mockRegistry}
173 173
 	build := &api.Build{
174
-		JSONBase: kubeapi.JSONBase{
174
+		TypeMeta: kubeapi.TypeMeta{
175 175
 			ID: "foo",
176 176
 		},
177 177
 	}
... ...
@@ -311,7 +311,7 @@ func TestBuildRESTValidatesCreate(t *testing.T) {
311 311
 	storage := REST{&mockRegistry}
312 312
 	failureCases := map[string]api.Build{
313 313
 		"empty input": {
314
-			JSONBase: kubeapi.JSONBase{ID: "abc"},
314
+			TypeMeta: kubeapi.TypeMeta{ID: "abc"},
315 315
 			Input:    api.BuildInput{},
316 316
 		},
317 317
 	}
... ...
@@ -331,14 +331,14 @@ func TestBuildRESTValidatesUpdate(t *testing.T) {
331 331
 	storage := REST{&mockRegistry}
332 332
 	failureCases := map[string]api.Build{
333 333
 		"empty ID": {
334
-			JSONBase: kubeapi.JSONBase{ID: ""},
334
+			TypeMeta: kubeapi.TypeMeta{ID: ""},
335 335
 			Input: api.BuildInput{
336 336
 				SourceURI: "http://my.build.com/the/build/Dockerfile",
337 337
 				ImageTag:  "repository/dataBuild",
338 338
 			},
339 339
 		},
340 340
 		"empty build input": {
341
-			JSONBase: kubeapi.JSONBase{ID: "abc"},
341
+			TypeMeta: kubeapi.TypeMeta{ID: "abc"},
342 342
 			Input:    api.BuildInput{},
343 343
 		},
344 344
 	}
... ...
@@ -355,7 +355,7 @@ func TestBuildRESTValidatesUpdate(t *testing.T) {
355 355
 
356 356
 func mockBuild() *api.Build {
357 357
 	return &api.Build{
358
-		JSONBase: kubeapi.JSONBase{
358
+		TypeMeta: kubeapi.TypeMeta{
359 359
 			ID: "dataBuild",
360 360
 		},
361 361
 		Input: api.BuildInput{
... ...
@@ -117,7 +117,7 @@ func TestListConfigsError(t *testing.T) {
117 117
 }
118 118
 
119 119
 func TestListEmptyConfigList(t *testing.T) {
120
-	mockRegistry := test.BuildConfigRegistry{BuildConfigs: &api.BuildConfigList{JSONBase: kubeapi.JSONBase{ResourceVersion: 1}}}
120
+	mockRegistry := test.BuildConfigRegistry{BuildConfigs: &api.BuildConfigList{TypeMeta: kubeapi.TypeMeta{ResourceVersion: "1"}}}
121 121
 	storage := REST{&mockRegistry}
122 122
 	buildConfigs, err := storage.List(nil, labels.Everything(), labels.Everything())
123 123
 	if err != nil {
... ...
@@ -127,7 +127,7 @@ func TestListEmptyConfigList(t *testing.T) {
127 127
 	if len(buildConfigs.(*api.BuildConfigList).Items) != 0 {
128 128
 		t.Errorf("Unexpected non-zero ctrl list: %#v", buildConfigs)
129 129
 	}
130
-	if buildConfigs.(*api.BuildConfigList).ResourceVersion != 1 {
130
+	if buildConfigs.(*api.BuildConfigList).ResourceVersion != "1" {
131 131
 		t.Errorf("Unexpected resource version: %#v", buildConfigs)
132 132
 	}
133 133
 }
... ...
@@ -137,12 +137,12 @@ func TestListConfigs(t *testing.T) {
137 137
 		BuildConfigs: &api.BuildConfigList{
138 138
 			Items: []api.BuildConfig{
139 139
 				{
140
-					JSONBase: kubeapi.JSONBase{
140
+					TypeMeta: kubeapi.TypeMeta{
141 141
 						ID: "foo",
142 142
 					},
143 143
 				},
144 144
 				{
145
-					JSONBase: kubeapi.JSONBase{
145
+					TypeMeta: kubeapi.TypeMeta{
146 146
 						ID: "bar",
147 147
 					},
148 148
 				},
... ...
@@ -170,7 +170,7 @@ func TestBuildConfigDecode(t *testing.T) {
170 170
 	mockRegistry := test.BuildConfigRegistry{}
171 171
 	storage := REST{registry: &mockRegistry}
172 172
 	buildConfig := &api.BuildConfig{
173
-		JSONBase: kubeapi.JSONBase{
173
+		TypeMeta: kubeapi.TypeMeta{
174 174
 			ID: "foo",
175 175
 		},
176 176
 	}
... ...
@@ -250,7 +250,7 @@ func TestCreateBuildConfig(t *testing.T) {
250 250
 
251 251
 func mockBuildConfig() *api.BuildConfig {
252 252
 	return &api.BuildConfig{
253
-		JSONBase: kubeapi.JSONBase{
253
+		TypeMeta: kubeapi.TypeMeta{
254 254
 			ID: "dataBuild",
255 255
 		},
256 256
 		DesiredInput: api.BuildInput{
... ...
@@ -318,7 +318,7 @@ func TestBuildConfigRESTValidatesCreate(t *testing.T) {
318 318
 	storage := REST{&mockRegistry}
319 319
 	failureCases := map[string]api.BuildConfig{
320 320
 		"blank sourceURI": {
321
-			JSONBase: kubeapi.JSONBase{ID: "abc"},
321
+			TypeMeta: kubeapi.TypeMeta{ID: "abc"},
322 322
 			DesiredInput: api.BuildInput{
323 323
 				SourceURI: "",
324 324
 				ImageTag:  "data/image",
... ...
@@ -328,14 +328,14 @@ func TestBuildConfigRESTValidatesCreate(t *testing.T) {
328 328
 			},
329 329
 		},
330 330
 		"blank ImageTag": {
331
-			JSONBase: kubeapi.JSONBase{ID: "abc"},
331
+			TypeMeta: kubeapi.TypeMeta{ID: "abc"},
332 332
 			DesiredInput: api.BuildInput{
333 333
 				SourceURI: "http://github.com/test/source",
334 334
 				ImageTag:  "",
335 335
 			},
336 336
 		},
337 337
 		"blank BuilderImage": {
338
-			JSONBase: kubeapi.JSONBase{ID: "abc"},
338
+			TypeMeta: kubeapi.TypeMeta{ID: "abc"},
339 339
 			DesiredInput: api.BuildInput{
340 340
 				SourceURI: "http://github.com/test/source",
341 341
 				ImageTag:  "data/image",
... ...
@@ -361,14 +361,14 @@ func TestBuildRESTValidatesUpdate(t *testing.T) {
361 361
 	storage := REST{&mockRegistry}
362 362
 	failureCases := map[string]api.BuildConfig{
363 363
 		"empty ID": {
364
-			JSONBase: kubeapi.JSONBase{ID: ""},
364
+			TypeMeta: kubeapi.TypeMeta{ID: ""},
365 365
 			DesiredInput: api.BuildInput{
366 366
 				SourceURI: "http://github.com/test/source",
367 367
 				ImageTag:  "data/image",
368 368
 			},
369 369
 		},
370 370
 		"blank sourceURI": {
371
-			JSONBase: kubeapi.JSONBase{ID: "abc"},
371
+			TypeMeta: kubeapi.TypeMeta{ID: "abc"},
372 372
 			DesiredInput: api.BuildInput{
373 373
 				SourceURI: "",
374 374
 				ImageTag:  "data/image",
... ...
@@ -378,14 +378,14 @@ func TestBuildRESTValidatesUpdate(t *testing.T) {
378 378
 			},
379 379
 		},
380 380
 		"blank ImageTag": {
381
-			JSONBase: kubeapi.JSONBase{ID: "abc"},
381
+			TypeMeta: kubeapi.TypeMeta{ID: "abc"},
382 382
 			DesiredInput: api.BuildInput{
383 383
 				SourceURI: "http://github.com/test/source",
384 384
 				ImageTag:  "",
385 385
 			},
386 386
 		},
387 387
 		"blank BuilderImage on STIBuildType": {
388
-			JSONBase: kubeapi.JSONBase{ID: "abc"},
388
+			TypeMeta: kubeapi.TypeMeta{ID: "abc"},
389 389
 			DesiredInput: api.BuildInput{
390 390
 				SourceURI: "http://github.com/test/source",
391 391
 				ImageTag:  "data/image",
... ...
@@ -20,7 +20,7 @@ func (p *podClient) ListPods(ctx kubeapi.Context, selector labels.Selector) (*ku
20 20
 
21 21
 func (p *podClient) GetPod(ctx kubeapi.Context, id string) (*kubeapi.Pod, error) {
22 22
 	pod := &kubeapi.Pod{
23
-		JSONBase:     kubeapi.JSONBase{ID: "foo"},
23
+		TypeMeta:     kubeapi.TypeMeta{ID: "foo"},
24 24
 		DesiredState: kubeapi.PodState{
25 25
 			Manifest: kubeapi.ContainerManifest{
26 26
 				Version: "v1beta1",
... ...
@@ -76,7 +76,7 @@ func TestRegistryResourceLocation(t *testing.T) {
76 76
 
77 77
 func mockBuild(buildStatus api.BuildStatus) *api.Build {
78 78
 	return &api.Build{
79
-		JSONBase: kubeapi.JSONBase{
79
+		TypeMeta: kubeapi.TypeMeta{
80 80
 			ID: "foo-build",
81 81
 		},
82 82
 		Status: buildStatus,
... ...
@@ -27,7 +27,7 @@ func makeBuildKey(id string) string {
27 27
 // ListBuilds obtains a list of Builds.
28 28
 func (r *Etcd) ListBuilds(selector labels.Selector) (*api.BuildList, error) {
29 29
 	allBuilds := api.BuildList{}
30
-	err := r.ExtractList("/registry/builds", &allBuilds.Items, &allBuilds.ResourceVersion)
30
+	err := r.ExtractToList("/registry/builds", &allBuilds)
31 31
 	if err != nil {
32 32
 		return nil, err
33 33
 	}
... ...
@@ -77,7 +77,7 @@ func makeBuildConfigKey(id string) string {
77 77
 // ListBuildConfigs obtains a list of BuildConfigs.
78 78
 func (r *Etcd) ListBuildConfigs(selector labels.Selector) (*api.BuildConfigList, error) {
79 79
 	allConfigs := api.BuildConfigList{}
80
-	err := r.ExtractList("/registry/build-configs", &allConfigs.Items, &allConfigs.ResourceVersion)
80
+	err := r.ExtractToList("/registry/build-configs", &allConfigs)
81 81
 	if err != nil {
82 82
 		return nil, err
83 83
 	}
... ...
@@ -16,12 +16,12 @@ import (
16 16
 )
17 17
 
18 18
 func NewTestEtcd(client tools.EtcdClient) *Etcd {
19
-	return New(tools.EtcdHelper{client, latest.Codec, latest.ResourceVersioner})
19
+	return New(tools.EtcdHelper{client, latest.Codec, tools.RuntimeVersionAdapter{latest.ResourceVersioner}})
20 20
 }
21 21
 
22 22
 func TestEtcdGetBuild(t *testing.T) {
23 23
 	fakeClient := tools.NewFakeEtcdClient(t)
24
-	fakeClient.Set("/registry/builds/foo", runtime.EncodeOrDie(latest.Codec, &api.Build{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
24
+	fakeClient.Set("/registry/builds/foo", runtime.EncodeOrDie(latest.Codec, &api.Build{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}), 0)
25 25
 	registry := NewTestEtcd(fakeClient)
26 26
 	build, err := registry.GetBuild("foo")
27 27
 	if err != nil {
... ...
@@ -57,7 +57,7 @@ func TestEtcdCreateBuild(t *testing.T) {
57 57
 	}
58 58
 	registry := NewTestEtcd(fakeClient)
59 59
 	err := registry.CreateBuild(&api.Build{
60
-		JSONBase: kubeapi.JSONBase{
60
+		TypeMeta: kubeapi.TypeMeta{
61 61
 			ID: "foo",
62 62
 		},
63 63
 		Input: api.BuildInput{
... ...
@@ -94,14 +94,14 @@ func TestEtcdCreateBuildAlreadyExisting(t *testing.T) {
94 94
 	fakeClient.Data["/registry/builds/foo"] = tools.EtcdResponseWithError{
95 95
 		R: &etcd.Response{
96 96
 			Node: &etcd.Node{
97
-				Value: runtime.EncodeOrDie(latest.Codec, &api.Build{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
97
+				Value: runtime.EncodeOrDie(latest.Codec, &api.Build{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
98 98
 			},
99 99
 		},
100 100
 		E: nil,
101 101
 	}
102 102
 	registry := NewTestEtcd(fakeClient)
103 103
 	err := registry.CreateBuild(&api.Build{
104
-		JSONBase: kubeapi.JSONBase{
104
+		TypeMeta: kubeapi.TypeMeta{
105 105
 			ID: "foo",
106 106
 		},
107 107
 	})
... ...
@@ -116,7 +116,7 @@ func TestEtcdDeleteBuild(t *testing.T) {
116 116
 
117 117
 	key := "/registry/builds/foo"
118 118
 	fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, &api.Build{
119
-		JSONBase: kubeapi.JSONBase{ID: "foo"},
119
+		TypeMeta: kubeapi.TypeMeta{ID: "foo"},
120 120
 	}), 0)
121 121
 	registry := NewTestEtcd(fakeClient)
122 122
 	err := registry.DeleteBuild("foo")
... ...
@@ -162,12 +162,12 @@ func TestEtcdListBuilds(t *testing.T) {
162 162
 				Nodes: []*etcd.Node{
163 163
 					{
164 164
 						Value: runtime.EncodeOrDie(latest.Codec, &api.Build{
165
-							JSONBase: kubeapi.JSONBase{ID: "foo"},
165
+							TypeMeta: kubeapi.TypeMeta{ID: "foo"},
166 166
 						}),
167 167
 					},
168 168
 					{
169 169
 						Value: runtime.EncodeOrDie(latest.Codec, &api.Build{
170
-							JSONBase: kubeapi.JSONBase{ID: "bar"},
170
+							TypeMeta: kubeapi.TypeMeta{ID: "bar"},
171 171
 						}),
172 172
 					},
173 173
 				},
... ...
@@ -188,7 +188,7 @@ func TestEtcdListBuilds(t *testing.T) {
188 188
 
189 189
 func TestEtcdGetBuildConfig(t *testing.T) {
190 190
 	fakeClient := tools.NewFakeEtcdClient(t)
191
-	fakeClient.Set("/registry/build-configs/foo", runtime.EncodeOrDie(latest.Codec, &api.BuildConfig{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
191
+	fakeClient.Set("/registry/build-configs/foo", runtime.EncodeOrDie(latest.Codec, &api.BuildConfig{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}), 0)
192 192
 	registry := NewTestEtcd(fakeClient)
193 193
 	buildConfig, err := registry.GetBuildConfig("foo")
194 194
 	if err != nil {
... ...
@@ -224,7 +224,7 @@ func TestEtcdCreateBuildConfig(t *testing.T) {
224 224
 	}
225 225
 	registry := NewTestEtcd(fakeClient)
226 226
 	err := registry.CreateBuildConfig(&api.BuildConfig{
227
-		JSONBase: kubeapi.JSONBase{
227
+		TypeMeta: kubeapi.TypeMeta{
228 228
 			ID: "foo",
229 229
 		},
230 230
 		DesiredInput: api.BuildInput{
... ...
@@ -259,14 +259,14 @@ func TestEtcdCreateBuildConfigAlreadyExisting(t *testing.T) {
259 259
 	fakeClient.Data["/registry/build-configs/foo"] = tools.EtcdResponseWithError{
260 260
 		R: &etcd.Response{
261 261
 			Node: &etcd.Node{
262
-				Value: runtime.EncodeOrDie(latest.Codec, &api.BuildConfig{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
262
+				Value: runtime.EncodeOrDie(latest.Codec, &api.BuildConfig{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
263 263
 			},
264 264
 		},
265 265
 		E: nil,
266 266
 	}
267 267
 	registry := NewTestEtcd(fakeClient)
268 268
 	err := registry.CreateBuildConfig(&api.BuildConfig{
269
-		JSONBase: kubeapi.JSONBase{
269
+		TypeMeta: kubeapi.TypeMeta{
270 270
 			ID: "foo",
271 271
 		},
272 272
 	})
... ...
@@ -281,7 +281,7 @@ func TestEtcdDeleteBuildConfig(t *testing.T) {
281 281
 
282 282
 	key := "/registry/build-configs/foo"
283 283
 	fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, &api.BuildConfig{
284
-		JSONBase: kubeapi.JSONBase{ID: "foo"},
284
+		TypeMeta: kubeapi.TypeMeta{ID: "foo"},
285 285
 	}), 0)
286 286
 	registry := NewTestEtcd(fakeClient)
287 287
 	err := registry.DeleteBuildConfig("foo")
... ...
@@ -327,12 +327,12 @@ func TestEtcdListBuildConfigs(t *testing.T) {
327 327
 				Nodes: []*etcd.Node{
328 328
 					{
329 329
 						Value: runtime.EncodeOrDie(latest.Codec, &api.BuildConfig{
330
-							JSONBase: kubeapi.JSONBase{ID: "foo"},
330
+							TypeMeta: kubeapi.TypeMeta{ID: "foo"},
331 331
 						}),
332 332
 					},
333 333
 					{
334 334
 						Value: runtime.EncodeOrDie(latest.Codec, &api.BuildConfig{
335
-							JSONBase: kubeapi.JSONBase{ID: "bar"},
335
+							TypeMeta: kubeapi.TypeMeta{ID: "bar"},
336 336
 						}),
337 337
 					},
338 338
 				},
... ...
@@ -25,7 +25,7 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*api.Pod,
25 25
 	}
26 26
 
27 27
 	pod := &api.Pod{
28
-		JSONBase: api.JSONBase{
28
+		TypeMeta: api.TypeMeta{
29 29
 			ID: build.PodID,
30 30
 		},
31 31
 		DesiredState: api.PodState{
... ...
@@ -12,8 +12,8 @@ func TestDockerCreateBuildPod(t *testing.T) {
12 12
 	expected := mockDockerBuild()
13 13
 	actual, _ := strategy.CreateBuildPod(expected)
14 14
 
15
-	if actual.JSONBase.ID != expected.PodID {
16
-		t.Errorf("Expected %s, but got %s!", expected.PodID, actual.JSONBase.ID)
15
+	if actual.TypeMeta.ID != expected.PodID {
16
+		t.Errorf("Expected %s, but got %s!", expected.PodID, actual.TypeMeta.ID)
17 17
 	}
18 18
 	if actual.DesiredState.Manifest.Version != "v1beta1" {
19 19
 		t.Error("Expected v1beta1, but got %s!, actual.DesiredState.Manifest.Version")
... ...
@@ -50,7 +50,7 @@ func TestDockerCreateBuildPod(t *testing.T) {
50 50
 
51 51
 func mockDockerBuild() *api.Build {
52 52
 	return &api.Build{
53
-		JSONBase: kubeapi.JSONBase{
53
+		TypeMeta: kubeapi.TypeMeta{
54 54
 			ID: "dockerBuild",
55 55
 		},
56 56
 		Input: api.BuildInput{
... ...
@@ -36,7 +36,7 @@ func NewSTIBuildStrategy(stiBuilderImage string, tc TempDirectoryCreator, useLoc
36 36
 // TODO: Make the Pod definition configurable
37 37
 func (bs *STIBuildStrategy) CreateBuildPod(build *buildapi.Build) (*api.Pod, error) {
38 38
 	pod := &api.Pod{
39
-		JSONBase: api.JSONBase{
39
+		TypeMeta: api.TypeMeta{
40 40
 			ID: build.PodID,
41 41
 		},
42 42
 		DesiredState: api.PodState{
... ...
@@ -18,8 +18,8 @@ func TestSTICreateBuildPod(t *testing.T) {
18 18
 	expected := mockSTIBuild()
19 19
 	actual, _ := strategy.CreateBuildPod(expected)
20 20
 
21
-	if actual.JSONBase.ID != expected.PodID {
22
-		t.Errorf("Expected %s, but got %s!", expected.PodID, actual.JSONBase.ID)
21
+	if actual.TypeMeta.ID != expected.PodID {
22
+		t.Errorf("Expected %s, but got %s!", expected.PodID, actual.TypeMeta.ID)
23 23
 	}
24 24
 	if actual.DesiredState.Manifest.Version != "v1beta1" {
25 25
 		t.Error("Expected v1beta1, but got %s!, actual.DesiredState.Manifest.Version")
... ...
@@ -57,7 +57,7 @@ func TestSTICreateBuildPod(t *testing.T) {
57 57
 
58 58
 func mockSTIBuild() *api.Build {
59 59
 	return &api.Build{
60
-		JSONBase: kubeapi.JSONBase{
60
+		TypeMeta: kubeapi.TypeMeta{
61 61
 			ID: "stiBuild",
62 62
 		},
63 63
 		Input: api.BuildInput{
... ...
@@ -56,7 +56,7 @@ type ImageInterface interface {
56 56
 type ImageRepositoryInterface interface {
57 57
 	ListImageRepositories(ctx api.Context, labels labels.Selector) (*imageapi.ImageRepositoryList, error)
58 58
 	GetImageRepository(ctx api.Context, id string) (*imageapi.ImageRepository, error)
59
-	WatchImageRepositories(ctx api.Context, field, label labels.Selector, resourceVersion uint64) (watch.Interface, error)
59
+	WatchImageRepositories(ctx api.Context, field, label labels.Selector, resourceVersion string) (watch.Interface, error)
60 60
 	CreateImageRepository(ctx api.Context, repo *imageapi.ImageRepository) (*imageapi.ImageRepository, error)
61 61
 	UpdateImageRepository(ctx api.Context, repo *imageapi.ImageRepository) (*imageapi.ImageRepository, error)
62 62
 }
... ...
@@ -69,7 +69,7 @@ type ImageRepositoryMappingInterface interface {
69 69
 // DeploymentConfigInterface contains methods for working with DeploymentConfigs
70 70
 type DeploymentConfigInterface interface {
71 71
 	ListDeploymentConfigs(ctx api.Context, selector labels.Selector) (*deployapi.DeploymentConfigList, error)
72
-	WatchDeploymentConfigs(ctx api.Context, field, label labels.Selector, resourceVersion uint64) (watch.Interface, error)
72
+	WatchDeploymentConfigs(ctx api.Context, field, label labels.Selector, resourceVersion string) (watch.Interface, error)
73 73
 	GetDeploymentConfig(ctx api.Context, id string) (*deployapi.DeploymentConfig, error)
74 74
 	CreateDeploymentConfig(ctx api.Context, config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error)
75 75
 	UpdateDeploymentConfig(ctx api.Context, config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error)
... ...
@@ -84,7 +84,7 @@ type DeploymentInterface interface {
84 84
 	CreateDeployment(ctx api.Context, deployment *deployapi.Deployment) (*deployapi.Deployment, error)
85 85
 	UpdateDeployment(ctx api.Context, deployment *deployapi.Deployment) (*deployapi.Deployment, error)
86 86
 	DeleteDeployment(ctx api.Context, id string) error
87
-	WatchDeployments(ctx api.Context, field, label labels.Selector, resourceVersion uint64) (watch.Interface, error)
87
+	WatchDeployments(ctx api.Context, field, label labels.Selector, resourceVersion string) (watch.Interface, error)
88 88
 }
89 89
 
90 90
 // RouteInterface exposes methods on Route resources
... ...
@@ -94,7 +94,7 @@ type RouteInterface interface {
94 94
 	CreateRoute(ctx api.Context, route *routeapi.Route) (*routeapi.Route, error)
95 95
 	UpdateRoute(ctx api.Context, route *routeapi.Route) (*routeapi.Route, error)
96 96
 	DeleteRoute(ctx api.Context, id string) error
97
-	WatchRoutes(ctx api.Context, label, field labels.Selector, resourceVersion uint64) (watch.Interface, error)
97
+	WatchRoutes(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error)
98 98
 }
99 99
 
100 100
 // Client is an OpenShift client object
... ...
@@ -227,11 +227,11 @@ func (c *Client) GetImageRepository(ctx api.Context, id string) (result *imageap
227 227
 }
228 228
 
229 229
 // WatchImageRepositories returns a watch.Interface that watches the requested imagerepositories.
230
-func (c *Client) WatchImageRepositories(ctx api.Context, field, label labels.Selector, resourceVersion uint64) (watch.Interface, error) {
230
+func (c *Client) WatchImageRepositories(ctx api.Context, field, label labels.Selector, resourceVersion string) (watch.Interface, error) {
231 231
 	return c.Get().
232 232
 		Path("watch").
233 233
 		Path("imageRepositories").
234
-		UintParam("resourceVersion", resourceVersion).
234
+		Param("resourceVersion", resourceVersion).
235 235
 		SelectorParam("labels", label).
236 236
 		SelectorParam("fields", field).
237 237
 		Watch()
... ...
@@ -263,11 +263,11 @@ func (c *Client) ListDeploymentConfigs(ctx api.Context, selector labels.Selector
263 263
 	return
264 264
 }
265 265
 
266
-func (c *Client) WatchDeploymentConfigs(ctx api.Context, field, label labels.Selector, resourceVersion uint64) (watch.Interface, error) {
266
+func (c *Client) WatchDeploymentConfigs(ctx api.Context, field, label labels.Selector, resourceVersion string) (watch.Interface, error) {
267 267
 	return c.Get().
268 268
 		Path("watch").
269 269
 		Path("deploymentConfigs").
270
-		UintParam("resourceVersion", resourceVersion).
270
+		Param("resourceVersion", resourceVersion).
271 271
 		SelectorParam("labels", label).
272 272
 		SelectorParam("fields", field).
273 273
 		Watch()
... ...
@@ -340,11 +340,11 @@ func (c *Client) DeleteDeployment(ctx api.Context, id string) error {
340 340
 }
341 341
 
342 342
 // WatchDeployments returns a watch.Interface that watches the requested deployments.
343
-func (c *Client) WatchDeployments(ctx api.Context, field, label labels.Selector, resourceVersion uint64) (watch.Interface, error) {
343
+func (c *Client) WatchDeployments(ctx api.Context, field, label labels.Selector, resourceVersion string) (watch.Interface, error) {
344 344
 	return c.Get().
345 345
 		Path("watch").
346 346
 		Path("deployments").
347
-		UintParam("resourceVersion", resourceVersion).
347
+		Param("resourceVersion", resourceVersion).
348 348
 		SelectorParam("labels", label).
349 349
 		SelectorParam("fields", field).
350 350
 		Watch()
... ...
@@ -384,11 +384,11 @@ func (c *Client) UpdateRoute(ctx api.Context, route *routeapi.Route) (result *ro
384 384
 }
385 385
 
386 386
 // WatchRoutes returns a watch.Interface that watches the requested routes.
387
-func (c *Client) WatchRoutes(ctx api.Context, label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) {
387
+func (c *Client) WatchRoutes(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) {
388 388
 	return c.Get().
389 389
 		Path("watch").
390 390
 		Path("routes").
391
-		UintParam("resourceVersion", resourceVersion).
391
+		Param("resourceVersion", resourceVersion).
392 392
 		SelectorParam("labels", label).
393 393
 		SelectorParam("fields", field).
394 394
 		Watch()
... ...
@@ -69,7 +69,7 @@ func (c *Fake) DeleteBuildConfig(ctx api.Context, id string) error {
69 69
 	return nil
70 70
 }
71 71
 
72
-func (c *Fake) WatchDeploymentConfigs(ctx api.Context, field, label labels.Selector, resourceVersion uint64) (watch.Interface, error) {
72
+func (c *Fake) WatchDeploymentConfigs(ctx api.Context, field, label labels.Selector, resourceVersion string) (watch.Interface, error) {
73 73
 	c.Actions = append(c.Actions, FakeAction{Action: "watch-deploymentconfig"})
74 74
 	return nil, nil
75 75
 }
... ...
@@ -99,7 +99,7 @@ func (c *Fake) GetImageRepository(ctx api.Context, id string) (*imageapi.ImageRe
99 99
 	return &imageapi.ImageRepository{}, nil
100 100
 }
101 101
 
102
-func (c *Fake) WatchImageRepositories(ctx api.Context, field, label labels.Selector, resourceVersion uint64) (watch.Interface, error) {
102
+func (c *Fake) WatchImageRepositories(ctx api.Context, field, label labels.Selector, resourceVersion string) (watch.Interface, error) {
103 103
 	c.Actions = append(c.Actions, FakeAction{Action: "watch-imagerepositories"})
104 104
 	return nil, nil
105 105
 }
... ...
@@ -174,7 +174,7 @@ func (c *Fake) DeleteDeployment(ctx api.Context, id string) error {
174 174
 	return nil
175 175
 }
176 176
 
177
-func (c *Fake) WatchDeployments(ctx api.Context, field, label labels.Selector, resourceVersion uint64) (watch.Interface, error) {
177
+func (c *Fake) WatchDeployments(ctx api.Context, field, label labels.Selector, resourceVersion string) (watch.Interface, error) {
178 178
 	c.Actions = append(c.Actions, FakeAction{Action: "watch-deployments"})
179 179
 	return nil, nil
180 180
 }
... ...
@@ -204,7 +204,7 @@ func (c *Fake) DeleteRoute(ctx api.Context, id string) error {
204 204
 	return nil
205 205
 }
206 206
 
207
-func (c *Fake) WatchRoutes(ctx api.Context, field, label labels.Selector, resourceVersion uint64) (watch.Interface, error) {
207
+func (c *Fake) WatchRoutes(ctx api.Context, field, label labels.Selector, resourceVersion string) (watch.Interface, error) {
208 208
 	c.Actions = append(c.Actions, FakeAction{Action: "watch-routes"})
209 209
 	return nil, nil
210 210
 }
... ...
@@ -31,7 +31,7 @@ func NewCommandKubecfg(name string) *cobra.Command {
31 31
 	flag := cmd.Flags()
32 32
 	flag.BoolVar(&cfg.ServerVersion, "server_version", false, "Print the server's version number.")
33 33
 	flag.BoolVar(&cfg.PreventSkew, "expect_version_match", false, "Fail if server's version doesn't match own version.")
34
-	flag.StringVarP(&cfg.ClientConfig.Host, "host", "h", "", "The host to connect to.")
34
+	flag.StringVar(&cfg.ClientConfig.Host, "host", "", "The host to connect to.")
35 35
 	flag.StringVarP(&cfg.Config, "config", "c", "", "Path or URL to the config file, or '-' to read from STDIN")
36 36
 	flag.StringVarP(&cfg.Selector, "label", "l", "", "Selector (label query) to use for listing")
37 37
 	flag.DurationVarP(&cfg.UpdatePeriod, "update", "u", 60*time.Second, "Update interval period")
... ...
@@ -309,7 +309,7 @@ func (c *KubeConfig) executeAPIRequest(method string, clients ClientMappings) bo
309 309
 
310 310
 	verb := ""
311 311
 	setBody := false
312
-	var version uint64
312
+	var version string
313 313
 	switch method {
314 314
 	case "get":
315 315
 		verb = "GET"
... ...
@@ -337,11 +337,11 @@ func (c *KubeConfig) executeAPIRequest(method string, clients ClientMappings) bo
337 337
 		if err != nil {
338 338
 			glog.Fatalf("error obtaining resource version for update: %v", err)
339 339
 		}
340
-		jsonBase, err := runtime.FindJSONBase(obj)
340
+		typeMeta, err := runtime.FindTypeMeta(obj)
341 341
 		if err != nil {
342 342
 			glog.Fatalf("error finding json base for update: %v", err)
343 343
 		}
344
-		version = jsonBase.ResourceVersion()
344
+		version = typeMeta.ResourceVersion()
345 345
 		verb = "PUT"
346 346
 		setBody = true
347 347
 		if !validStorage || !hasSuffix {
... ...
@@ -355,17 +355,17 @@ func (c *KubeConfig) executeAPIRequest(method string, clients ClientMappings) bo
355 355
 		Path(path).
356 356
 		ParseSelectorParam("labels", c.Selector)
357 357
 	if setBody {
358
-		if version != 0 {
358
+		if len(version) != 0 {
359 359
 			data := c.readConfig(storage, client.Codec)
360 360
 			obj, err := latest.Codec.Decode(data)
361 361
 			if err != nil {
362 362
 				glog.Fatalf("error setting resource version: %v", err)
363 363
 			}
364
-			jsonBase, err := runtime.FindJSONBase(obj)
364
+			typeMeta, err := runtime.FindTypeMeta(obj)
365 365
 			if err != nil {
366 366
 				glog.Fatalf("error setting resource version: %v", err)
367 367
 			}
368
-			jsonBase.SetResourceVersion(version)
368
+			typeMeta.SetResourceVersion(version)
369 369
 			data, err = client.Codec.Encode(obj)
370 370
 			if err != nil {
371 371
 				glog.Fatalf("error setting resource version: %v", err)
372 372
new file mode 100644
... ...
@@ -0,0 +1,74 @@
0
+/*
1
+Copyright 2014 Google Inc. All rights reserved.
2
+
3
+Licensed under the Apache License, Version 2.0 (the "License");
4
+you may not use this file except in compliance with the License.
5
+You may obtain a copy of the License at
6
+
7
+    http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+Unless required by applicable law or agreed to in writing, software
10
+distributed under the License is distributed on an "AS IS" BASIS,
11
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+See the License for the specific language governing permissions and
13
+limitations under the License.
14
+*/
15
+package flagtypes
16
+
17
+import (
18
+	"fmt"
19
+	"net"
20
+	"strings"
21
+)
22
+
23
+// lifted from kubernetes/pkg/util/net.go.   same flags vs pflags problem as we had with StringList
24
+
25
+// IP adapts net.IP for use as a flag.
26
+type IP net.IP
27
+
28
+func (ip IP) String() string {
29
+	return net.IP(ip).String()
30
+}
31
+
32
+func (ip *IP) Set(value string) error {
33
+	*ip = IP(net.ParseIP(strings.TrimSpace(value)))
34
+	if *ip == nil {
35
+		return fmt.Errorf("invalid IP address: '%s'", value)
36
+	}
37
+	return nil
38
+}
39
+
40
+// Type returns a string representation of what kind of argument this is
41
+func (ip *IP) Type() string {
42
+	return "cmd.flagtypes.IP"
43
+}
44
+
45
+// IPNet adapts net.IPNet for use as a flag.
46
+type IPNet net.IPNet
47
+
48
+func DefaultIPNet(value string) IPNet {
49
+	ret := IPNet{}
50
+	if err := ret.Set(value); err != nil {
51
+		panic(err)
52
+	}
53
+	return ret
54
+}
55
+
56
+func (ipnet IPNet) String() string {
57
+	n := net.IPNet(ipnet)
58
+	return n.String()
59
+}
60
+
61
+func (ipnet *IPNet) Set(value string) error {
62
+	_, n, err := net.ParseCIDR(strings.TrimSpace(value))
63
+	if err != nil {
64
+		return err
65
+	}
66
+	*ipnet = IPNet(*n)
67
+	return nil
68
+}
69
+
70
+// Type returns a string representation of what kind of argument this is
71
+func (ipnet *IPNet) Type() string {
72
+	return "cmd.flagtypes.IPNet"
73
+}
... ...
@@ -2,6 +2,7 @@ package kubernetes
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"net"
5 6
 	"net/http"
6 7
 	"time"
7 8
 
... ...
@@ -9,9 +10,9 @@ import (
9 9
 	kubeclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
10 10
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
11 11
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
12
-	// done to force the service package into godeps
13
-	_ "github.com/GoogleCloudPlatform/kubernetes/pkg/service"
12
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/service"
14 13
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
14
+	kubeutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
15 15
 	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler"
16 16
 	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory"
17 17
 	"github.com/golang/glog"
... ...
@@ -27,11 +28,19 @@ const (
27 27
 // MasterConfig defines the required values to start a Kubernetes master
28 28
 type MasterConfig struct {
29 29
 	NodeHosts []string
30
+	PortalNet *net.IPNet
30 31
 
31 32
 	EtcdHelper tools.EtcdHelper
32 33
 	KubeClient *kubeclient.Client
33 34
 }
34 35
 
36
+// TODO: Longer term we should read this from some config store, rather than a flag.
37
+func (c *MasterConfig) EnsurePortalFlags() {
38
+	if c.PortalNet == nil {
39
+		glog.Fatal("No --portal-net specified")
40
+	}
41
+}
42
+
35 43
 // InstallAPI starts a Kubernetes master and registers the supported REST APIs
36 44
 // into the provided mux, then returns an array of strings indicating what
37 45
 // endpoints were started (these are format strings that will expect to be sent
... ...
@@ -48,6 +57,7 @@ func (c *MasterConfig) InstallAPI(mux util.Mux) []string {
48 48
 		HealthCheckMinions: true,
49 49
 		Minions:            c.NodeHosts,
50 50
 		PodInfoGetter:      podInfoGetter,
51
+		PortalNet:          c.PortalNet,
51 52
 	}
52 53
 	m := master.New(masterConfig)
53 54
 
... ...
@@ -67,7 +77,15 @@ func (c *MasterConfig) RunReplicationController() {
67 67
 	glog.Infof("Started Kubernetes Replication Manager")
68 68
 }
69 69
 
70
-// RunReplicationController starts the Kubernetes scheduler
70
+// RunEndpointController starts the Kubernetes replication controller sync loop
71
+func (c *MasterConfig) RunEndpointController() {
72
+	endpoints := service.NewEndpointController(c.KubeClient)
73
+	go kubeutil.Forever(func() { endpoints.SyncServiceEndpoints() }, time.Second*10)
74
+
75
+	glog.Infof("Started Kubernetes Replication Manager")
76
+}
77
+
78
+// RunScheduler starts the Kubernetes scheduler
71 79
 func (c *MasterConfig) RunScheduler() {
72 80
 	configFactory := &factory.ConfigFactory{Client: c.KubeClient}
73 81
 	config := configFactory.Create()
... ...
@@ -1,8 +1,10 @@
1 1
 package kubernetes
2 2
 
3 3
 import (
4
+	"net"
4 5
 	"os"
5 6
 	"path/filepath"
7
+	"reflect"
6 8
 	"time"
7 9
 
8 10
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
... ...
@@ -10,12 +12,16 @@ import (
10 10
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/proxy"
11 11
 	pconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy/config"
12 12
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
13
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
14
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/iptables"
13 15
 	"github.com/coreos/go-etcd/etcd"
14 16
 	"github.com/fsouza/go-dockerclient"
15 17
 	"github.com/golang/glog"
16 18
 	cadvisor "github.com/google/cadvisor/client"
17 19
 
18 20
 	dockerutil "github.com/openshift/origin/pkg/cmd/util/docker"
21
+
22
+	"github.com/openshift/origin/pkg/service"
19 23
 )
20 24
 
21 25
 // NodePort is the default Kubelet port for serving information about the node.
... ...
@@ -74,19 +80,12 @@ func (c *NodeConfig) EnsureVolumeDir() {
74 74
 
75 75
 // RunKubelet starts the Kubelet.
76 76
 func (c *NodeConfig) RunKubelet() {
77
-	// cAdvisor should be running on the local machine
78
-	cadvisorClient, err := cadvisor.NewClient("http://" + c.NodeHost + ":4194")
79
-	if err != nil {
80
-		glog.Errorf("Error on creating cadvisor client: %v", err)
81
-	}
82
-
83 77
 	// initialize Kubelet
84 78
 	cfg := kconfig.NewPodConfig(kconfig.PodConfigNotificationSnapshotAndUpdates)
85 79
 	kconfig.NewSourceEtcd(kconfig.EtcdKeyForHost(c.NodeHost), c.EtcdClient, cfg.Channel("etcd"))
86 80
 	k := kubelet.NewMainKubelet(
87 81
 		c.NodeHost,
88 82
 		c.DockerClient,
89
-		cadvisorClient,
90 83
 		c.EtcdClient,
91 84
 		c.VolumeDir,
92 85
 		c.NetworkContainerImage,
... ...
@@ -94,10 +93,31 @@ func (c *NodeConfig) RunKubelet() {
94 94
 		0.0,
95 95
 		10)
96 96
 	go util.Forever(func() { k.Run(cfg.Updates()) }, 0)
97
+
98
+	// TODO expose this as a command line parameter
99
+	enableDebuggingHandlers := false
97 100
 	go util.Forever(func() {
98 101
 		glog.Infof("Started Kubelet for node %s, server at %s:%d", c.NodeHost, c.BindHost, NodePort)
99
-		kubelet.ListenAndServeKubeletServer(k, cfg.Channel("http"), c.BindHost, uint(NodePort))
102
+		kubelet.ListenAndServeKubeletServer(k, cfg.Channel("http"), net.ParseIP(c.BindHost), uint(NodePort), enableDebuggingHandlers)
100 103
 	}, 0)
104
+
105
+	// this mirrors 1fc92bef53fdd1bc70f623c0693736c763cff45f
106
+	// I don't fully understand what a cadvisor is, but it seems that we're supposed to run it separately from the rest of the kubelet
107
+	go func() {
108
+		defer util.HandleCrash()
109
+		// TODO: Monitor this connection, reconnect if needed?
110
+		glog.V(1).Infof("Trying to create cadvisor client.")
111
+		// cAdvisor should be running on the local machine
112
+		cadvisorClient, err := cadvisor.NewClient("http://" + c.NodeHost + ":4194")
113
+		if err != nil {
114
+			glog.Errorf("Error on creating cadvisor client: %v", err)
115
+			return
116
+		}
117
+		glog.V(1).Infof("Successfully created cadvisor client.")
118
+		// this binds the cadvisor to the kubelet for later references
119
+		k.SetCadvisorClient(cadvisorClient)
120
+	}()
121
+
101 122
 }
102 123
 
103 124
 // RunProxy starts the proxy
... ...
@@ -109,8 +129,15 @@ func (c *NodeConfig) RunProxy() {
109 109
 		serviceConfig.Channel("etcd"),
110 110
 		endpointsConfig.Channel("etcd"))
111 111
 	loadBalancer := proxy.NewLoadBalancerRR()
112
-	proxier := proxy.NewProxier(loadBalancer, c.BindHost)
113
-	serviceConfig.RegisterHandler(proxier)
114 112
 	endpointsConfig.RegisterHandler(loadBalancer)
113
+
114
+	var proxier pconfig.ServiceConfigHandler
115
+	proxier = proxy.NewProxier(loadBalancer, net.ParseIP(c.BindHost), iptables.New(exec.New()))
116
+	if proxier == nil || reflect.ValueOf(proxier).IsNil() { // explicitly declared interfaces aren't plain nil, you must reflect inside to see if it's really nil or not
117
+		glog.Errorf("WARNING: Could not modify iptables.  iptables must be mutable by this process to use services.  Do you have root permissions?")
118
+		proxier = &service.FailingServiceConfigProxy{}
119
+	}
120
+	serviceConfig.RegisterHandler(proxier)
121
+
115 122
 	glog.Infof("Started Kubernetes Proxy on %s", c.BindHost)
116 123
 }
... ...
@@ -306,7 +306,7 @@ func NewEtcdHelper(version string, client *etcdclient.Client) (helper tools.Etcd
306 306
 	if err != nil {
307 307
 		return helper, err
308 308
 	}
309
-	return tools.EtcdHelper{client, interfaces.Codec, interfaces.ResourceVersioner}, nil
309
+	return tools.EtcdHelper{client, interfaces.Codec, tools.RuntimeVersionAdapter{interfaces.ResourceVersioner}}, nil
310 310
 }
311 311
 
312 312
 // env returns an environment variable, or the defaultValue if it is not set.
... ...
@@ -66,6 +66,7 @@ type config struct {
66 66
 	BindAddr       flagtypes.Addr
67 67
 	EtcdAddr       flagtypes.Addr
68 68
 	KubernetesAddr flagtypes.Addr
69
+	PortalNet      flagtypes.IPNet
69 70
 
70 71
 	Hostname  string
71 72
 	VolumeDir string
... ...
@@ -87,6 +88,7 @@ func NewCommandStartServer(name string) *cobra.Command {
87 87
 		BindAddr:       flagtypes.Addr{Value: "0.0.0.0:8080", DefaultScheme: "http", DefaultPort: 8080, AllowPrefix: true}.Default(),
88 88
 		EtcdAddr:       flagtypes.Addr{Value: "0.0.0.0:4001", DefaultScheme: "http", DefaultPort: 4001}.Default(),
89 89
 		KubernetesAddr: flagtypes.Addr{DefaultScheme: "http", DefaultPort: 8080}.Default(),
90
+		PortalNet:      flagtypes.DefaultIPNet("172.17.17.0/24"),
90 91
 
91 92
 		NodeList: flagtypes.StringList{"127.0.0.1"},
92 93
 	}
... ...
@@ -157,7 +159,7 @@ func NewCommandStartServer(name string) *cobra.Command {
157 157
 				if err != nil {
158 158
 					glog.Errorf("Error setting up server storage: %v", err)
159 159
 				}
160
-				ketcdHelper, err := kmaster.NewEtcdHelper(etcdClient.GetCluster(), klatest.Version)
160
+				ketcdHelper, err := kmaster.NewEtcdHelper(etcdClient, klatest.Version)
161 161
 				if err != nil {
162 162
 					glog.Errorf("Error setting up Kubernetes server storage: %v", err)
163 163
 				}
... ...
@@ -191,16 +193,20 @@ func NewCommandStartServer(name string) *cobra.Command {
191 191
 				}
192 192
 
193 193
 				if startKube {
194
+					portalNet := net.IPNet(cfg.PortalNet)
194 195
 					kmaster := &kubernetes.MasterConfig{
195 196
 						NodeHosts:  cfg.NodeList,
197
+						PortalNet:  &portalNet,
196 198
 						EtcdHelper: ketcdHelper,
197 199
 						KubeClient: osmaster.KubeClient,
198 200
 					}
201
+					kmaster.EnsurePortalFlags()
199 202
 
200 203
 					osmaster.RunAPI(kmaster, auth)
201 204
 
202 205
 					kmaster.RunScheduler()
203 206
 					kmaster.RunReplicationController()
207
+					kmaster.RunEndpointController()
204 208
 
205 209
 				} else {
206 210
 					osmaster.RunAPI(auth)
... ...
@@ -251,6 +257,7 @@ func NewCommandStartServer(name string) *cobra.Command {
251 251
 	flag.Var(&cfg.MasterAddr, "master", "The address the master can be reached on (host, host:port, or URL).")
252 252
 	flag.Var(&cfg.EtcdAddr, "etcd", "The address of the etcd server (host, host:port, or URL).")
253 253
 	flag.Var(&cfg.KubernetesAddr, "kubernetes", "The address of the Kubernetes server (host, host:port, or URL). If specified no Kubernetes components will be started.")
254
+	flag.Var(&cfg.PortalNet, "portal-net", "A CIDR notation IP range from which to assign portal IPs. This must not overlap with any IP ranges assigned to nodes for pods.")
254 255
 
255 256
 	flag.StringVar(&cfg.VolumeDir, "volume-dir", "openshift.local.volumes", "The volume storage directory.")
256 257
 	flag.StringVar(&cfg.EtcdDir, "etcd-dir", "openshift.local.etcd", "The etcd data directory.")
... ...
@@ -9,7 +9,7 @@ import (
9 9
 // TODO: Unify with Kubernetes Config
10 10
 //       https://github.com/GoogleCloudPlatform/kubernetes/pull/1007
11 11
 type Config struct {
12
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
12
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
13 13
 
14 14
 	// Required: Name identifies the Config.
15 15
 	Name string `json:"name" yaml:"name"`
... ...
@@ -9,7 +9,7 @@ import (
9 9
 // TODO: Unify with Kubernetes Config
10 10
 //       https://github.com/GoogleCloudPlatform/kubernetes/pull/1007
11 11
 type Config struct {
12
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
12
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
13 13
 
14 14
 	// Required: Name identifies the Config.
15 15
 	Name string `json:"name" yaml:"name"`
... ...
@@ -43,7 +43,7 @@ func Apply(data []byte, storage clientapi.ClientMappings) (result []ApplyResult,
43 43
 			continue
44 44
 		}
45 45
 
46
-		itemBase := kubeapi.JSONBase{}
46
+		itemBase := kubeapi.TypeMeta{}
47 47
 
48 48
 		err = json.Unmarshal(item, &itemBase)
49 49
 		if err != nil {
... ...
@@ -51,7 +51,7 @@ func TestApplyInvalidConfig(t *testing.T) {
51 51
 }
52 52
 
53 53
 type FakeResource struct {
54
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
54
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
55 55
 }
56 56
 
57 57
 func (*FakeResource) IsAnAPIObject() {}
... ...
@@ -123,7 +123,7 @@ func ExampleApply() {
123 123
 }
124 124
 
125 125
 type FakeLabelsResource struct {
126
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
126
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
127 127
 	Labels           map[string]string `json:"labels" yaml:"labels"`
128 128
 }
129 129
 
... ...
@@ -7,7 +7,7 @@ import (
7 7
 // A deployment represents a single configuration of a pod deployed into the cluster, and may
8 8
 // represent both a current deployment or a historical deployment.
9 9
 type Deployment struct {
10
-	api.JSONBase       `json:",inline" yaml:",inline"`
10
+	api.TypeMeta       `json:",inline" yaml:",inline"`
11 11
 	Labels             map[string]string              `json:"labels,omitempty" yaml:"labels,omitempty"`
12 12
 	Strategy           DeploymentStrategy             `json:"strategy,omitempty" yaml:"strategy,omitempty"`
13 13
 	ControllerTemplate api.ReplicationControllerState `json:"controllerTemplate,omitempty" yaml:"controllerTemplate,omitempty"`
... ...
@@ -16,7 +16,7 @@ type Deployment struct {
16 16
 
17 17
 // A DeploymentList is a collection of deployments.
18 18
 type DeploymentList struct {
19
-	api.JSONBase `json:",inline" yaml:",inline"`
19
+	api.TypeMeta `json:",inline" yaml:",inline"`
20 20
 	Items        []Deployment `json:"items,omitempty" yaml:"items,omitempty"`
21 21
 }
22 22
 
... ...
@@ -71,7 +71,7 @@ type CustomPodDeploymentStrategy struct {
71 71
 // what the template is for the deployment, how new deployments are triggered, what the desired
72 72
 // deployment state is.
73 73
 type DeploymentConfig struct {
74
-	api.JSONBase `json:",inline" yaml:",inline"`
74
+	api.TypeMeta `json:",inline" yaml:",inline"`
75 75
 	Labels       map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
76 76
 	// Triggers determine how updates to a DeploymentConfig result in new deployments. If no triggers
77 77
 	// are defined, a new deployment can only occur as a result of an explicit client update to the
... ...
@@ -86,7 +86,7 @@ type DeploymentConfig struct {
86 86
 
87 87
 // A DeploymentConfigList is a collection of deployment configs.
88 88
 type DeploymentConfigList struct {
89
-	api.JSONBase `json:",inline" yaml:",inline"`
89
+	api.TypeMeta `json:",inline" yaml:",inline"`
90 90
 	Items        []DeploymentConfig `json:"items,omitempty" yaml:"items,omitempty"`
91 91
 }
92 92
 
... ...
@@ -7,7 +7,7 @@ import (
7 7
 // A deployment represents a single configuration of a pod deployed into the cluster, and may
8 8
 // represent both a current deployment or a historical deployment.
9 9
 type Deployment struct {
10
-	api.JSONBase       `json:",inline" yaml:",inline"`
10
+	api.TypeMeta       `json:",inline" yaml:",inline"`
11 11
 	Labels             map[string]string              `json:"labels,omitempty" yaml:"labels,omitempty"`
12 12
 	Strategy           DeploymentStrategy             `json:"strategy,omitempty" yaml:"strategy,omitempty"`
13 13
 	ControllerTemplate api.ReplicationControllerState `json:"controllerTemplate,omitempty" yaml:"controllerTemplate,omitempty"`
... ...
@@ -16,7 +16,7 @@ type Deployment struct {
16 16
 
17 17
 // A DeploymentList is a collection of deployments.
18 18
 type DeploymentList struct {
19
-	api.JSONBase `json:",inline" yaml:",inline"`
19
+	api.TypeMeta `json:",inline" yaml:",inline"`
20 20
 	Items        []Deployment `json:"items,omitempty" yaml:"items,omitempty"`
21 21
 }
22 22
 
... ...
@@ -71,7 +71,7 @@ type CustomPodDeploymentStrategy struct {
71 71
 // what the template is for the deployment, how new deployments are triggered, what the desired
72 72
 // deployment state is.
73 73
 type DeploymentConfig struct {
74
-	api.JSONBase `json:",inline" yaml:",inline"`
74
+	api.TypeMeta `json:",inline" yaml:",inline"`
75 75
 	Labels       map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
76 76
 	// Triggers determine how updates to a DeploymentConfig result in new deployments.
77 77
 	Triggers []DeploymentTriggerPolicy `json:"triggers,omitempty" yaml:"triggers,omitempty"`
... ...
@@ -84,7 +84,7 @@ type DeploymentConfig struct {
84 84
 
85 85
 // A DeploymentConfigList is a collection of deployment configs.
86 86
 type DeploymentConfigList struct {
87
-	api.JSONBase `json:",inline" yaml:",inline"`
87
+	api.TypeMeta `json:",inline" yaml:",inline"`
88 88
 	Items        []DeploymentConfig `json:"items,omitempty" yaml:"items,omitempty"`
89 89
 }
90 90
 
... ...
@@ -123,7 +123,7 @@ func (i *testChangeStrategy) UpdateDeploymentConfig(ctx kapi.Context, config *de
123 123
 
124 124
 func initialConfig() *deployapi.DeploymentConfig {
125 125
   return &deployapi.DeploymentConfig{
126
-    JSONBase: kapi.JSONBase{ID: "test-deploy-config"},
126
+    TypeMeta: kapi.TypeMeta{ID: "test-deploy-config"},
127 127
     Triggers: []deployapi.DeploymentTriggerPolicy{
128 128
       {
129 129
         Type: deployapi.DeploymentTriggerOnConfigChange,
... ...
@@ -165,7 +165,7 @@ func initialConfig() *deployapi.DeploymentConfig {
165 165
 
166 166
 func diffedConfig() *deployapi.DeploymentConfig {
167 167
   return &deployapi.DeploymentConfig{
168
-    JSONBase: kapi.JSONBase{ID: "test-deploy-config"},
168
+    TypeMeta: kapi.TypeMeta{ID: "test-deploy-config"},
169 169
     Triggers: []deployapi.DeploymentTriggerPolicy{
170 170
       {
171 171
         Type: deployapi.DeploymentTriggerOnConfigChange,
... ...
@@ -207,7 +207,7 @@ func diffedConfig() *deployapi.DeploymentConfig {
207 207
 
208 208
 func generatedConfig() *deployapi.DeploymentConfig {
209 209
   return &deployapi.DeploymentConfig{
210
-    JSONBase: kapi.JSONBase{ID: "test-deploy-config"},
210
+    TypeMeta: kapi.TypeMeta{ID: "test-deploy-config"},
211 211
     Triggers: []deployapi.DeploymentTriggerPolicy{
212 212
       {
213 213
         Type: deployapi.DeploymentTriggerOnConfigChange,
... ...
@@ -249,7 +249,7 @@ func generatedConfig() *deployapi.DeploymentConfig {
249 249
 
250 250
 func matchingInitialDeployment() *deployapi.Deployment {
251 251
   return &deployapi.Deployment{
252
-    JSONBase: kapi.JSONBase{ID: "test-deploy-config-1"},
252
+    TypeMeta: kapi.TypeMeta{ID: "test-deploy-config-1"},
253 253
     Status:   deployapi.DeploymentStatusNew,
254 254
     Strategy: deployapi.DeploymentStrategy{
255 255
       Type: deployapi.DeploymentStrategyTypeCustomPod,
... ...
@@ -155,7 +155,7 @@ func (dc *CustomPodDeploymentController) makeDeploymentPod(deployment *deployapi
155 155
 	}
156 156
 
157 157
 	return &kapi.Pod{
158
-		JSONBase: kapi.JSONBase{
158
+		TypeMeta: kapi.TypeMeta{
159 159
 			ID: podID,
160 160
 		},
161 161
 		DesiredState: kapi.PodState{
... ...
@@ -195,7 +195,7 @@ func TestHandlePodTerminatedNotOk(t *testing.T) {
195 195
 
196 196
 func basicDeployment() *deployapi.Deployment {
197 197
   return &deployapi.Deployment{
198
-    JSONBase: kapi.JSONBase{ID: "deploy1"},
198
+    TypeMeta: kapi.TypeMeta{ID: "deploy1"},
199 199
     Status:   deployapi.DeploymentStatusNew,
200 200
     Strategy: deployapi.DeploymentStrategy{
201 201
       Type: deployapi.DeploymentStrategyTypeBasic,
... ...
@@ -99,7 +99,7 @@ func (c *DeploymentConfigController) deploy(ctx kapi.Context, config *deployapi.
99 99
 	labels[deployapi.DeploymentConfigLabel] = config.ID
100 100
 
101 101
 	deployment := &deployapi.Deployment{
102
-		JSONBase: kapi.JSONBase{
102
+		TypeMeta: kapi.TypeMeta{
103 103
 			ID: deployutil.LatestDeploymentIDForConfig(config),
104 104
 		},
105 105
 		Labels:             labels,
... ...
@@ -131,7 +131,7 @@ func TestHandleConfigChangeWithPodTemplateDiff(t *testing.T) {
131 131
 
132 132
 func manualDeploymentConfig() *deployapi.DeploymentConfig {
133 133
   return &deployapi.DeploymentConfig{
134
-    JSONBase: kapi.JSONBase{ID: "manual-deploy-config"},
134
+    TypeMeta: kapi.TypeMeta{ID: "manual-deploy-config"},
135 135
     Triggers: []deployapi.DeploymentTriggerPolicy{
136 136
       {
137 137
         Type: deployapi.DeploymentTriggerManual,
... ...
@@ -172,7 +172,7 @@ func manualDeploymentConfig() *deployapi.DeploymentConfig {
172 172
 
173 173
 func matchingDeployment() *deployapi.Deployment {
174 174
   return &deployapi.Deployment{
175
-    JSONBase: kapi.JSONBase{ID: "manual-deploy-config-1"},
175
+    TypeMeta: kapi.TypeMeta{ID: "manual-deploy-config-1"},
176 176
     Status:   deployapi.DeploymentStatusNew,
177 177
     Strategy: deployapi.DeploymentStrategy{
178 178
       Type: deployapi.DeploymentStrategyTypeCustomPod,
... ...
@@ -147,13 +147,13 @@ func (lw *podLW) List() (runtime.Object, error) {
147 147
 }
148 148
 
149 149
 // Watch watches all pods with the given selector.
150
-func (lw *podLW) Watch(resourceVersion uint64) (watch.Interface, error) {
150
+func (lw *podLW) Watch(resourceVersion string) (watch.Interface, error) {
151 151
   return lw.client.
152 152
     Get().
153 153
     Path("watch").
154 154
     Path("pods").
155 155
     SelectorParam("labels", lw.labelSelector).
156
-    UintParam("resourceVersion", resourceVersion).
156
+    Param("resourceVersion", resourceVersion).
157 157
     Watch()
158 158
 }
159 159
 
... ...
@@ -190,8 +190,8 @@ func (lw *deploymentLW) List() (runtime.Object, error) {
190 190
 }
191 191
 
192 192
 // Watch watches all Deployments matching the given field selector.
193
-func (lw *deploymentLW) Watch(resourceVersion uint64) (watch.Interface, error) {
194
-  return lw.client.WatchDeployments(kapi.NewContext(), lw.field, labels.Everything(), 0)
193
+func (lw *deploymentLW) Watch(resourceVersion string) (watch.Interface, error) {
194
+  return lw.client.WatchDeployments(kapi.NewContext(), lw.field, labels.Everything(), "0")
195 195
 }
196 196
 
197 197
 // deploymentConfigLW is a ListWatcher implementation for DeploymentConfigs.
... ...
@@ -205,8 +205,8 @@ func (lw *deploymentConfigLW) List() (runtime.Object, error) {
205 205
 }
206 206
 
207 207
 // Watch watches all DeploymentConfigs.
208
-func (lw *deploymentConfigLW) Watch(resourceVersion uint64) (watch.Interface, error) {
209
-  return lw.client.WatchDeploymentConfigs(kapi.NewContext(), labels.Everything(), labels.Everything(), 0)
208
+func (lw *deploymentConfigLW) Watch(resourceVersion string) (watch.Interface, error) {
209
+  return lw.client.WatchDeploymentConfigs(kapi.NewContext(), labels.Everything(), labels.Everything(), "0")
210 210
 }
211 211
 
212 212
 // imageRepositoryLW is a ListWatcher for ImageRepositories.
... ...
@@ -220,6 +220,6 @@ func (lw *imageRepositoryLW) List() (runtime.Object, error) {
220 220
 }
221 221
 
222 222
 // Watch watches all ImageRepositories.
223
-func (lw *imageRepositoryLW) Watch(resourceVersion uint64) (watch.Interface, error) {
224
-  return lw.client.WatchImageRepositories(kapi.NewContext(), labels.Everything(), labels.Everything(), 0)
223
+func (lw *imageRepositoryLW) Watch(resourceVersion string) (watch.Interface, error) {
224
+  return lw.client.WatchImageRepositories(kapi.NewContext(), labels.Everything(), labels.Everything(), "0")
225 225
 }
... ...
@@ -113,7 +113,7 @@ func TestImageChange(t *testing.T) {
113 113
 
114 114
 func originalImageRepo() *imageapi.ImageRepository {
115 115
   return &imageapi.ImageRepository{
116
-    JSONBase:              kapi.JSONBase{ID: "test-image-repo"},
116
+    TypeMeta:              kapi.TypeMeta{ID: "test-image-repo"},
117 117
     DockerImageRepository: "registry:8080/openshift/test-image",
118 118
     Tags: map[string]string{
119 119
       "test-tag": "ref-1",
... ...
@@ -123,7 +123,7 @@ func originalImageRepo() *imageapi.ImageRepository {
123 123
 
124 124
 func unregisteredTagUpdate() *imageapi.ImageRepository {
125 125
   return &imageapi.ImageRepository{
126
-    JSONBase:              kapi.JSONBase{ID: "test-image-repo"},
126
+    TypeMeta:              kapi.TypeMeta{ID: "test-image-repo"},
127 127
     DockerImageRepository: "registry:8080/openshift/test-image",
128 128
     Tags: map[string]string{
129 129
       "test-tag":       "ref-1",
... ...
@@ -134,7 +134,7 @@ func unregisteredTagUpdate() *imageapi.ImageRepository {
134 134
 
135 135
 func tagUpdate() *imageapi.ImageRepository {
136 136
   return &imageapi.ImageRepository{
137
-    JSONBase:              kapi.JSONBase{ID: "test-image-repo"},
137
+    TypeMeta:              kapi.TypeMeta{ID: "test-image-repo"},
138 138
     DockerImageRepository: "registry:8080/openshift/test-image",
139 139
     Tags: map[string]string{
140 140
       "test-tag": "ref-2",
... ...
@@ -144,7 +144,7 @@ func tagUpdate() *imageapi.ImageRepository {
144 144
 
145 145
 func imageChangeDeploymentConfig() *deployapi.DeploymentConfig {
146 146
   return &deployapi.DeploymentConfig{
147
-    JSONBase: kapi.JSONBase{ID: "image-change-deploy-config"},
147
+    TypeMeta: kapi.TypeMeta{ID: "image-change-deploy-config"},
148 148
     Triggers: []deployapi.DeploymentTriggerPolicy{
149 149
       {
150 150
         Type: deployapi.DeploymentTriggerOnImageChange,
... ...
@@ -191,7 +191,7 @@ func imageChangeDeploymentConfig() *deployapi.DeploymentConfig {
191 191
 
192 192
 func regeneratedConfig() *deployapi.DeploymentConfig {
193 193
   return &deployapi.DeploymentConfig{
194
-    JSONBase: kapi.JSONBase{ID: "image-change-deploy-config"},
194
+    TypeMeta: kapi.TypeMeta{ID: "image-change-deploy-config"},
195 195
     Triggers: []deployapi.DeploymentTriggerPolicy{
196 196
       {
197 197
         Type: deployapi.DeploymentTriggerOnImageChange,
... ...
@@ -191,7 +191,7 @@ func basicPodTemplate() kapi.PodTemplate {
191 191
 }
192 192
 func basicDeploymentConfig() *deployapi.DeploymentConfig {
193 193
 	return &deployapi.DeploymentConfig{
194
-		JSONBase:      kapi.JSONBase{ID: "deploy1"},
194
+		TypeMeta:      kapi.TypeMeta{ID: "deploy1"},
195 195
 		LatestVersion: 1,
196 196
 		Triggers: []deployapi.DeploymentTriggerPolicy{
197 197
 			{
... ...
@@ -217,7 +217,7 @@ func basicImageRepo() *imageapi.ImageRepositoryList {
217 217
 	return &imageapi.ImageRepositoryList{
218 218
 		Items: []imageapi.ImageRepository{
219 219
 			{
220
-				JSONBase:              kapi.JSONBase{ID: "imageRepo1"},
220
+				TypeMeta:              kapi.TypeMeta{ID: "imageRepo1"},
221 221
 				DockerImageRepository: "registry:8080/repo1",
222 222
 				Tags: map[string]string{
223 223
 					"tag1": "ref1",
... ...
@@ -231,7 +231,7 @@ func updatedImageRepo() *imageapi.ImageRepositoryList {
231 231
 	return &imageapi.ImageRepositoryList{
232 232
 		Items: []imageapi.ImageRepository{
233 233
 			{
234
-				JSONBase:              kapi.JSONBase{ID: "imageRepo1"},
234
+				TypeMeta:              kapi.TypeMeta{ID: "imageRepo1"},
235 235
 				DockerImageRepository: "registry:8080/repo1",
236 236
 				Tags: map[string]string{
237 237
 					"tag1": "ref2",
... ...
@@ -1,17 +1,17 @@
1 1
 package deploy
2 2
 
3 3
 import (
4
-  "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
5
-  "github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
6
-  api "github.com/openshift/origin/pkg/deploy/api"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
6
+	api "github.com/openshift/origin/pkg/deploy/api"
7 7
 )
8 8
 
9 9
 // Registry is an interface for things that know how to store Deployments.
10 10
 type Registry interface {
11
-  ListDeployments(selector labels.Selector) (*api.DeploymentList, error)
12
-  GetDeployment(id string) (*api.Deployment, error)
13
-  CreateDeployment(deployment *api.Deployment) error
14
-  UpdateDeployment(deployment *api.Deployment) error
15
-  DeleteDeployment(id string) error
16
-  WatchDeployments(resourceVersion uint64, filter func(repo *api.Deployment) bool) (watch.Interface, error)
11
+	ListDeployments(selector labels.Selector) (*api.DeploymentList, error)
12
+	GetDeployment(id string) (*api.Deployment, error)
13
+	CreateDeployment(deployment *api.Deployment) error
14
+	UpdateDeployment(deployment *api.Deployment) error
15
+	DeleteDeployment(id string) error
16
+	WatchDeployments(resourceVersion string, filter func(repo *api.Deployment) bool) (watch.Interface, error)
17 17
 }
... ...
@@ -105,7 +105,7 @@ func (s *REST) Update(ctx kubeapi.Context, obj runtime.Object) (<-chan runtime.O
105 105
 }
106 106
 
107 107
 // Watch begins watching for new, changed, or deleted Deployments.
108
-func (s *REST) Watch(ctx kubeapi.Context, label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) {
108
+func (s *REST) Watch(ctx kubeapi.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) {
109 109
 	return s.registry.WatchDeployments(resourceVersion, func(deployment *deployapi.Deployment) bool {
110 110
 		fields := labels.Set{
111 111
 			"ID":       deployment.ID,
... ...
@@ -56,12 +56,12 @@ func TestListDeploymentsPopulatedList(t *testing.T) {
56 56
 	mockRegistry.Deployments = &api.DeploymentList{
57 57
 		Items: []api.Deployment{
58 58
 			{
59
-				JSONBase: kapi.JSONBase{
59
+				TypeMeta: kapi.TypeMeta{
60 60
 					ID: "foo",
61 61
 				},
62 62
 			},
63 63
 			{
64
-				JSONBase: kapi.JSONBase{
64
+				TypeMeta: kapi.TypeMeta{
65 65
 					ID: "bar",
66 66
 				},
67 67
 			},
... ...
@@ -102,7 +102,7 @@ func TestCreateRegistrySaveError(t *testing.T) {
102 102
 	storage := REST{registry: mockRegistry}
103 103
 
104 104
 	channel, err := storage.Create(nil, &api.Deployment{
105
-		JSONBase:           kapi.JSONBase{ID: "foo"},
105
+		TypeMeta:           kapi.TypeMeta{ID: "foo"},
106 106
 		Strategy:           deploytest.OkStrategy(),
107 107
 		ControllerTemplate: deploytest.OkControllerTemplate(),
108 108
 	})
... ...
@@ -133,7 +133,7 @@ func TestCreateDeploymentOk(t *testing.T) {
133 133
 	storage := REST{registry: mockRegistry}
134 134
 
135 135
 	channel, err := storage.Create(nil, &api.Deployment{
136
-		JSONBase:           kapi.JSONBase{ID: "foo"},
136
+		TypeMeta:           kapi.TypeMeta{ID: "foo"},
137 137
 		Strategy:           deploytest.OkStrategy(),
138 138
 		ControllerTemplate: deploytest.OkControllerTemplate(),
139 139
 	})
... ...
@@ -176,7 +176,7 @@ func TestGetDeploymentError(t *testing.T) {
176 176
 func TestGetDeploymentOk(t *testing.T) {
177 177
 	mockRegistry := test.NewDeploymentRegistry()
178 178
 	mockRegistry.Deployment = &api.Deployment{
179
-		JSONBase: kapi.JSONBase{ID: "foo"},
179
+		TypeMeta: kapi.TypeMeta{ID: "foo"},
180 180
 	}
181 181
 	storage := REST{registry: mockRegistry}
182 182
 
... ...
@@ -222,7 +222,7 @@ func TestUpdateRegistryErrorSaving(t *testing.T) {
222 222
 	storage := REST{registry: mockRepositoryRegistry}
223 223
 
224 224
 	channel, err := storage.Update(nil, &api.Deployment{
225
-		JSONBase: kapi.JSONBase{ID: "bar"},
225
+		TypeMeta: kapi.TypeMeta{ID: "bar"},
226 226
 	})
227 227
 	if err != nil {
228 228
 		t.Errorf("Unexpected non-nil error: %#v", err)
... ...
@@ -242,7 +242,7 @@ func TestUpdateDeploymentOk(t *testing.T) {
242 242
 	storage := REST{registry: mockRepositoryRegistry}
243 243
 
244 244
 	channel, err := storage.Update(nil, &api.Deployment{
245
-		JSONBase: kapi.JSONBase{ID: "bar"},
245
+		TypeMeta: kapi.TypeMeta{ID: "bar"},
246 246
 	})
247 247
 	if err != nil {
248 248
 		t.Errorf("Unexpected non-nil error: %#v", err)
... ...
@@ -9,7 +9,7 @@ import (
9 9
 // Registry is an interface for things that know how to store DeploymentConfigs.
10 10
 type Registry interface {
11 11
 	ListDeploymentConfigs(selector labels.Selector) (*api.DeploymentConfigList, error)
12
-	WatchDeploymentConfigs(resourceVersion uint64, filter func(repo *api.DeploymentConfig) bool) (watch.Interface, error)
12
+	WatchDeploymentConfigs(resourceVersion string, filter func(repo *api.DeploymentConfig) bool) (watch.Interface, error)
13 13
 	GetDeploymentConfig(id string) (*api.DeploymentConfig, error)
14 14
 	CreateDeploymentConfig(deploymentConfig *api.DeploymentConfig) error
15 15
 	UpdateDeploymentConfig(deploymentConfig *api.DeploymentConfig) error
... ...
@@ -42,7 +42,7 @@ func (s *REST) List(ctx kubeapi.Context, selector, fields labels.Selector) (runt
42 42
 }
43 43
 
44 44
 // Watch begins watching for new, changed, or deleted ImageRepositories.
45
-func (s *REST) Watch(ctx kubeapi.Context, label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) {
45
+func (s *REST) Watch(ctx kubeapi.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) {
46 46
 	return s.registry.WatchDeploymentConfigs(resourceVersion, func(config *deployapi.DeploymentConfig) bool {
47 47
 		fields := labels.Set{
48 48
 			"ID": config.ID,
... ...
@@ -56,12 +56,12 @@ func TestListDeploymentConfigsPopulatedList(t *testing.T) {
56 56
 	mockRegistry.DeploymentConfigs = &api.DeploymentConfigList{
57 57
 		Items: []api.DeploymentConfig{
58 58
 			{
59
-				JSONBase: kapi.JSONBase{
59
+				TypeMeta: kapi.TypeMeta{
60 60
 					ID: "foo",
61 61
 				},
62 62
 			},
63 63
 			{
64
-				JSONBase: kapi.JSONBase{
64
+				TypeMeta: kapi.TypeMeta{
65 65
 					ID: "bar",
66 66
 				},
67 67
 			},
... ...
@@ -102,9 +102,9 @@ func TestCreateRegistrySaveError(t *testing.T) {
102 102
 	storage := REST{registry: mockRegistry}
103 103
 
104 104
 	channel, err := storage.Create(nil, &api.DeploymentConfig{
105
-		JSONBase: kapi.JSONBase{ID: "foo"},
105
+		TypeMeta: kapi.TypeMeta{ID: "foo"},
106 106
 		Template: api.DeploymentTemplate{
107
-			Strategy: deploytest.OkStrategy(),
107
+			Strategy:           deploytest.OkStrategy(),
108 108
 			ControllerTemplate: deploytest.OkControllerTemplate(),
109 109
 		},
110 110
 	})
... ...
@@ -135,9 +135,9 @@ func TestCreateDeploymentConfigOK(t *testing.T) {
135 135
 	storage := REST{registry: mockRegistry}
136 136
 
137 137
 	channel, err := storage.Create(nil, &api.DeploymentConfig{
138
-		JSONBase: kapi.JSONBase{ID: "foo"},
138
+		TypeMeta: kapi.TypeMeta{ID: "foo"},
139 139
 		Template: api.DeploymentTemplate{
140
-			Strategy: deploytest.OkStrategy(),
140
+			Strategy:           deploytest.OkStrategy(),
141 141
 			ControllerTemplate: deploytest.OkControllerTemplate(),
142 142
 		},
143 143
 	})
... ...
@@ -180,7 +180,7 @@ func TestGetDeploymentConfigError(t *testing.T) {
180 180
 func TestGetDeploymentConfigOK(t *testing.T) {
181 181
 	mockRegistry := test.NewDeploymentConfigRegistry()
182 182
 	mockRegistry.DeploymentConfig = &api.DeploymentConfig{
183
-		JSONBase: kapi.JSONBase{ID: "foo"},
183
+		TypeMeta: kapi.TypeMeta{ID: "foo"},
184 184
 	}
185 185
 	storage := REST{registry: mockRegistry}
186 186
 
... ...
@@ -226,7 +226,7 @@ func TestUpdateRegistryErrorSaving(t *testing.T) {
226 226
 	storage := REST{registry: mockRepositoryRegistry}
227 227
 
228 228
 	channel, err := storage.Update(nil, &api.DeploymentConfig{
229
-		JSONBase: kapi.JSONBase{ID: "bar"},
229
+		TypeMeta: kapi.TypeMeta{ID: "bar"},
230 230
 	})
231 231
 	if err != nil {
232 232
 		t.Errorf("Unexpected non-nil error: %#v", err)
... ...
@@ -246,7 +246,7 @@ func TestUpdateDeploymentConfigOK(t *testing.T) {
246 246
 	storage := REST{registry: mockRepositoryRegistry}
247 247
 
248 248
 	channel, err := storage.Update(nil, &api.DeploymentConfig{
249
-		JSONBase: kapi.JSONBase{ID: "bar"},
249
+		TypeMeta: kapi.TypeMeta{ID: "bar"},
250 250
 	})
251 251
 	if err != nil {
252 252
 		t.Errorf("Unexpected non-nil error: %#v", err)
... ...
@@ -1,6 +1,8 @@
1 1
 package etcd
2 2
 
3 3
 import (
4
+	"strconv"
5
+
4 6
 	etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd"
5 7
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
6 8
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
... ...
@@ -25,7 +27,7 @@ func New(helper tools.EtcdHelper) *Etcd {
25 25
 // ListDeployments obtains a list of Deployments.
26 26
 func (r *Etcd) ListDeployments(selector labels.Selector) (*api.DeploymentList, error) {
27 27
 	deployments := api.DeploymentList{}
28
-	err := r.ExtractList("/deployments", &deployments.Items, &deployments.ResourceVersion)
28
+	err := r.ExtractToList("/deployments", &deployments)
29 29
 	if err != nil {
30 30
 		return nil, err
31 31
 	}
... ...
@@ -75,8 +77,13 @@ func (r *Etcd) DeleteDeployment(id string) error {
75 75
 }
76 76
 
77 77
 // WatchDeployments begins watching for new, changed, or deleted Deployments.
78
-func (r *Etcd) WatchDeployments(resourceVersion uint64, filter func(deployment *api.Deployment) bool) (watch.Interface, error) {
79
-	return r.WatchList("/deployments", resourceVersion, func(obj runtime.Object) bool {
78
+func (r *Etcd) WatchDeployments(resourceVersion string, filter func(deployment *api.Deployment) bool) (watch.Interface, error) {
79
+	version, err := parseWatchResourceVersion(resourceVersion, "deployment")
80
+	if err != nil {
81
+		return nil, err
82
+	}
83
+
84
+	return r.WatchList("/deployments", version, func(obj runtime.Object) bool {
80 85
 		deployment, ok := obj.(*api.Deployment)
81 86
 		if !ok {
82 87
 			glog.Errorf("Unexpected object during deployment watch: %#v", obj)
... ...
@@ -89,7 +96,7 @@ func (r *Etcd) WatchDeployments(resourceVersion uint64, filter func(deployment *
89 89
 // ListDeploymentConfigs obtains a list of DeploymentConfigs.
90 90
 func (r *Etcd) ListDeploymentConfigs(selector labels.Selector) (*api.DeploymentConfigList, error) {
91 91
 	deploymentConfigs := api.DeploymentConfigList{}
92
-	err := r.ExtractList("/deploymentConfigs", &deploymentConfigs.Items, &deploymentConfigs.ResourceVersion)
92
+	err := r.ExtractToList("/deploymentConfigs", &deploymentConfigs)
93 93
 	if err != nil {
94 94
 		return nil, err
95 95
 	}
... ...
@@ -104,9 +111,30 @@ func (r *Etcd) ListDeploymentConfigs(selector labels.Selector) (*api.DeploymentC
104 104
 	return &deploymentConfigs, err
105 105
 }
106 106
 
107
+// TODO expose this from kubernetes.  I will do that, but I don't want this merge stuck on kubernetes refactoring
108
+// parseWatchResourceVersion takes a resource version argument and converts it to
109
+// the etcd version we should pass to helper.Watch(). Because resourceVersion is
110
+// an opaque value, the default watch behavior for non-zero watch is to watch
111
+// the next value (if you pass "1", you will see updates from "2" onwards).
112
+func parseWatchResourceVersion(resourceVersion, kind string) (uint64, error) {
113
+	if resourceVersion == "" || resourceVersion == "0" {
114
+		return 0, nil
115
+	}
116
+	version, err := strconv.ParseUint(resourceVersion, 10, 64)
117
+	if err != nil {
118
+		return 0, etcderr.InterpretResourceVersionError(err, kind, resourceVersion)
119
+	}
120
+	return version + 1, nil
121
+}
122
+
107 123
 // WatchDeploymentConfigs begins watching for new, changed, or deleted DeploymentConfigs.
108
-func (r *Etcd) WatchDeploymentConfigs(resourceVersion uint64, filter func(repo *api.DeploymentConfig) bool) (watch.Interface, error) {
109
-	return r.WatchList("/deploymentConfigs", resourceVersion, func(obj runtime.Object) bool {
124
+func (r *Etcd) WatchDeploymentConfigs(resourceVersion string, filter func(repo *api.DeploymentConfig) bool) (watch.Interface, error) {
125
+	version, err := parseWatchResourceVersion(resourceVersion, "deploymentConfig")
126
+	if err != nil {
127
+		return nil, err
128
+	}
129
+
130
+	return r.WatchList("/deploymentConfigs", version, func(obj runtime.Object) bool {
110 131
 		config, ok := obj.(*api.DeploymentConfig)
111 132
 		if !ok {
112 133
 			glog.Errorf("Unexpected object during deploymentConfig watch: %#v", obj)
... ...
@@ -16,7 +16,7 @@ import (
16 16
 )
17 17
 
18 18
 func NewTestEtcd(client tools.EtcdClient) *Etcd {
19
-	return New(tools.EtcdHelper{client, latest.Codec, latest.ResourceVersioner})
19
+	return New(tools.EtcdHelper{client, latest.Codec, tools.RuntimeVersionAdapter{latest.ResourceVersioner}})
20 20
 }
21 21
 
22 22
 func TestEtcdListEmptyDeployments(t *testing.T) {
... ...
@@ -69,10 +69,10 @@ func TestEtcdListEverythingDeployments(t *testing.T) {
69 69
 			Node: &etcd.Node{
70 70
 				Nodes: []*etcd.Node{
71 71
 					{
72
-						Value: runtime.EncodeOrDie(latest.Codec, &api.Deployment{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
72
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Deployment{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
73 73
 					},
74 74
 					{
75
-						Value: runtime.EncodeOrDie(latest.Codec, &api.Deployment{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
75
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Deployment{TypeMeta: kubeapi.TypeMeta{ID: "bar"}}),
76 76
 					},
77 77
 				},
78 78
 			},
... ...
@@ -99,13 +99,13 @@ func TestEtcdListFilteredDeployments(t *testing.T) {
99 99
 				Nodes: []*etcd.Node{
100 100
 					{
101 101
 						Value: runtime.EncodeOrDie(latest.Codec, &api.Deployment{
102
-							JSONBase: kubeapi.JSONBase{ID: "foo"},
102
+							TypeMeta: kubeapi.TypeMeta{ID: "foo"},
103 103
 							Labels:   map[string]string{"env": "prod"},
104 104
 						}),
105 105
 					},
106 106
 					{
107 107
 						Value: runtime.EncodeOrDie(latest.Codec, &api.Deployment{
108
-							JSONBase: kubeapi.JSONBase{ID: "bar"},
108
+							TypeMeta: kubeapi.TypeMeta{ID: "bar"},
109 109
 							Labels:   map[string]string{"env": "dev"},
110 110
 						}),
111 111
 					},
... ...
@@ -127,7 +127,7 @@ func TestEtcdListFilteredDeployments(t *testing.T) {
127 127
 
128 128
 func TestEtcdGetDeployments(t *testing.T) {
129 129
 	fakeClient := tools.NewFakeEtcdClient(t)
130
-	fakeClient.Set("/deployments/foo", runtime.EncodeOrDie(latest.Codec, &api.Deployment{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
130
+	fakeClient.Set("/deployments/foo", runtime.EncodeOrDie(latest.Codec, &api.Deployment{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}), 0)
131 131
 	registry := NewTestEtcd(fakeClient)
132 132
 	deployment, err := registry.GetDeployment("foo")
133 133
 	if err != nil {
... ...
@@ -168,7 +168,7 @@ func TestEtcdCreateDeployments(t *testing.T) {
168 168
 	}
169 169
 	registry := NewTestEtcd(fakeClient)
170 170
 	err := registry.CreateDeployment(&api.Deployment{
171
-		JSONBase: kubeapi.JSONBase{
171
+		TypeMeta: kubeapi.TypeMeta{
172 172
 			ID: "foo",
173 173
 		},
174 174
 	})
... ...
@@ -196,14 +196,14 @@ func TestEtcdCreateAlreadyExistsDeployments(t *testing.T) {
196 196
 	fakeClient.Data["/deployments/foo"] = tools.EtcdResponseWithError{
197 197
 		R: &etcd.Response{
198 198
 			Node: &etcd.Node{
199
-				Value: runtime.EncodeOrDie(latest.Codec, &api.Deployment{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
199
+				Value: runtime.EncodeOrDie(latest.Codec, &api.Deployment{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
200 200
 			},
201 201
 		},
202 202
 		E: nil,
203 203
 	}
204 204
 	registry := NewTestEtcd(fakeClient)
205 205
 	err := registry.CreateDeployment(&api.Deployment{
206
-		JSONBase: kubeapi.JSONBase{
206
+		TypeMeta: kubeapi.TypeMeta{
207 207
 			ID: "foo",
208 208
 		},
209 209
 	})
... ...
@@ -312,10 +312,10 @@ func TestEtcdListEverythingDeploymentConfig(t *testing.T) {
312 312
 			Node: &etcd.Node{
313 313
 				Nodes: []*etcd.Node{
314 314
 					{
315
-						Value: runtime.EncodeOrDie(latest.Codec, &api.DeploymentConfig{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
315
+						Value: runtime.EncodeOrDie(latest.Codec, &api.DeploymentConfig{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
316 316
 					},
317 317
 					{
318
-						Value: runtime.EncodeOrDie(latest.Codec, &api.DeploymentConfig{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
318
+						Value: runtime.EncodeOrDie(latest.Codec, &api.DeploymentConfig{TypeMeta: kubeapi.TypeMeta{ID: "bar"}}),
319 319
 					},
320 320
 				},
321 321
 			},
... ...
@@ -342,13 +342,13 @@ func TestEtcdListFilteredDeploymentConfig(t *testing.T) {
342 342
 				Nodes: []*etcd.Node{
343 343
 					{
344 344
 						Value: runtime.EncodeOrDie(latest.Codec, &api.DeploymentConfig{
345
-							JSONBase: kubeapi.JSONBase{ID: "foo"},
345
+							TypeMeta: kubeapi.TypeMeta{ID: "foo"},
346 346
 							Labels:   map[string]string{"env": "prod"},
347 347
 						}),
348 348
 					},
349 349
 					{
350 350
 						Value: runtime.EncodeOrDie(latest.Codec, &api.DeploymentConfig{
351
-							JSONBase: kubeapi.JSONBase{ID: "bar"},
351
+							TypeMeta: kubeapi.TypeMeta{ID: "bar"},
352 352
 							Labels:   map[string]string{"env": "dev"},
353 353
 						}),
354 354
 					},
... ...
@@ -370,7 +370,7 @@ func TestEtcdListFilteredDeploymentConfig(t *testing.T) {
370 370
 
371 371
 func TestEtcdGetDeploymentConfig(t *testing.T) {
372 372
 	fakeClient := tools.NewFakeEtcdClient(t)
373
-	fakeClient.Set("/deploymentConfigs/foo", runtime.EncodeOrDie(latest.Codec, &api.DeploymentConfig{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
373
+	fakeClient.Set("/deploymentConfigs/foo", runtime.EncodeOrDie(latest.Codec, &api.DeploymentConfig{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}), 0)
374 374
 	registry := NewTestEtcd(fakeClient)
375 375
 	deployment, err := registry.GetDeploymentConfig("foo")
376 376
 	if err != nil {
... ...
@@ -411,7 +411,7 @@ func TestEtcdCreateDeploymentConfig(t *testing.T) {
411 411
 	}
412 412
 	registry := NewTestEtcd(fakeClient)
413 413
 	err := registry.CreateDeploymentConfig(&api.DeploymentConfig{
414
-		JSONBase: kubeapi.JSONBase{
414
+		TypeMeta: kubeapi.TypeMeta{
415 415
 			ID: "foo",
416 416
 		},
417 417
 	})
... ...
@@ -439,14 +439,14 @@ func TestEtcdCreateAlreadyExistsDeploymentConfig(t *testing.T) {
439 439
 	fakeClient.Data["/deploymentConfigs/foo"] = tools.EtcdResponseWithError{
440 440
 		R: &etcd.Response{
441 441
 			Node: &etcd.Node{
442
-				Value: runtime.EncodeOrDie(latest.Codec, &api.DeploymentConfig{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
442
+				Value: runtime.EncodeOrDie(latest.Codec, &api.DeploymentConfig{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
443 443
 			},
444 444
 		},
445 445
 		E: nil,
446 446
 	}
447 447
 	registry := NewTestEtcd(fakeClient)
448 448
 	err := registry.CreateDeploymentConfig(&api.DeploymentConfig{
449
-		JSONBase: kubeapi.JSONBase{
449
+		TypeMeta: kubeapi.TypeMeta{
450 450
 			ID: "foo",
451 451
 		},
452 452
 	})
... ...
@@ -56,6 +56,6 @@ func (r *DeploymentRegistry) DeleteDeployment(id string) error {
56 56
 	return r.Err
57 57
 }
58 58
 
59
-func (r *DeploymentRegistry) WatchDeployments(resourceVersion uint64, filter func(repo *api.Deployment) bool) (watch.Interface, error) {
59
+func (r *DeploymentRegistry) WatchDeployments(resourceVersion string, filter func(repo *api.Deployment) bool) (watch.Interface, error) {
60 60
 	return nil, r.Err
61 61
 }
... ...
@@ -56,6 +56,6 @@ func (r *DeploymentConfigRegistry) DeleteDeploymentConfig(id string) error {
56 56
 	return r.Err
57 57
 }
58 58
 
59
-func (r *DeploymentConfigRegistry) WatchDeploymentConfigs(resourceVersion uint64, filter func(repo *api.DeploymentConfig) bool) (watch.Interface, error) {
59
+func (r *DeploymentConfigRegistry) WatchDeploymentConfigs(resourceVersion string, filter func(repo *api.DeploymentConfig) bool) (watch.Interface, error) {
60 60
 	return nil, r.Err
61 61
 }
... ...
@@ -7,13 +7,13 @@ import (
7 7
 
8 8
 // ImageList is a list of Image objects.
9 9
 type ImageList struct {
10
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
10
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
11 11
 	Items            []Image `json:"items,omitempty" yaml:"items,omitempty"`
12 12
 }
13 13
 
14 14
 // Image is an immutable representation of a Docker image and metadata at a point in time.
15 15
 type Image struct {
16
-	kubeapi.JSONBase     `json:",inline" yaml:",inline"`
16
+	kubeapi.TypeMeta     `json:",inline" yaml:",inline"`
17 17
 	Labels               map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
18 18
 	DockerImageReference string            `json:"dockerImageReference,omitempty" yaml:"dockerImageReference,omitempty"`
19 19
 	Metadata             docker.Image      `json:"metadata,omitempty" yaml:"metadata,omitempty"`
... ...
@@ -21,7 +21,7 @@ type Image struct {
21 21
 
22 22
 // ImageRepositoryList is a list of ImageRepository objects.
23 23
 type ImageRepositoryList struct {
24
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
24
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
25 25
 	Items            []ImageRepository `json:"items,omitempty" yaml:"items,omitempty"`
26 26
 }
27 27
 
... ...
@@ -29,7 +29,7 @@ type ImageRepositoryList struct {
29 29
 // when images are tagged in a repository, and an optional reference to a Docker image
30 30
 // repository on a registry.
31 31
 type ImageRepository struct {
32
-	kubeapi.JSONBase      `json:",inline" yaml:",inline"`
32
+	kubeapi.TypeMeta      `json:",inline" yaml:",inline"`
33 33
 	Labels                map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
34 34
 	DockerImageRepository string            `json:"dockerImageRepository,omitempty" yaml:"dockerImageRepository,omitempty"`
35 35
 	Tags                  map[string]string `json:"tags,omitempty" yaml:"tags,omitempty"`
... ...
@@ -40,7 +40,7 @@ type ImageRepository struct {
40 40
 // ImageRepositoryMapping represents a mapping from a single tag to a Docker image as
41 41
 // well as the reference to the Docker image repository the image came from.
42 42
 type ImageRepositoryMapping struct {
43
-	kubeapi.JSONBase      `json:",inline" yaml:",inline"`
43
+	kubeapi.TypeMeta      `json:",inline" yaml:",inline"`
44 44
 	DockerImageRepository string `json:"dockerImageRepository" yaml:"dockerImageRepository"`
45 45
 	Image                 Image  `json:"image" yaml:"image"`
46 46
 	Tag                   string `json:"tag" yaml:"tag"`
... ...
@@ -7,13 +7,13 @@ import (
7 7
 
8 8
 // ImageList is a list of Image objects.
9 9
 type ImageList struct {
10
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
10
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
11 11
 	Items            []Image `json:"items,omitempty" yaml:"items,omitempty"`
12 12
 }
13 13
 
14 14
 // Image is an immutable representation of a Docker image and metadata at a point in time.
15 15
 type Image struct {
16
-	kubeapi.JSONBase     `json:",inline" yaml:",inline"`
16
+	kubeapi.TypeMeta     `json:",inline" yaml:",inline"`
17 17
 	Labels               map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
18 18
 	DockerImageReference string            `json:"dockerImageReference,omitempty" yaml:"dockerImageReference,omitempty"`
19 19
 	Metadata             docker.Image      `json:"metadata,omitempty" yaml:"metadata,omitempty"`
... ...
@@ -21,7 +21,7 @@ type Image struct {
21 21
 
22 22
 // ImageRepositoryList is a list of ImageRepository objects.
23 23
 type ImageRepositoryList struct {
24
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
24
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
25 25
 	Items            []ImageRepository `json:"items,omitempty" yaml:"items,omitempty"`
26 26
 }
27 27
 
... ...
@@ -29,7 +29,7 @@ type ImageRepositoryList struct {
29 29
 // when images are tagged in a repository, and an optional reference to a Docker image
30 30
 // repository on a registry.
31 31
 type ImageRepository struct {
32
-	kubeapi.JSONBase      `json:",inline" yaml:",inline"`
32
+	kubeapi.TypeMeta      `json:",inline" yaml:",inline"`
33 33
 	Labels                map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
34 34
 	DockerImageRepository string            `json:"dockerImageRepository,omitempty" yaml:"dockerImageRepository,omitempty"`
35 35
 	Tags                  map[string]string `json:"tags,omitempty" yaml:"tags,omitempty"`
... ...
@@ -40,7 +40,7 @@ type ImageRepository struct {
40 40
 // ImageRepositoryMapping represents a mapping from a single tag to a Docker image as
41 41
 // well as the reference to the Docker image repository the image came from.
42 42
 type ImageRepositoryMapping struct {
43
-	kubeapi.JSONBase      `json:",inline" yaml:",inline"`
43
+	kubeapi.TypeMeta      `json:",inline" yaml:",inline"`
44 44
 	DockerImageRepository string `json:"dockerImageRepository" yaml:"dockerImageRepository"`
45 45
 	Image                 Image  `json:"image" yaml:"image"`
46 46
 	Tag                   string `json:"tag" yaml:"tag"`
... ...
@@ -10,7 +10,7 @@ import (
10 10
 
11 11
 func TestValidateImageOK(t *testing.T) {
12 12
 	errs := ValidateImage(&api.Image{
13
-		JSONBase:             kubeapi.JSONBase{ID: "foo"},
13
+		TypeMeta:             kubeapi.TypeMeta{ID: "foo"},
14 14
 		DockerImageReference: "openshift/ruby-19-centos",
15 15
 	})
16 16
 	if len(errs) > 0 {
... ...
@@ -25,7 +25,7 @@ func TestValidateImageMissingFields(t *testing.T) {
25 25
 		F string
26 26
 	}{
27 27
 		"missing ID":                   {api.Image{DockerImageReference: "ref"}, errors.ValidationErrorTypeRequired, "ID"},
28
-		"missing DockerImageReference": {api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}, errors.ValidationErrorTypeRequired, "DockerImageReference"},
28
+		"missing DockerImageReference": {api.Image{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}, errors.ValidationErrorTypeRequired, "DockerImageReference"},
29 29
 	}
30 30
 
31 31
 	for k, v := range errorCases {
... ...
@@ -55,7 +55,7 @@ func TestValidateImageRepositoryMappingNotOK(t *testing.T) {
55 55
 			api.ImageRepositoryMapping{
56 56
 				Tag: "latest",
57 57
 				Image: api.Image{
58
-					JSONBase: kubeapi.JSONBase{
58
+					TypeMeta: kubeapi.TypeMeta{
59 59
 						ID: "foo",
60 60
 					},
61 61
 					DockerImageReference: "openshift/ruby-19-centos",
... ...
@@ -68,7 +68,7 @@ func TestValidateImageRepositoryMappingNotOK(t *testing.T) {
68 68
 			api.ImageRepositoryMapping{
69 69
 				DockerImageRepository: "openshift/ruby-19-centos",
70 70
 				Image: api.Image{
71
-					JSONBase: kubeapi.JSONBase{
71
+					TypeMeta: kubeapi.TypeMeta{
72 72
 						ID: "foo",
73 73
 					},
74 74
 					DockerImageReference: "openshift/ruby-19-centos",
... ...
@@ -2,6 +2,7 @@ package etcd
2 2
 
3 3
 import (
4 4
 	"errors"
5
+	"strconv"
5 6
 
6 7
 	etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd"
7 8
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
... ...
@@ -28,7 +29,7 @@ func New(helper tools.EtcdHelper) *Etcd {
28 28
 // ListImages retrieves a list of images that match selector.
29 29
 func (r *Etcd) ListImages(selector labels.Selector) (*api.ImageList, error) {
30 30
 	list := api.ImageList{}
31
-	err := r.ExtractList("/images", &list.Items, &list.ResourceVersion)
31
+	err := r.ExtractToList("/images", &list)
32 32
 	if err != nil {
33 33
 		return nil, err
34 34
 	}
... ...
@@ -76,7 +77,7 @@ func (r *Etcd) DeleteImage(id string) error {
76 76
 // ListImageRepositories retrieves a list of ImageRepositories that match selector.
77 77
 func (r *Etcd) ListImageRepositories(selector labels.Selector) (*api.ImageRepositoryList, error) {
78 78
 	list := api.ImageRepositoryList{}
79
-	err := r.ExtractList("/imageRepositories", &list.Items, &list.ResourceVersion)
79
+	err := r.ExtractToList("/imageRepositories", &list)
80 80
 	if err != nil {
81 81
 		return nil, err
82 82
 	}
... ...
@@ -103,9 +104,30 @@ func (r *Etcd) GetImageRepository(id string) (*api.ImageRepository, error) {
103 103
 	return &repo, nil
104 104
 }
105 105
 
106
+// TODO expose this from kubernetes.  I will do that, but I don't want this merge stuck on kubernetes refactoring
107
+// parseWatchResourceVersion takes a resource version argument and converts it to
108
+// the etcd version we should pass to helper.Watch(). Because resourceVersion is
109
+// an opaque value, the default watch behavior for non-zero watch is to watch
110
+// the next value (if you pass "1", you will see updates from "2" onwards).
111
+func parseWatchResourceVersion(resourceVersion, kind string) (uint64, error) {
112
+	if resourceVersion == "" || resourceVersion == "0" {
113
+		return 0, nil
114
+	}
115
+	version, err := strconv.ParseUint(resourceVersion, 10, 64)
116
+	if err != nil {
117
+		return 0, etcderr.InterpretResourceVersionError(err, kind, resourceVersion)
118
+	}
119
+	return version + 1, nil
120
+}
121
+
106 122
 // WatchImageRepositories begins watching for new, changed, or deleted ImageRepositories.
107
-func (r *Etcd) WatchImageRepositories(resourceVersion uint64, filter func(repo *api.ImageRepository) bool) (watch.Interface, error) {
108
-	return r.WatchList("/imageRepositories", resourceVersion, func(obj runtime.Object) bool {
123
+func (r *Etcd) WatchImageRepositories(resourceVersion string, filter func(repo *api.ImageRepository) bool) (watch.Interface, error) {
124
+	version, err := parseWatchResourceVersion(resourceVersion, "imageRepository")
125
+	if err != nil {
126
+		return nil, err
127
+	}
128
+
129
+	return r.WatchList("/imageRepositories", version, func(obj runtime.Object) bool {
109 130
 		repo, ok := obj.(*api.ImageRepository)
110 131
 		if !ok {
111 132
 			glog.Errorf("Unexpected object during image repository watch: %#v", obj)
... ...
@@ -3,6 +3,7 @@ package etcd
3 3
 import (
4 4
 	"fmt"
5 5
 	"reflect"
6
+	"strconv"
6 7
 	"testing"
7 8
 
8 9
 	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
... ...
@@ -19,7 +20,7 @@ import (
19 19
 )
20 20
 
21 21
 func NewTestEtcd(client tools.EtcdClient) *Etcd {
22
-	return New(tools.EtcdHelper{client, latest.Codec, latest.ResourceVersioner})
22
+	return New(tools.EtcdHelper{client, latest.Codec, tools.RuntimeVersionAdapter{latest.ResourceVersioner}})
23 23
 }
24 24
 
25 25
 func TestEtcdListImagesEmpty(t *testing.T) {
... ...
@@ -72,10 +73,10 @@ func TestEtcdListImagesEverything(t *testing.T) {
72 72
 			Node: &etcd.Node{
73 73
 				Nodes: []*etcd.Node{
74 74
 					{
75
-						Value: runtime.EncodeOrDie(latest.Codec, &api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
75
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Image{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
76 76
 					},
77 77
 					{
78
-						Value: runtime.EncodeOrDie(latest.Codec, &api.Image{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
78
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Image{TypeMeta: kubeapi.TypeMeta{ID: "bar"}}),
79 79
 					},
80 80
 				},
81 81
 			},
... ...
@@ -102,13 +103,13 @@ func TestEtcdListImagesFiltered(t *testing.T) {
102 102
 				Nodes: []*etcd.Node{
103 103
 					{
104 104
 						Value: runtime.EncodeOrDie(latest.Codec, &api.Image{
105
-							JSONBase: kubeapi.JSONBase{ID: "foo"},
105
+							TypeMeta: kubeapi.TypeMeta{ID: "foo"},
106 106
 							Labels:   map[string]string{"env": "prod"},
107 107
 						}),
108 108
 					},
109 109
 					{
110 110
 						Value: runtime.EncodeOrDie(latest.Codec, &api.Image{
111
-							JSONBase: kubeapi.JSONBase{ID: "bar"},
111
+							TypeMeta: kubeapi.TypeMeta{ID: "bar"},
112 112
 							Labels:   map[string]string{"env": "dev"},
113 113
 						}),
114 114
 					},
... ...
@@ -130,7 +131,7 @@ func TestEtcdListImagesFiltered(t *testing.T) {
130 130
 
131 131
 func TestEtcdGetImage(t *testing.T) {
132 132
 	fakeClient := tools.NewFakeEtcdClient(t)
133
-	fakeClient.Set("/images/foo", runtime.EncodeOrDie(latest.Codec, &api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
133
+	fakeClient.Set("/images/foo", runtime.EncodeOrDie(latest.Codec, &api.Image{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}), 0)
134 134
 	registry := NewTestEtcd(fakeClient)
135 135
 	image, err := registry.GetImage("foo")
136 136
 	if err != nil {
... ...
@@ -171,7 +172,7 @@ func TestEtcdCreateImage(t *testing.T) {
171 171
 	}
172 172
 	registry := NewTestEtcd(fakeClient)
173 173
 	err := registry.CreateImage(&api.Image{
174
-		JSONBase: kubeapi.JSONBase{
174
+		TypeMeta: kubeapi.TypeMeta{
175 175
 			ID: "foo",
176 176
 		},
177 177
 		DockerImageReference: "openshift/ruby-19-centos",
... ...
@@ -211,14 +212,14 @@ func TestEtcdCreateImageAlreadyExists(t *testing.T) {
211 211
 	fakeClient.Data["/images/foo"] = tools.EtcdResponseWithError{
212 212
 		R: &etcd.Response{
213 213
 			Node: &etcd.Node{
214
-				Value: runtime.EncodeOrDie(latest.Codec, &api.Image{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
214
+				Value: runtime.EncodeOrDie(latest.Codec, &api.Image{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
215 215
 			},
216 216
 		},
217 217
 		E: nil,
218 218
 	}
219 219
 	registry := NewTestEtcd(fakeClient)
220 220
 	err := registry.CreateImage(&api.Image{
221
-		JSONBase: kubeapi.JSONBase{
221
+		TypeMeta: kubeapi.TypeMeta{
222 222
 			ID: "foo",
223 223
 		},
224 224
 	})
... ...
@@ -327,10 +328,10 @@ func TestEtcdListImageRepositoriesEverything(t *testing.T) {
327 327
 			Node: &etcd.Node{
328 328
 				Nodes: []*etcd.Node{
329 329
 					{
330
-						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
330
+						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
331 331
 					},
332 332
 					{
333
-						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
333
+						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{TypeMeta: kubeapi.TypeMeta{ID: "bar"}}),
334 334
 					},
335 335
 				},
336 336
 			},
... ...
@@ -357,13 +358,13 @@ func TestEtcdListImageRepositoriesFiltered(t *testing.T) {
357 357
 				Nodes: []*etcd.Node{
358 358
 					{
359 359
 						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{
360
-							JSONBase: kubeapi.JSONBase{ID: "foo"},
360
+							TypeMeta: kubeapi.TypeMeta{ID: "foo"},
361 361
 							Labels:   map[string]string{"env": "prod"},
362 362
 						}),
363 363
 					},
364 364
 					{
365 365
 						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{
366
-							JSONBase: kubeapi.JSONBase{ID: "bar"},
366
+							TypeMeta: kubeapi.TypeMeta{ID: "bar"},
367 367
 							Labels:   map[string]string{"env": "dev"},
368 368
 						}),
369 369
 					},
... ...
@@ -385,7 +386,7 @@ func TestEtcdListImageRepositoriesFiltered(t *testing.T) {
385 385
 
386 386
 func TestEtcdGetImageRepository(t *testing.T) {
387 387
 	fakeClient := tools.NewFakeEtcdClient(t)
388
-	fakeClient.Set("/imageRepositories/foo", runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
388
+	fakeClient.Set("/imageRepositories/foo", runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}), 0)
389 389
 	registry := NewTestEtcd(fakeClient)
390 390
 	repo, err := registry.GetImageRepository("foo")
391 391
 	if err != nil {
... ...
@@ -426,7 +427,7 @@ func TestEtcdCreateImageRepository(t *testing.T) {
426 426
 	}
427 427
 	registry := NewTestEtcd(fakeClient)
428 428
 	err := registry.CreateImageRepository(&api.ImageRepository{
429
-		JSONBase: kubeapi.JSONBase{
429
+		TypeMeta: kubeapi.TypeMeta{
430 430
 			ID: "foo",
431 431
 		},
432 432
 		Labels:                map[string]string{"a": "b"},
... ...
@@ -469,14 +470,14 @@ func TestEtcdCreateImageRepositoryAlreadyExists(t *testing.T) {
469 469
 	fakeClient.Data["/imageRepositories/foo"] = tools.EtcdResponseWithError{
470 470
 		R: &etcd.Response{
471 471
 			Node: &etcd.Node{
472
-				Value: runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
472
+				Value: runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
473 473
 			},
474 474
 		},
475 475
 		E: nil,
476 476
 	}
477 477
 	registry := NewTestEtcd(fakeClient)
478 478
 	err := registry.CreateImageRepository(&api.ImageRepository{
479
-		JSONBase: kubeapi.JSONBase{
479
+		TypeMeta: kubeapi.TypeMeta{
480 480
 			ID: "foo",
481 481
 		},
482 482
 	})
... ...
@@ -492,10 +493,10 @@ func TestEtcdUpdateImageRepository(t *testing.T) {
492 492
 	fakeClient := tools.NewFakeEtcdClient(t)
493 493
 	fakeClient.TestIndex = true
494 494
 
495
-	resp, _ := fakeClient.Set("/imageRepositories/foo", runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
495
+	resp, _ := fakeClient.Set("/imageRepositories/foo", runtime.EncodeOrDie(latest.Codec, &api.ImageRepository{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}), 0)
496 496
 	registry := NewTestEtcd(fakeClient)
497 497
 	err := registry.UpdateImageRepository(&api.ImageRepository{
498
-		JSONBase:              kubeapi.JSONBase{ID: "foo", ResourceVersion: resp.Node.ModifiedIndex},
498
+		TypeMeta:              kubeapi.TypeMeta{ID: "foo", ResourceVersion: strconv.FormatUint(resp.Node.ModifiedIndex, 10)},
499 499
 		DockerImageRepository: "some/repo",
500 500
 	})
501 501
 	if err != nil {
... ...
@@ -551,7 +552,7 @@ func TestEtcdWatchImageRepositories(t *testing.T) {
551 551
 	registry := NewTestEtcd(fakeClient)
552 552
 	filterFields := labels.SelectorFromSet(labels.Set{"ID": "foo"})
553 553
 
554
-	watching, err := registry.WatchImageRepositories(1, func(repo *api.ImageRepository) bool {
554
+	watching, err := registry.WatchImageRepositories("1", func(repo *api.ImageRepository) bool {
555 555
 		fields := labels.Set{
556 556
 			"ID": repo.ID,
557 557
 		}
... ...
@@ -562,7 +563,7 @@ func TestEtcdWatchImageRepositories(t *testing.T) {
562 562
 	}
563 563
 	fakeClient.WaitForWatchCompletion()
564 564
 
565
-	repo := &api.ImageRepository{JSONBase: kubeapi.JSONBase{ID: "foo"}}
565
+	repo := &api.ImageRepository{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}
566 566
 	repoBytes, _ := latest.Codec.Encode(repo)
567 567
 	fakeClient.WatchResponse <- &etcd.Response{
568 568
 		Action: "set",
... ...
@@ -56,12 +56,12 @@ func TestListImagesPopulatedList(t *testing.T) {
56 56
 	mockRegistry.Images = &api.ImageList{
57 57
 		Items: []api.Image{
58 58
 			{
59
-				JSONBase: kubeapi.JSONBase{
59
+				TypeMeta: kubeapi.TypeMeta{
60 60
 					ID: "foo",
61 61
 				},
62 62
 			},
63 63
 			{
64
-				JSONBase: kubeapi.JSONBase{
64
+				TypeMeta: kubeapi.TypeMeta{
65 65
 					ID: "bar",
66 66
 				},
67 67
 			},
... ...
@@ -114,7 +114,7 @@ func TestCreateRegistrySaveError(t *testing.T) {
114 114
 	storage := REST{registry: mockRegistry}
115 115
 
116 116
 	channel, err := storage.Create(nil, &api.Image{
117
-		JSONBase:             kubeapi.JSONBase{ID: "foo"},
117
+		TypeMeta:             kubeapi.TypeMeta{ID: "foo"},
118 118
 		DockerImageReference: "openshift/ruby-19-centos",
119 119
 	})
120 120
 	if channel == nil {
... ...
@@ -143,7 +143,7 @@ func TestCreateImageOK(t *testing.T) {
143 143
 	storage := REST{registry: mockRegistry}
144 144
 
145 145
 	channel, err := storage.Create(nil, &api.Image{
146
-		JSONBase:             kubeapi.JSONBase{ID: "foo"},
146
+		TypeMeta:             kubeapi.TypeMeta{ID: "foo"},
147 147
 		DockerImageReference: "openshift/ruby-19-centos",
148 148
 	})
149 149
 	if channel == nil {
... ...
@@ -184,7 +184,7 @@ func TestGetImageError(t *testing.T) {
184 184
 func TestGetImageOK(t *testing.T) {
185 185
 	mockRegistry := test.NewImageRegistry()
186 186
 	mockRegistry.Image = &api.Image{
187
-		JSONBase:             kubeapi.JSONBase{ID: "foo"},
187
+		TypeMeta:             kubeapi.TypeMeta{ID: "foo"},
188 188
 		DockerImageReference: "openshift/ruby-19-centos",
189 189
 	}
190 190
 	storage := REST{registry: mockRegistry}
... ...
@@ -13,7 +13,7 @@ type Registry interface {
13 13
 	// GetImageRepository retrieves a specific image repository.
14 14
 	GetImageRepository(id string) (*api.ImageRepository, error)
15 15
 	// WatchImageRepositories watches for new/changed/deleted image repositories.
16
-	WatchImageRepositories(resourceVersion uint64, filter func(repo *api.ImageRepository) bool) (watch.Interface, error)
16
+	WatchImageRepositories(resourceVersion string, filter func(repo *api.ImageRepository) bool) (watch.Interface, error)
17 17
 	// CreateImageRepository creates a new image repository.
18 18
 	CreateImageRepository(repo *api.ImageRepository) error
19 19
 	// UpdateImageRepository updates an image repository.
... ...
@@ -49,7 +49,7 @@ func (s *REST) Get(ctx kubeapi.Context, id string) (runtime.Object, error) {
49 49
 }
50 50
 
51 51
 // Watch begins watching for new, changed, or deleted ImageRepositories.
52
-func (s *REST) Watch(ctx kubeapi.Context, label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) {
52
+func (s *REST) Watch(ctx kubeapi.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) {
53 53
 	return s.registry.WatchImageRepositories(resourceVersion, func(repo *api.ImageRepository) bool {
54 54
 		fields := labels.Set{
55 55
 			"ID": repo.ID,
... ...
@@ -29,7 +29,7 @@ func TestGetImageRepositoryError(t *testing.T) {
29 29
 func TestGetImageRepositoryOK(t *testing.T) {
30 30
 	mockRepositoryRegistry := test.NewImageRepositoryRegistry()
31 31
 	mockRepositoryRegistry.ImageRepository = &api.ImageRepository{
32
-		JSONBase:              kubeapi.JSONBase{ID: "foo"},
32
+		TypeMeta:              kubeapi.TypeMeta{ID: "foo"},
33 33
 		DockerImageRepository: "openshift/ruby-19-centos",
34 34
 	}
35 35
 	storage := REST{registry: mockRepositoryRegistry}
... ...
@@ -89,12 +89,12 @@ func TestListImageRepositoriesPopulatedList(t *testing.T) {
89 89
 	mockRepositoryRegistry.ImageRepositories = &api.ImageRepositoryList{
90 90
 		Items: []api.ImageRepository{
91 91
 			{
92
-				JSONBase: kubeapi.JSONBase{
92
+				TypeMeta: kubeapi.TypeMeta{
93 93
 					ID: "foo",
94 94
 				},
95 95
 			},
96 96
 			{
97
-				JSONBase: kubeapi.JSONBase{
97
+				TypeMeta: kubeapi.TypeMeta{
98 98
 					ID: "bar",
99 99
 				},
100 100
 			},
... ...
@@ -200,7 +200,7 @@ func TestUpdateRegistryErrorSaving(t *testing.T) {
200 200
 	storage := REST{registry: mockRepositoryRegistry}
201 201
 
202 202
 	channel, err := storage.Update(nil, &api.ImageRepository{
203
-		JSONBase: kubeapi.JSONBase{ID: "bar"},
203
+		TypeMeta: kubeapi.TypeMeta{ID: "bar"},
204 204
 	})
205 205
 	if err != nil {
206 206
 		t.Errorf("Unexpected non-nil error: %#v", err)
... ...
@@ -220,7 +220,7 @@ func TestUpdateImageRepositoryOK(t *testing.T) {
220 220
 	storage := REST{registry: mockRepositoryRegistry}
221 221
 
222 222
 	channel, err := storage.Update(nil, &api.ImageRepository{
223
-		JSONBase: kubeapi.JSONBase{ID: "bar"},
223
+		TypeMeta: kubeapi.TypeMeta{ID: "bar"},
224 224
 	})
225 225
 	if err != nil {
226 226
 		t.Errorf("Unexpected non-nil error: %#v", err)
... ...
@@ -109,7 +109,7 @@ func TestCreateImageRepositoryMappingFindError(t *testing.T) {
109 109
 	mapping := api.ImageRepositoryMapping{
110 110
 		DockerImageRepository: "localhost:5000/someproject/somerepo",
111 111
 		Image: api.Image{
112
-			JSONBase: kubeapi.JSONBase{
112
+			TypeMeta: kubeapi.TypeMeta{
113 113
 				ID: "imageID1",
114 114
 			},
115 115
 			DockerImageReference: "localhost:5000/someproject/somerepo:imageID1",
... ...
@@ -135,7 +135,7 @@ func TestCreateImageRepositoryMappingNotFound(t *testing.T) {
135 135
 	imageRepositoryRegistry.ImageRepositories = &api.ImageRepositoryList{
136 136
 		Items: []api.ImageRepository{
137 137
 			{
138
-				JSONBase: kubeapi.JSONBase{
138
+				TypeMeta: kubeapi.TypeMeta{
139 139
 					ID: "repo1",
140 140
 				},
141 141
 				DockerImageRepository: "localhost:5000/test/repo",
... ...
@@ -147,7 +147,7 @@ func TestCreateImageRepositoryMappingNotFound(t *testing.T) {
147 147
 	mapping := api.ImageRepositoryMapping{
148 148
 		DockerImageRepository: "localhost:5000/someproject/somerepo",
149 149
 		Image: api.Image{
150
-			JSONBase: kubeapi.JSONBase{
150
+			TypeMeta: kubeapi.TypeMeta{
151 151
 				ID: "imageID1",
152 152
 			},
153 153
 			DockerImageReference: "localhost:5000/someproject/somerepo:imageID1",
... ...
@@ -173,7 +173,7 @@ func TestCreateImageRepositoryMapping(t *testing.T) {
173 173
 	imageRepositoryRegistry.ImageRepositories = &api.ImageRepositoryList{
174 174
 		Items: []api.ImageRepository{
175 175
 			{
176
-				JSONBase: kubeapi.JSONBase{
176
+				TypeMeta: kubeapi.TypeMeta{
177 177
 					ID: "repo1",
178 178
 				},
179 179
 				DockerImageRepository: "localhost:5000/someproject/somerepo",
... ...
@@ -185,7 +185,7 @@ func TestCreateImageRepositoryMapping(t *testing.T) {
185 185
 	mapping := api.ImageRepositoryMapping{
186 186
 		DockerImageRepository: "localhost:5000/someproject/somerepo",
187 187
 		Image: api.Image{
188
-			JSONBase: kubeapi.JSONBase{
188
+			TypeMeta: kubeapi.TypeMeta{
189 189
 				ID: "imageID1",
190 190
 			},
191 191
 			DockerImageReference: "localhost:5000/someproject/somerepo:imageID1",
... ...
@@ -33,7 +33,7 @@ func (r *ImageRepositoryRegistry) GetImageRepository(id string) (*api.ImageRepos
33 33
 	return r.ImageRepository, r.Err
34 34
 }
35 35
 
36
-func (r *ImageRepositoryRegistry) WatchImageRepositories(resourceVersion uint64, filter func(repo *api.ImageRepository) bool) (watch.Interface, error) {
36
+func (r *ImageRepositoryRegistry) WatchImageRepositories(resourceVersion string, filter func(repo *api.ImageRepository) bool) (watch.Interface, error) {
37 37
 	return nil, r.Err
38 38
 }
39 39
 
... ...
@@ -5,7 +5,7 @@ import (
5 5
 )
6 6
 
7 7
 type AccessToken struct {
8
-	api.JSONBase `json:",inline" yaml:",inline"`
8
+	api.TypeMeta `json:",inline" yaml:",inline"`
9 9
 	Labels       map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
10 10
 
11 11
 	// Name is the unique value for an access token - also known as its secret
... ...
@@ -20,7 +20,7 @@ type AccessToken struct {
20 20
 }
21 21
 
22 22
 type AuthorizeToken struct {
23
-	api.JSONBase `json:",inline" yaml:",inline"`
23
+	api.TypeMeta `json:",inline" yaml:",inline"`
24 24
 
25 25
 	// Name is the unique value for an authorization token - also known as its secret
26 26
 	Name string `json:"name,omitempty" yaml:"name,omitempty"`
... ...
@@ -49,7 +49,7 @@ type AuthorizeToken struct {
49 49
 }
50 50
 
51 51
 type Client struct {
52
-	api.JSONBase `json:",inline" yaml:",inline"`
52
+	api.TypeMeta `json:",inline" yaml:",inline"`
53 53
 	Labels       map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
54 54
 
55 55
 	// Name is the unique identifier of the client
... ...
@@ -63,7 +63,7 @@ type Client struct {
63 63
 }
64 64
 
65 65
 type ClientAuthorization struct {
66
-	api.JSONBase `json:",inline" yaml:",inline"`
66
+	api.TypeMeta `json:",inline" yaml:",inline"`
67 67
 
68 68
 	// ClientName references the client that created this authorization
69 69
 	ClientName string `json:"clientName,omitempty" yaml:"clientName,omitempty"`
... ...
@@ -80,22 +80,22 @@ type ClientAuthorization struct {
80 80
 }
81 81
 
82 82
 type AccessTokenList struct {
83
-	api.JSONBase `json:",inline" yaml:",inline"`
83
+	api.TypeMeta `json:",inline" yaml:",inline"`
84 84
 	Items        []AccessToken `json:"items,omitempty" yaml:"items,omitempty"`
85 85
 }
86 86
 
87 87
 type AuthorizeTokenList struct {
88
-	api.JSONBase `json:",inline" yaml:",inline"`
88
+	api.TypeMeta `json:",inline" yaml:",inline"`
89 89
 	Items        []AuthorizeToken `json:"items,omitempty" yaml:"items,omitempty"`
90 90
 }
91 91
 
92 92
 type ClientList struct {
93
-	api.JSONBase `json:",inline" yaml:",inline"`
93
+	api.TypeMeta `json:",inline" yaml:",inline"`
94 94
 	Items        []Client `json:"items,omitempty" yaml:"items,omitempty"`
95 95
 }
96 96
 
97 97
 type ClientAuthorizationList struct {
98
-	api.JSONBase `json:",inline" yaml:",inline"`
98
+	api.TypeMeta `json:",inline" yaml:",inline"`
99 99
 	Items        []ClientAuthorization `json:"items,omitempty" yaml:"items,omitempty"`
100 100
 }
101 101
 
... ...
@@ -5,7 +5,7 @@ import (
5 5
 )
6 6
 
7 7
 type AccessToken struct {
8
-	api.JSONBase `json:",inline" yaml:",inline"`
8
+	api.TypeMeta `json:",inline" yaml:",inline"`
9 9
 	Labels       map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
10 10
 
11 11
 	// Name is the unique value for an access token - also known as its secret
... ...
@@ -20,7 +20,7 @@ type AccessToken struct {
20 20
 }
21 21
 
22 22
 type AuthorizeToken struct {
23
-	api.JSONBase `json:",inline" yaml:",inline"`
23
+	api.TypeMeta `json:",inline" yaml:",inline"`
24 24
 
25 25
 	// Name is the unique value for an authorization token - also known as its secret
26 26
 	Name string `json:"name,omitempty" yaml:"name,omitempty"`
... ...
@@ -49,7 +49,7 @@ type AuthorizeToken struct {
49 49
 }
50 50
 
51 51
 type Client struct {
52
-	api.JSONBase `json:",inline" yaml:",inline"`
52
+	api.TypeMeta `json:",inline" yaml:",inline"`
53 53
 	Labels       map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
54 54
 
55 55
 	// Name is the unique identifier of the client
... ...
@@ -63,7 +63,7 @@ type Client struct {
63 63
 }
64 64
 
65 65
 type ClientAuthorization struct {
66
-	api.JSONBase `json:",inline" yaml:",inline"`
66
+	api.TypeMeta `json:",inline" yaml:",inline"`
67 67
 
68 68
 	// ClientName references the client that created this authorization
69 69
 	ClientName string `json:"clientName,omitempty" yaml:"clientName,omitempty"`
... ...
@@ -80,22 +80,22 @@ type ClientAuthorization struct {
80 80
 }
81 81
 
82 82
 type AccessTokenList struct {
83
-	api.JSONBase `json:",inline" yaml:",inline"`
83
+	api.TypeMeta `json:",inline" yaml:",inline"`
84 84
 	Items        []AccessToken `json:"items,omitempty" yaml:"items,omitempty"`
85 85
 }
86 86
 
87 87
 type AuthorizeTokenList struct {
88
-	api.JSONBase `json:",inline" yaml:",inline"`
88
+	api.TypeMeta `json:",inline" yaml:",inline"`
89 89
 	Items        []AuthorizeToken `json:"items,omitempty" yaml:"items,omitempty"`
90 90
 }
91 91
 
92 92
 type ClientList struct {
93
-	api.JSONBase `json:",inline" yaml:",inline"`
93
+	api.TypeMeta `json:",inline" yaml:",inline"`
94 94
 	Items        []Client `json:"items,omitempty" yaml:"items,omitempty"`
95 95
 }
96 96
 
97 97
 type ClientAuthorizationList struct {
98
-	api.JSONBase `json:",inline" yaml:",inline"`
98
+	api.TypeMeta `json:",inline" yaml:",inline"`
99 99
 	Items        []ClientAuthorization `json:"items,omitempty" yaml:"items,omitempty"`
100 100
 }
101 101
 
... ...
@@ -34,7 +34,7 @@ func (r *Etcd) GetAccessToken(name string) (token *api.AccessToken, err error) {
34 34
 
35 35
 func (r *Etcd) ListAccessTokens(selector labels.Selector) (*api.AccessTokenList, error) {
36 36
 	list := api.AccessTokenList{}
37
-	err := r.ExtractList("/accessTokens", &list.Items, &list.ResourceVersion)
37
+	err := r.ExtractToList("/accessTokens", &list)
38 38
 	if err != nil && !tools.IsEtcdNotFound(err) {
39 39
 		return nil, err
40 40
 	}
... ...
@@ -75,7 +75,7 @@ func (r *Etcd) GetAuthorizeToken(name string) (token *api.AuthorizeToken, err er
75 75
 
76 76
 func (r *Etcd) ListAuthorizeTokens(selector labels.Selector) (*api.AuthorizeTokenList, error) {
77 77
 	list := api.AuthorizeTokenList{}
78
-	err := r.ExtractList("/authorizeTokens", &list.Items, &list.ResourceVersion)
78
+	err := r.ExtractToList("/authorizeTokens", &list)
79 79
 	if err != nil && !tools.IsEtcdNotFound(err) {
80 80
 		return nil, err
81 81
 	}
... ...
@@ -109,7 +109,7 @@ func (r *Etcd) GetClient(name string) (client *api.Client, err error) {
109 109
 
110 110
 func (r *Etcd) ListClients(selector labels.Selector) (*api.ClientList, error) {
111 111
 	list := api.ClientList{}
112
-	err := r.ExtractList("/clients", &list.Items, &list.ResourceVersion)
112
+	err := r.ExtractToList("/clients", &list)
113 113
 	if err != nil && !tools.IsEtcdNotFound(err) {
114 114
 		return nil, err
115 115
 	}
... ...
@@ -154,7 +154,7 @@ func (r *Etcd) GetClientAuthorization(name string) (client *api.ClientAuthorizat
154 154
 
155 155
 func (r *Etcd) ListClientAuthorizations(label, field labels.Selector) (*api.ClientAuthorizationList, error) {
156 156
 	list := api.ClientAuthorizationList{}
157
-	err := r.ExtractList("/clients", &list.Items, &list.ResourceVersion)
157
+	err := r.ExtractToList("/clients", &list)
158 158
 	if err != nil && !tools.IsEtcdNotFound(err) {
159 159
 		return nil, err
160 160
 	}
... ...
@@ -90,7 +90,7 @@ func (s *storage) GetClient(id string) (osin.Client, error) {
90 90
 // SaveAuthorize saves authorize data.
91 91
 func (s *storage) SaveAuthorize(data *osin.AuthorizeData) error {
92 92
 	token := &api.AuthorizeToken{
93
-		JSONBase: kapi.JSONBase{
93
+		TypeMeta: kapi.TypeMeta{
94 94
 			CreationTimestamp: util.Time{data.CreatedAt},
95 95
 		},
96 96
 		Name:        data.Code,
... ...
@@ -145,7 +145,7 @@ func (s *storage) RemoveAuthorize(code string) error {
145 145
 // If RefreshToken is not blank, it must save in a way that can be loaded using LoadRefresh.
146 146
 func (s *storage) SaveAccess(data *osin.AccessData) error {
147 147
 	token := &api.AccessToken{
148
-		JSONBase: kapi.JSONBase{
148
+		TypeMeta: kapi.TypeMeta{
149 149
 			CreationTimestamp: util.Time{data.CreatedAt},
150 150
 		},
151 151
 		Name:         data.AccessToken,
... ...
@@ -6,13 +6,13 @@ import (
6 6
 
7 7
 // ProjectList is a list of Project objects.
8 8
 type ProjectList struct {
9
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
9
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
10 10
 	Items            []Project `json:"items,omitempty" yaml:"items,omitempty"`
11 11
 }
12 12
 
13 13
 // Project is a logical top-level container for a set of origin resources
14 14
 type Project struct {
15
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
15
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
16 16
 	Labels           map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
17 17
 	DisplayName      string            `json:"displayName,omitempty" yaml:"displayName,omitempty"`
18 18
 	Description      string            `json:"description,omitempty" yaml:"description,omitempty"`
... ...
@@ -6,13 +6,13 @@ import (
6 6
 
7 7
 // ProjectList is a list of Project objects.
8 8
 type ProjectList struct {
9
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
9
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
10 10
 	Items            []Project `json:"items,omitempty" yaml:"items,omitempty"`
11 11
 }
12 12
 
13 13
 // Project is a logical top-level container for a set of origin resources
14 14
 type Project struct {
15
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
15
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
16 16
 	Labels           map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
17 17
 	DisplayName      string            `json:"displayName,omitempty" yaml:"displayName,omitempty"`
18 18
 	Description      string            `json:"description,omitempty" yaml:"description,omitempty"`
... ...
@@ -16,7 +16,7 @@ func TestValidateProject(t *testing.T) {
16 16
 		{
17 17
 			name: "missing id",
18 18
 			project: api.Project{
19
-				JSONBase:    kubeapi.JSONBase{Namespace: kubeapi.NamespaceDefault},
19
+				TypeMeta:    kubeapi.TypeMeta{Namespace: kubeapi.NamespaceDefault},
20 20
 				DisplayName: "hi",
21 21
 				Description: "This is a description",
22 22
 			},
... ...
@@ -26,7 +26,7 @@ func TestValidateProject(t *testing.T) {
26 26
 		{
27 27
 			name: "invalid id",
28 28
 			project: api.Project{
29
-				JSONBase:    kubeapi.JSONBase{ID: "141-.124.$", Namespace: kubeapi.NamespaceDefault},
29
+				TypeMeta:    kubeapi.TypeMeta{ID: "141-.124.$", Namespace: kubeapi.NamespaceDefault},
30 30
 				DisplayName: "hi",
31 31
 				Description: "This is a description",
32 32
 			},
... ...
@@ -36,7 +36,7 @@ func TestValidateProject(t *testing.T) {
36 36
 		{
37 37
 			name: "missing namespace",
38 38
 			project: api.Project{
39
-				JSONBase:    kubeapi.JSONBase{ID: "foo", Namespace: ""},
39
+				TypeMeta:    kubeapi.TypeMeta{ID: "foo", Namespace: ""},
40 40
 				DisplayName: "hi",
41 41
 				Description: "This is a description",
42 42
 			},
... ...
@@ -46,7 +46,7 @@ func TestValidateProject(t *testing.T) {
46 46
 		{
47 47
 			name: "invalid namespace",
48 48
 			project: api.Project{
49
-				JSONBase:    kubeapi.JSONBase{ID: "foo", Namespace: "141-.124.$"},
49
+				TypeMeta:    kubeapi.TypeMeta{ID: "foo", Namespace: "141-.124.$"},
50 50
 				DisplayName: "hi",
51 51
 				Description: "This is a description",
52 52
 			},
... ...
@@ -56,7 +56,7 @@ func TestValidateProject(t *testing.T) {
56 56
 		{
57 57
 			name: "invalid description",
58 58
 			project: api.Project{
59
-				JSONBase:    kubeapi.JSONBase{ID: "foo", Namespace: "foo"},
59
+				TypeMeta:    kubeapi.TypeMeta{ID: "foo", Namespace: "foo"},
60 60
 				DisplayName: "hi",
61 61
 				Description: "This is a \n description",
62 62
 			},
... ...
@@ -66,7 +66,7 @@ func TestValidateProject(t *testing.T) {
66 66
 		{
67 67
 			name: "invalid display name",
68 68
 			project: api.Project{
69
-				JSONBase:    kubeapi.JSONBase{ID: "foo", Namespace: "foo"},
69
+				TypeMeta:    kubeapi.TypeMeta{ID: "foo", Namespace: "foo"},
70 70
 				DisplayName: "h\t\ni",
71 71
 				Description: "This is a description",
72 72
 			},
... ...
@@ -83,7 +83,7 @@ func TestValidateProject(t *testing.T) {
83 83
 	}
84 84
 
85 85
 	project := api.Project{
86
-		JSONBase:    kubeapi.JSONBase{ID: "foo", Namespace: kubeapi.NamespaceDefault},
86
+		TypeMeta:    kubeapi.TypeMeta{ID: "foo", Namespace: kubeapi.NamespaceDefault},
87 87
 		DisplayName: "hi",
88 88
 		Description: "This is a description",
89 89
 	}
... ...
@@ -41,7 +41,7 @@ func makeProjectKey(ctx kubeapi.Context, id string) string {
41 41
 // ListProjects retrieves a list of projects that match selector.
42 42
 func (r *Etcd) ListProjects(ctx kubeapi.Context, selector labels.Selector) (*api.ProjectList, error) {
43 43
 	list := api.ProjectList{}
44
-	err := r.ExtractList(makeProjectListKey(ctx), &list.Items, &list.ResourceVersion)
44
+	err := r.ExtractToList(makeProjectListKey(ctx), &list)
45 45
 	if err != nil {
46 46
 		return nil, err
47 47
 	}
... ...
@@ -16,7 +16,7 @@ import (
16 16
 )
17 17
 
18 18
 func NewTestEtcd(client tools.EtcdClient) *Etcd {
19
-	return New(tools.EtcdHelper{client, latest.Codec, latest.ResourceVersioner})
19
+	return New(tools.EtcdHelper{client, latest.Codec, tools.RuntimeVersionAdapter{latest.ResourceVersioner}})
20 20
 }
21 21
 
22 22
 func TestEtcdListProjectsEmpty(t *testing.T) {
... ...
@@ -72,10 +72,10 @@ func TestEtcdListProjectsEverything(t *testing.T) {
72 72
 			Node: &etcd.Node{
73 73
 				Nodes: []*etcd.Node{
74 74
 					{
75
-						Value: runtime.EncodeOrDie(latest.Codec, &api.Project{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
75
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Project{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
76 76
 					},
77 77
 					{
78
-						Value: runtime.EncodeOrDie(latest.Codec, &api.Project{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
78
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Project{TypeMeta: kubeapi.TypeMeta{ID: "bar"}}),
79 79
 					},
80 80
 				},
81 81
 			},
... ...
@@ -103,13 +103,13 @@ func TestEtcdListProjectsFiltered(t *testing.T) {
103 103
 				Nodes: []*etcd.Node{
104 104
 					{
105 105
 						Value: runtime.EncodeOrDie(latest.Codec, &api.Project{
106
-							JSONBase: kubeapi.JSONBase{ID: "foo"},
106
+							TypeMeta: kubeapi.TypeMeta{ID: "foo"},
107 107
 							Labels:   map[string]string{"env": "prod"},
108 108
 						}),
109 109
 					},
110 110
 					{
111 111
 						Value: runtime.EncodeOrDie(latest.Codec, &api.Project{
112
-							JSONBase: kubeapi.JSONBase{ID: "bar"},
112
+							TypeMeta: kubeapi.TypeMeta{ID: "bar"},
113 113
 							Labels:   map[string]string{"env": "dev"},
114 114
 						}),
115 115
 					},
... ...
@@ -132,7 +132,7 @@ func TestEtcdListProjectsFiltered(t *testing.T) {
132 132
 func TestEtcdGetProject(t *testing.T) {
133 133
 	ctx := kubeapi.NewContext()
134 134
 	fakeClient := tools.NewFakeEtcdClient(t)
135
-	fakeClient.Set(makeProjectKey(ctx, "foo"), runtime.EncodeOrDie(latest.Codec, &api.Project{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
135
+	fakeClient.Set(makeProjectKey(ctx, "foo"), runtime.EncodeOrDie(latest.Codec, &api.Project{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}), 0)
136 136
 	registry := NewTestEtcd(fakeClient)
137 137
 	project, err := registry.GetProject(ctx, "foo")
138 138
 	if err != nil {
... ...
@@ -175,7 +175,7 @@ func TestEtcdCreateProject(t *testing.T) {
175 175
 	}
176 176
 	registry := NewTestEtcd(fakeClient)
177 177
 	err := registry.CreateProject(ctx, &api.Project{
178
-		JSONBase: kubeapi.JSONBase{
178
+		TypeMeta: kubeapi.TypeMeta{
179 179
 			ID: "foo",
180 180
 		},
181 181
 	})
... ...
@@ -204,14 +204,14 @@ func TestEtcdCreateProjectAlreadyExists(t *testing.T) {
204 204
 	fakeClient.Data[makeProjectKey(ctx, "foo")] = tools.EtcdResponseWithError{
205 205
 		R: &etcd.Response{
206 206
 			Node: &etcd.Node{
207
-				Value: runtime.EncodeOrDie(latest.Codec, &api.Project{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
207
+				Value: runtime.EncodeOrDie(latest.Codec, &api.Project{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
208 208
 			},
209 209
 		},
210 210
 		E: nil,
211 211
 	}
212 212
 	registry := NewTestEtcd(fakeClient)
213 213
 	err := registry.CreateProject(ctx, &api.Project{
214
-		JSONBase: kubeapi.JSONBase{
214
+		TypeMeta: kubeapi.TypeMeta{
215 215
 			ID: "foo",
216 216
 		},
217 217
 	})
... ...
@@ -56,12 +56,12 @@ func TestListProjectsPopulatedList(t *testing.T) {
56 56
 	mockRegistry.Projects = &api.ProjectList{
57 57
 		Items: []api.Project{
58 58
 			{
59
-				JSONBase: kubeapi.JSONBase{
59
+				TypeMeta: kubeapi.TypeMeta{
60 60
 					ID: "foo",
61 61
 				},
62 62
 			},
63 63
 			{
64
-				JSONBase: kubeapi.JSONBase{
64
+				TypeMeta: kubeapi.TypeMeta{
65 65
 					ID: "bar",
66 66
 				},
67 67
 			},
... ...
@@ -114,7 +114,7 @@ func TestCreateRegistrySaveError(t *testing.T) {
114 114
 	storage := REST{registry: mockRegistry}
115 115
 
116 116
 	channel, err := storage.Create(nil, &api.Project{
117
-		JSONBase: kubeapi.JSONBase{ID: "foo"},
117
+		TypeMeta: kubeapi.TypeMeta{ID: "foo"},
118 118
 	})
119 119
 	if channel == nil {
120 120
 		t.Errorf("Expected nil channel, got %v", channel)
... ...
@@ -143,7 +143,7 @@ func TestCreateProjectOK(t *testing.T) {
143 143
 	storage := REST{registry: mockRegistry}
144 144
 
145 145
 	channel, err := storage.Create(nil, &api.Project{
146
-		JSONBase: kubeapi.JSONBase{ID: "foo"},
146
+		TypeMeta: kubeapi.TypeMeta{ID: "foo"},
147 147
 	})
148 148
 	if channel == nil {
149 149
 		t.Errorf("Expected nil channel, got %v", channel)
... ...
@@ -184,7 +184,7 @@ func TestGetProjectError(t *testing.T) {
184 184
 func TestGetProjectOK(t *testing.T) {
185 185
 	mockRegistry := test.NewProjectRegistry()
186 186
 	mockRegistry.Project = &api.Project{
187
-		JSONBase: kubeapi.JSONBase{ID: "foo"},
187
+		TypeMeta: kubeapi.TypeMeta{ID: "foo"},
188 188
 	}
189 189
 	storage := REST{registry: mockRegistry}
190 190
 
... ...
@@ -6,7 +6,7 @@ import (
6 6
 
7 7
 // Route encapsulates the inputs needed to connect a DNS/alias to a service proxy.
8 8
 type Route struct {
9
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
9
+	kubeapi.TypeMeta         `json:",inline" yaml:",inline"`
10 10
 
11 11
 	// Required: Alias/DNS that points to the service
12 12
 	// Can be host or host:port
... ...
@@ -22,6 +22,6 @@ type Route struct {
22 22
 
23 23
 // RouteList is a collection of Routes.
24 24
 type RouteList struct {
25
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
26
-	Items            []Route `json:"items,omitempty" yaml:"items,omitempty"`
25
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
26
+	Items []Route    `json:"items,omitempty" yaml:"items,omitempty"`
27 27
 }
... ...
@@ -6,7 +6,7 @@ import (
6 6
 
7 7
 // Route encapsulates the inputs needed to connect a DNS/alias to a service proxy.
8 8
 type Route struct {
9
-	v1beta1.JSONBase         `json:",inline" yaml:",inline"`
9
+	v1beta1.TypeMeta         `json:",inline" yaml:",inline"`
10 10
 
11 11
 	// Required: Alias/DNS that points to the service
12 12
 	// Can be host or host:port
... ...
@@ -22,6 +22,6 @@ type Route struct {
22 22
 
23 23
 // RouteList is a collection of Routes.
24 24
 type RouteList struct {
25
-	v1beta1.JSONBase `json:",inline" yaml:",inline"`
25
+	v1beta1.TypeMeta `json:",inline" yaml:",inline"`
26 26
 	Items []Route    `json:"items,omitempty" yaml:"items,omitempty"`
27 27
 }
... ...
@@ -30,7 +30,7 @@ func makeRouteKey(id string) string {
30 30
 // ListRoutes obtains a list of Routes.
31 31
 func (registry *Etcd) ListRoutes(selector labels.Selector) (*api.RouteList, error) {
32 32
 	allRoutes := api.RouteList{}
33
-	err := registry.ExtractList("/routes", &allRoutes.Items, &allRoutes.ResourceVersion)
33
+	err := registry.ExtractToList("/routes", &allRoutes)
34 34
 	if err != nil {
35 35
 		return nil, err
36 36
 	}
... ...
@@ -17,7 +17,7 @@ import (
17 17
 )
18 18
 
19 19
 func NewTestEtcd(client tools.EtcdClient) *Etcd {
20
-	return New(tools.EtcdHelper{client, latest.Codec, latest.ResourceVersioner})
20
+	return New(tools.EtcdHelper{client, latest.Codec, tools.RuntimeVersionAdapter{latest.ResourceVersioner}})
21 21
 }
22 22
 
23 23
 func TestEtcdListEmptyRoutes(t *testing.T) {
... ...
@@ -70,10 +70,10 @@ func TestEtcdListEverythingRoutes(t *testing.T) {
70 70
 			Node: &etcd.Node{
71 71
 				Nodes: []*etcd.Node{
72 72
 					{
73
-						Value: runtime.EncodeOrDie(latest.Codec, &api.Route{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
73
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Route{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
74 74
 					},
75 75
 					{
76
-						Value: runtime.EncodeOrDie(latest.Codec, &api.Route{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
76
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Route{TypeMeta: kubeapi.TypeMeta{ID: "bar"}}),
77 77
 					},
78 78
 				},
79 79
 			},
... ...
@@ -100,13 +100,13 @@ func TestEtcdListFilteredRoutes(t *testing.T) {
100 100
 				Nodes: []*etcd.Node{
101 101
 					{
102 102
 						Value: runtime.EncodeOrDie(latest.Codec, &api.Route{
103
-							JSONBase: kubeapi.JSONBase{ID: "foo"},
103
+							TypeMeta: kubeapi.TypeMeta{ID: "foo"},
104 104
 							Labels:   map[string]string{"env": "prod"},
105 105
 						}),
106 106
 					},
107 107
 					{
108 108
 						Value: runtime.EncodeOrDie(latest.Codec, &api.Route{
109
-							JSONBase: kubeapi.JSONBase{ID: "bar"},
109
+							TypeMeta: kubeapi.TypeMeta{ID: "bar"},
110 110
 							Labels:   map[string]string{"env": "dev"},
111 111
 						}),
112 112
 					},
... ...
@@ -128,7 +128,7 @@ func TestEtcdListFilteredRoutes(t *testing.T) {
128 128
 
129 129
 func TestEtcdGetRoutes(t *testing.T) {
130 130
 	fakeClient := tools.NewFakeEtcdClient(t)
131
-	fakeClient.Set("/routes/foo", runtime.EncodeOrDie(latest.Codec, &api.Route{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
131
+	fakeClient.Set("/routes/foo", runtime.EncodeOrDie(latest.Codec, &api.Route{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}), 0)
132 132
 	registry := NewTestEtcd(fakeClient)
133 133
 	route, err := registry.GetRoute("foo")
134 134
 	if err != nil {
... ...
@@ -169,7 +169,7 @@ func TestEtcdCreateRoutes(t *testing.T) {
169 169
 	}
170 170
 	registry := NewTestEtcd(fakeClient)
171 171
 	err := registry.CreateRoute(&api.Route{
172
-		JSONBase: kubeapi.JSONBase{
172
+		TypeMeta: kubeapi.TypeMeta{
173 173
 			ID: "foo",
174 174
 		},
175 175
 	})
... ...
@@ -197,14 +197,14 @@ func TestEtcdCreateAlreadyExistsRoutes(t *testing.T) {
197 197
 	fakeClient.Data["/routes/foo"] = tools.EtcdResponseWithError{
198 198
 		R: &etcd.Response{
199 199
 			Node: &etcd.Node{
200
-				Value: runtime.EncodeOrDie(latest.Codec, &api.Route{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
200
+				Value: runtime.EncodeOrDie(latest.Codec, &api.Route{TypeMeta: kubeapi.TypeMeta{ID: "foo"}}),
201 201
 			},
202 202
 		},
203 203
 		E: nil,
204 204
 	}
205 205
 	registry := NewTestEtcd(fakeClient)
206 206
 	err := registry.CreateRoute(&api.Route{
207
-		JSONBase: kubeapi.JSONBase{
207
+		TypeMeta: kubeapi.TypeMeta{
208 208
 			ID: "foo",
209 209
 		},
210 210
 	})
... ...
@@ -36,12 +36,12 @@ func TestListRoutesPopulatedList(t *testing.T) {
36 36
 	mockRegistry.Routes = &api.RouteList{
37 37
 		Items: []api.Route{
38 38
 			{
39
-				JSONBase: kubeapi.JSONBase{
39
+				TypeMeta: kubeapi.TypeMeta{
40 40
 					ID: "foo",
41 41
 				},
42 42
 			},
43 43
 			{
44
-				JSONBase: kubeapi.JSONBase{
44
+				TypeMeta: kubeapi.TypeMeta{
45 45
 					ID: "bar",
46 46
 				},
47 47
 			},
... ...
@@ -81,7 +81,7 @@ func TestCreateRouteOK(t *testing.T) {
81 81
 	storage := REST{registry: mockRegistry}
82 82
 
83 83
 	channel, err := storage.Create(nil, &api.Route{
84
-		JSONBase:    kubeapi.JSONBase{ID: "foo"},
84
+		TypeMeta:    kubeapi.TypeMeta{ID: "foo"},
85 85
 		Host:        "www.frontend.com",
86 86
 		ServiceName: "myrubyservice",
87 87
 	})
... ...
@@ -126,7 +126,7 @@ func TestGetRouteOK(t *testing.T) {
126 126
 	mockRegistry.Routes = &api.RouteList{
127 127
 		Items: []api.Route{
128 128
 			{
129
-				JSONBase: kubeapi.JSONBase{ID: "foo"},
129
+				TypeMeta: kubeapi.TypeMeta{ID: "foo"},
130 130
 			},
131 131
 		},
132 132
 	}
... ...
@@ -173,7 +173,7 @@ func TestUpdateRegistryErrorSaving(t *testing.T) {
173 173
 	storage := REST{registry: mockRepositoryRegistry}
174 174
 
175 175
 	channel, err := storage.Update(nil, &api.Route{
176
-		JSONBase:    kubeapi.JSONBase{ID: "foo"},
176
+		TypeMeta:    kubeapi.TypeMeta{ID: "foo"},
177 177
 		Host:        "www.frontend.com",
178 178
 		ServiceName: "rubyservice",
179 179
 	})
... ...
@@ -195,7 +195,7 @@ func TestUpdateRouteOK(t *testing.T) {
195 195
 	mockRepositoryRegistry.Routes = &api.RouteList{
196 196
 		Items: []api.Route{
197 197
 			{
198
-				JSONBase:    kubeapi.JSONBase{ID: "bar"},
198
+				TypeMeta:    kubeapi.TypeMeta{ID: "bar"},
199 199
 				Host:        "www.frontend.com",
200 200
 				ServiceName: "rubyservice",
201 201
 			},
... ...
@@ -205,7 +205,7 @@ func TestUpdateRouteOK(t *testing.T) {
205 205
 	storage := REST{registry: mockRepositoryRegistry}
206 206
 
207 207
 	channel, err := storage.Update(nil, &api.Route{
208
-		JSONBase:    kubeapi.JSONBase{ID: "bar"},
208
+		TypeMeta:    kubeapi.TypeMeta{ID: "bar"},
209 209
 		Host:        "www.newfrontend.com",
210 210
 		ServiceName: "newrubyservice",
211 211
 	})
... ...
@@ -250,7 +250,7 @@ func TestDeleteRouteOk(t *testing.T) {
250 250
 	mockRegistry.Routes = &api.RouteList{
251 251
 		Items: []api.Route{
252 252
 			{
253
-				JSONBase: kubeapi.JSONBase{ID: "foo"},
253
+				TypeMeta: kubeapi.TypeMeta{ID: "foo"},
254 254
 			},
255 255
 		},
256 256
 	}
257 257
new file mode 100644
... ...
@@ -0,0 +1,14 @@
0
+package service
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+	"github.com/golang/glog"
5
+)
6
+
7
+type FailingServiceConfigProxy struct {
8
+}
9
+
10
+// OnUpdate implements method for kubernetes/pkg/proxy/config/ServiceConfigHandler
11
+func (proxy *FailingServiceConfigProxy) OnUpdate(services []api.Service) {
12
+	glog.Errorf("Failed to properly wire up service.  This can happen if you forget to launch with permissions to iptables.  Access to the following services will be impaired: %#v\n", services)
13
+}
... ...
@@ -7,7 +7,7 @@ import (
7 7
 
8 8
 // Template contains the inputs needed to produce a Config.
9 9
 type Template struct {
10
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
10
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
11 11
 
12 12
 	// Required: Name identifies the Template.
13 13
 	Name string `json:"name" yaml:"name"`
... ...
@@ -7,7 +7,7 @@ import (
7 7
 
8 8
 // Template contains the inputs needed to produce a Config.
9 9
 type Template struct {
10
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
10
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
11 11
 
12 12
 	// Required: Name identifies the Template.
13 13
 	Name string `json:"name" yaml:"name"`
... ...
@@ -47,27 +47,27 @@ func TestValidateTemplate(t *testing.T) {
47 47
 		},
48 48
 		{ // Template with ID, should pass
49 49
 			&api.Template{
50
-				JSONBase: kubeapi.JSONBase{ID: "templateId"},
50
+				TypeMeta: kubeapi.TypeMeta{ID: "templateId"},
51 51
 			},
52 52
 			true,
53 53
 		},
54 54
 		{ // Template with invalid Parameter, should fail on Parameter name
55 55
 			&api.Template{
56
-				JSONBase:   kubeapi.JSONBase{ID: "templateId"},
56
+				TypeMeta:   kubeapi.TypeMeta{ID: "templateId"},
57 57
 				Parameters: []api.Parameter{{Name: "", Value: "1"}},
58 58
 			},
59 59
 			false,
60 60
 		},
61 61
 		{ // Template with valid Parameter, should pass
62 62
 			&api.Template{
63
-				JSONBase:   kubeapi.JSONBase{ID: "templateId"},
63
+				TypeMeta:   kubeapi.TypeMeta{ID: "templateId"},
64 64
 				Parameters: []api.Parameter{{Name: "VALID_NAME", Value: "1"}},
65 65
 			},
66 66
 			true,
67 67
 		},
68 68
 		{ // Template with Item of unknown Kind, should pass
69 69
 			&api.Template{
70
-				JSONBase:   kubeapi.JSONBase{ID: "templateId"},
70
+				TypeMeta:   kubeapi.TypeMeta{ID: "templateId"},
71 71
 				Parameters: []api.Parameter{{Name: "VALID_NAME", Value: "1"}},
72 72
 				Items:      []runtime.EmbeddedObject{{}},
73 73
 			},
... ...
@@ -129,5 +129,5 @@ func ExampleProcessTemplateParameters() {
129 129
 	result, _ := latest.Codec.Encode(config)
130 130
 	fmt.Println(string(result))
131 131
 	// Output:
132
-	//{"kind":"Config","id":"guestbook","creationTimestamp":"1980-01-01T00:00:00Z","apiVersion":"v1beta1","namespace":"","name":"guestbook-example","description":"Example shows how to build a simple multi-tier application using Kubernetes and Docker","items":[{"kind":"Route","id":"frontendroute","creationTimestamp":null,"apiVersion":"v1beta1","namespace":"","host":"guestbook.example.com","serviceName":"frontend","labels":{"name":"frontend"}},{"kind":"Service","id":"frontend","creationTimestamp":null,"apiVersion":"v1beta1","namespace":"","port":5432,"selector":{"name":"frontend"},"containerPort":0},{"kind":"Service","id":"redismaster","creationTimestamp":null,"apiVersion":"v1beta1","namespace":"","port":10000,"selector":{"name":"redis-master"},"containerPort":0},{"kind":"Service","id":"redisslave","creationTimestamp":null,"apiVersion":"v1beta1","namespace":"","port":10001,"labels":{"name":"redisslave"},"selector":{"name":"redisslave"},"containerPort":0},{"kind":"Pod","id":"redis-master-2","creationTimestamp":null,"apiVersion":"v1beta1","namespace":"","labels":{"name":"redis-master"},"desiredState":{"manifest":{"version":"v1beta1","id":"redis-master-2","volumes":null,"containers":[{"name":"master","image":"dockerfile/redis","ports":[{"containerPort":6379}],"env":[{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"currentState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}},{"kind":"ReplicationController","id":"frontendController","creationTimestamp":null,"apiVersion":"v1beta1","namespace":"","desiredState":{"replicas":3,"replicaSelector":{"name":"frontend"},"podTemplate":{"desiredState":{"manifest":{"version":"v1beta1","id":"frontendController","volumes":null,"containers":[{"name":"php-redis","image":"brendanburns/php-redis","ports":[{"hostPort":8000,"containerPort":80}],"env":[{"name":"ADMIN_USERNAME","key":"ADMIN_USERNAME","value":"adminQ3H"},{"name":"ADMIN_PASSWORD","key":"ADMIN_PASSWORD","value":"dwNJiJwW"},{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"labels":{"name":"frontend"}}},"currentState":{"replicas":0,"podTemplate":{"desiredState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}}},"labels":{"name":"frontend"}},{"kind":"ReplicationController","id":"redisSlaveController","creationTimestamp":null,"apiVersion":"v1beta1","namespace":"","desiredState":{"replicas":2,"replicaSelector":{"name":"redisslave"},"podTemplate":{"desiredState":{"manifest":{"version":"v1beta1","id":"redisSlaveController","volumes":null,"containers":[{"name":"slave","image":"brendanburns/redis-slave","ports":[{"hostPort":6380,"containerPort":6379}],"env":[{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"labels":{"name":"redisslave"}}},"currentState":{"replicas":0,"podTemplate":{"desiredState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}}},"labels":{"name":"redisslave"}}]}
132
+	//{"kind":"Config","id":"guestbook","creationTimestamp":"1980-01-01T00:00:00Z","apiVersion":"v1beta1","name":"guestbook-example","description":"Example shows how to build a simple multi-tier application using Kubernetes and Docker","items":[{"kind":"Route","id":"frontendroute","creationTimestamp":null,"apiVersion":"v1beta1","host":"guestbook.example.com","serviceName":"frontend","labels":{"name":"frontend"}},{"kind":"Service","id":"frontend","creationTimestamp":null,"apiVersion":"v1beta1","port":5432,"selector":{"name":"frontend"},"containerPort":0},{"kind":"Service","id":"redismaster","creationTimestamp":null,"apiVersion":"v1beta1","port":10000,"selector":{"name":"redis-master"},"containerPort":0},{"kind":"Service","id":"redisslave","creationTimestamp":null,"apiVersion":"v1beta1","port":10001,"labels":{"name":"redisslave"},"selector":{"name":"redisslave"},"containerPort":0},{"kind":"Pod","id":"redis-master-2","creationTimestamp":null,"apiVersion":"v1beta1","labels":{"name":"redis-master"},"desiredState":{"manifest":{"version":"v1beta1","id":"redis-master-2","volumes":null,"containers":[{"name":"master","image":"dockerfile/redis","ports":[{"containerPort":6379}],"env":[{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"currentState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}},{"kind":"ReplicationController","id":"frontendController","creationTimestamp":null,"apiVersion":"v1beta1","desiredState":{"replicas":3,"replicaSelector":{"name":"frontend"},"podTemplate":{"desiredState":{"manifest":{"version":"v1beta1","id":"frontendController","volumes":null,"containers":[{"name":"php-redis","image":"brendanburns/php-redis","ports":[{"hostPort":8000,"containerPort":80}],"env":[{"name":"ADMIN_USERNAME","key":"ADMIN_USERNAME","value":"adminQ3H"},{"name":"ADMIN_PASSWORD","key":"ADMIN_PASSWORD","value":"dwNJiJwW"},{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"labels":{"name":"frontend"}}},"currentState":{"replicas":0,"podTemplate":{"desiredState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}}},"labels":{"name":"frontend"}},{"kind":"ReplicationController","id":"redisSlaveController","creationTimestamp":null,"apiVersion":"v1beta1","desiredState":{"replicas":2,"replicaSelector":{"name":"redisslave"},"podTemplate":{"desiredState":{"manifest":{"version":"v1beta1","id":"redisSlaveController","volumes":null,"containers":[{"name":"slave","image":"brendanburns/redis-slave","ports":[{"hostPort":6380,"containerPort":6379}],"env":[{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"labels":{"name":"redisslave"}}},"currentState":{"replicas":0,"podTemplate":{"desiredState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}}},"labels":{"name":"redisslave"}}]}
133 133
 }
... ...
@@ -8,25 +8,25 @@ import (
8 8
 // POST to UserIdentityMapping, get back error or a filled out UserIdentityMapping object
9 9
 
10 10
 type User struct {
11
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
11
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
12 12
 	Labels           map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
13 13
 
14 14
 	// Name is a human readable string uniquely representing this user at any time.
15 15
 	Name string `json:"name,omitempty" yaml:"name,omitempty"`
16 16
 
17 17
 	// UID is a unique in time and space value which uniquely identifies this user.
18
-	UID string `json:"uid,omitempty" yaml:"uid,omitempty"`
18
+	UserUID string `json:"useruid,omitempty" yaml:"useruid,omitempty"`
19 19
 
20 20
 	FullName string `json:"fullName,omitempty" yaml:"fullName,omitempty"`
21 21
 }
22 22
 
23 23
 type UserList struct {
24
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
24
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
25 25
 	Items            []User `json:"items,omitempty" yaml:"items,omitempty"`
26 26
 }
27 27
 
28 28
 type Identity struct {
29
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
29
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
30 30
 	Labels           map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
31 31
 
32 32
 	// Name is the unique identifier of a user within a given provider
... ...
@@ -39,7 +39,7 @@ type Identity struct {
39 39
 }
40 40
 
41 41
 type UserIdentityMapping struct {
42
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
42
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
43 43
 
44 44
 	Identity Identity `json:"identity,omitempty" yaml:"identity,omitempty"`
45 45
 	User     User     `json:"user,omitempty" yaml:"user,omitempty"`
... ...
@@ -8,25 +8,25 @@ import (
8 8
 // POST to UserIdentityMapping, get back error or a filled out UserIdentityMapping object
9 9
 
10 10
 type User struct {
11
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
11
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
12 12
 	Labels           map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
13 13
 
14 14
 	// Name is a human readable string uniquely representing this user at any time.
15 15
 	Name string `json:"name,omitempty" yaml:"name,omitempty"`
16 16
 
17 17
 	// UID is a unique in time and space value which uniquely identifies this user.
18
-	UID string `json:"uid,omitempty" yaml:"uid,omitempty"`
18
+	UserUID string `json:"useruid,omitempty" yaml:"useruid,omitempty"`
19 19
 
20 20
 	FullName string `json:"fullName,omitempty" yaml:"fullName,omitempty"`
21 21
 }
22 22
 
23 23
 type UserList struct {
24
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
24
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
25 25
 	Items            []User `json:"items,omitempty" yaml:"items,omitempty"`
26 26
 }
27 27
 
28 28
 type Identity struct {
29
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
29
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
30 30
 	Labels           map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
31 31
 
32 32
 	// Name is the unique identifier of a user within a given provider
... ...
@@ -39,7 +39,7 @@ type Identity struct {
39 39
 }
40 40
 
41 41
 type UserIdentityMapping struct {
42
-	kubeapi.JSONBase `json:",inline" yaml:",inline"`
42
+	kubeapi.TypeMeta `json:",inline" yaml:",inline"`
43 43
 
44 44
 	Identity Identity `json:"identity,omitempty" yaml:"identity,omitempty"`
45 45
 	User     User     `json:"user,omitempty" yaml:"user,omitempty"`
... ...
@@ -54,14 +54,14 @@ func (r *Etcd) CreateOrUpdateUserIdentityMapping(mapping *api.UserIdentityMappin
54 54
 		// did not previously exist
55 55
 		if existing.Identity.Name == "" {
56 56
 			uid := uuid.New()
57
-			existing.User.UID = uid
57
+			existing.User.UserUID = uid
58 58
 			existing.User.Name = name
59 59
 			if err := r.initializer.InitializeUser(&mapping.Identity, &existing.User); err != nil {
60 60
 				return in, err
61 61
 			}
62 62
 
63 63
 			// set these again to prevent bad initialization from messing up data
64
-			existing.User.UID = uid
64
+			existing.User.UserUID = uid
65 65
 			existing.User.Name = name
66 66
 			existing.Identity = mapping.Identity
67 67
 
... ...
@@ -34,12 +34,12 @@ func TestBuildConfigClient(t *testing.T) {
34 34
 	deleteAllEtcdKeys()
35 35
 	ctx := kapi.NewContext()
36 36
 	etcdClient := newEtcdClient()
37
-	helper, _ := master.NewEtcdHelper(etcdClient.GetCluster(), klatest.Version)
37
+	helper, _ := master.NewEtcdHelper(etcdClient, klatest.Version)
38 38
 	m := master.New(&master.Config{
39 39
 		EtcdHelper: helper,
40 40
 	})
41 41
 	interfaces, _ := latest.InterfacesFor(latest.Version)
42
-	buildRegistry := buildetcd.New(tools.EtcdHelper{etcdClient, interfaces.Codec, interfaces.ResourceVersioner})
42
+	buildRegistry := buildetcd.New(tools.EtcdHelper{etcdClient, interfaces.Codec, tools.RuntimeVersionAdapter{interfaces.ResourceVersioner}})
43 43
 	storage := map[string]apiserver.RESTStorage{
44 44
 		"builds":       buildregistry.NewREST(buildRegistry),
45 45
 		"buildConfigs": buildconfigregistry.NewREST(buildRegistry),
... ...
@@ -34,12 +34,12 @@ func TestBuildClient(t *testing.T) {
34 34
 	deleteAllEtcdKeys()
35 35
 	ctx := kapi.NewContext()
36 36
 	etcdClient := newEtcdClient()
37
-	helper, _ := master.NewEtcdHelper(etcdClient.GetCluster(), klatest.Version)
37
+	helper, _ := master.NewEtcdHelper(etcdClient, klatest.Version)
38 38
 	m := master.New(&master.Config{
39 39
 		EtcdHelper: helper,
40 40
 	})
41 41
 	interfaces, _ := latest.InterfacesFor(latest.Version)
42
-	buildRegistry := buildetcd.New(tools.EtcdHelper{etcdClient, interfaces.Codec, interfaces.ResourceVersioner})
42
+	buildRegistry := buildetcd.New(tools.EtcdHelper{etcdClient, interfaces.Codec, tools.RuntimeVersionAdapter{interfaces.ResourceVersioner}})
43 43
 	storage := map[string]apiserver.RESTStorage{
44 44
 		"builds":       buildregistry.NewREST(buildRegistry),
45 45
 		"buildConfigs": buildconfigregistry.NewREST(buildRegistry),
... ...
@@ -59,7 +59,7 @@ func TestSuccessfulManualDeployment(t *testing.T) {
59 59
 	}
60 60
 
61 61
 	watch, err := openshift.Client.WatchDeployments(ctx, labels.Everything(),
62
-		labels.Set{deployapi.DeploymentConfigLabel: config.ID}.AsSelector(), 0)
62
+		labels.Set{deployapi.DeploymentConfigLabel: config.ID}.AsSelector(), "0")
63 63
 	if err != nil {
64 64
 		t.Fatalf("Couldn't subscribe to Deployments: %v", err)
65 65
 	}
... ...
@@ -77,7 +77,7 @@ func TestSimpleImageChangeTrigger(t *testing.T) {
77 77
 	openshift := NewTestOpenshift(t)
78 78
 
79 79
 	imageRepo := &imageapi.ImageRepository{
80
-		JSONBase:              kapi.JSONBase{ID: "test-image-repo"},
80
+		TypeMeta:              kapi.TypeMeta{ID: "test-image-repo"},
81 81
 		DockerImageRepository: "registry:8080/openshift/test-image",
82 82
 		Tags: map[string]string{
83 83
 			"latest": "ref-1",
... ...
@@ -105,7 +105,7 @@ func TestSimpleImageChangeTrigger(t *testing.T) {
105 105
 	}
106 106
 
107 107
 	watch, err := openshift.Client.WatchDeployments(ctx, labels.Everything(),
108
-		labels.Set{deployapi.DeploymentConfigLabel: config.ID}.AsSelector(), 0)
108
+		labels.Set{deployapi.DeploymentConfigLabel: config.ID}.AsSelector(), "0")
109 109
 	if err != nil {
110 110
 		t.Fatalf("Couldn't subscribe to Deployments %v", err)
111 111
 	}
... ...
@@ -158,7 +158,7 @@ func TestSimpleConfigChangeTrigger(t *testing.T) {
158 158
 	}
159 159
 
160 160
 	watch, err := openshift.Client.WatchDeployments(ctx, labels.Everything(),
161
-		labels.Set{deployapi.DeploymentConfigLabel: config.ID}.AsSelector(), 0)
161
+		labels.Set{deployapi.DeploymentConfigLabel: config.ID}.AsSelector(), "0")
162 162
 	if err != nil {
163 163
 		t.Fatalf("Couldn't subscribe to Deployments %v", err)
164 164
 	}
... ...
@@ -207,7 +207,7 @@ type podInfoGetter struct {
207 207
 	Error   error
208 208
 }
209 209
 
210
-func (p *podInfoGetter) GetPodInfo(host, podID string) (kapi.PodInfo, error) {
210
+func (p *podInfoGetter) GetPodInfo(host, namespace, podID string) (kapi.PodInfo, error) {
211 211
 	return p.PodInfo, p.Error
212 212
 }
213 213
 
... ...
@@ -222,7 +222,7 @@ func NewTestOpenshift(t *testing.T) *testOpenshift {
222 222
 	openshift := &testOpenshift{}
223 223
 
224 224
 	etcdClient := newEtcdClient()
225
-	etcdHelper, _ := master.NewEtcdHelper(etcdClient.GetCluster(), klatest.Version)
225
+	etcdHelper, _ := master.NewEtcdHelper(etcdClient, klatest.Version)
226 226
 
227 227
 	osMux := http.NewServeMux()
228 228
 	openshift.server = httptest.NewServer(osMux)
... ...
@@ -286,7 +286,7 @@ func NewTestOpenshift(t *testing.T) *testOpenshift {
286 286
 
287 287
 func imageChangeDeploymentConfig() *deployapi.DeploymentConfig {
288 288
 	return &deployapi.DeploymentConfig{
289
-		JSONBase: kapi.JSONBase{ID: "image-deploy-config"},
289
+		TypeMeta: kapi.TypeMeta{ID: "image-deploy-config"},
290 290
 		Triggers: []deployapi.DeploymentTriggerPolicy{
291 291
 			{
292 292
 				Type: deployapi.DeploymentTriggerOnImageChange,
... ...
@@ -339,7 +339,7 @@ func imageChangeDeploymentConfig() *deployapi.DeploymentConfig {
339 339
 
340 340
 func manualDeploymentConfig() *deployapi.DeploymentConfig {
341 341
 	return &deployapi.DeploymentConfig{
342
-		JSONBase: kapi.JSONBase{ID: "manual-deploy-config"},
342
+		TypeMeta: kapi.TypeMeta{ID: "manual-deploy-config"},
343 343
 		Triggers: []deployapi.DeploymentTriggerPolicy{
344 344
 			{
345 345
 				Type: deployapi.DeploymentTriggerManual,
... ...
@@ -380,7 +380,7 @@ func manualDeploymentConfig() *deployapi.DeploymentConfig {
380 380
 
381 381
 func changeDeploymentConfig() *deployapi.DeploymentConfig {
382 382
 	return &deployapi.DeploymentConfig{
383
-		JSONBase: kapi.JSONBase{ID: "change-deploy-config"},
383
+		TypeMeta: kapi.TypeMeta{ID: "change-deploy-config"},
384 384
 		Triggers: []deployapi.DeploymentTriggerPolicy{
385 385
 			{
386 386
 				Type: deployapi.DeploymentTriggerManual,
... ...
@@ -54,7 +54,7 @@ func TestOAuthStorage(t *testing.T) {
54 54
 	deleteAllEtcdKeys()
55 55
 	interfaces, _ := latest.InterfacesFor(latest.Version)
56 56
 	etcdClient := newEtcdClient()
57
-	etcdHelper := tools.EtcdHelper{etcdClient, interfaces.Codec, interfaces.ResourceVersioner}
57
+	etcdHelper := tools.EtcdHelper{etcdClient, interfaces.Codec, tools.RuntimeVersionAdapter{interfaces.ResourceVersioner}}
58 58
 	registry := etcd.New(etcdHelper)
59 59
 
60 60
 	user := &testUser{UserName: "test", UserUID: "1"}
... ...
@@ -35,7 +35,7 @@ func TestUserInitialization(t *testing.T) {
35 35
 	deleteAllEtcdKeys()
36 36
 	etcdClient := newEtcdClient()
37 37
 	interfaces, _ := latest.InterfacesFor(latest.Version)
38
-	userRegistry := etcd.New(tools.EtcdHelper{etcdClient, interfaces.Codec, interfaces.ResourceVersioner}, user.NewDefaultUserInitStrategy())
38
+	userRegistry := etcd.New(tools.EtcdHelper{etcdClient, interfaces.Codec, tools.RuntimeVersionAdapter{interfaces.ResourceVersioner}}, user.NewDefaultUserInitStrategy())
39 39
 	storage := map[string]apiserver.RESTStorage{
40 40
 		"userIdentityMappings": useridentitymapping.NewREST(userRegistry),
41 41
 		"users":                userregistry.NewREST(userRegistry),
... ...
@@ -68,14 +68,14 @@ func TestUserInitialization(t *testing.T) {
68 68
 
69 69
 	expectedUser := api.User{
70 70
 		Name:     ":test",
71
-		UID:      actual.User.UID,
71
+		UserUID:  actual.User.UserUID,
72 72
 		FullName: "Mr. Test",
73 73
 	}
74 74
 	expected := &api.UserIdentityMapping{
75 75
 		Identity: mapping.Identity,
76 76
 		User:     expectedUser,
77 77
 	}
78
-	actual.JSONBase = kapi.JSONBase{}
78
+	actual.TypeMeta = kapi.TypeMeta{}
79 79
 	if !reflect.DeepEqual(expected, actual) {
80 80
 		t.Errorf("expected %#v, got %#v", expected, actual)
81 81
 	}
... ...
@@ -84,7 +84,7 @@ func TestUserInitialization(t *testing.T) {
84 84
 	if err != nil {
85 85
 		t.Fatalf("unexpected error: %v", err)
86 86
 	}
87
-	user.JSONBase = kapi.JSONBase{}
87
+	user.TypeMeta = kapi.TypeMeta{}
88 88
 	if !reflect.DeepEqual(&expected.User, user) {
89 89
 		t.Errorf("expected %#v, got %#v", expected.User, user)
90 90
 	}
... ...
@@ -93,7 +93,7 @@ func TestUserInitialization(t *testing.T) {
93 93
 	if err != nil {
94 94
 		t.Fatalf("unexpected error: %v", err)
95 95
 	}
96
-	actualUser.JSONBase = kapi.JSONBase{}
96
+	actualUser.TypeMeta = kapi.TypeMeta{}
97 97
 	if !reflect.DeepEqual(&expected.User, actualUser) {
98 98
 		t.Errorf("expected %#v, got %#v", expected.User, actualUser)
99 99
 	}
... ...
@@ -112,7 +112,7 @@ func TestUserLookup(t *testing.T) {
112 112
 	deleteAllEtcdKeys()
113 113
 	etcdClient := newEtcdClient()
114 114
 	interfaces, _ := latest.InterfacesFor(latest.Version)
115
-	userRegistry := etcd.New(tools.EtcdHelper{etcdClient, interfaces.Codec, interfaces.ResourceVersioner}, user.NewDefaultUserInitStrategy())
115
+	userRegistry := etcd.New(tools.EtcdHelper{etcdClient, interfaces.Codec, tools.RuntimeVersionAdapter{interfaces.ResourceVersioner}}, user.NewDefaultUserInitStrategy())
116 116
 	userInfo := &authapi.DefaultUserInfo{
117 117
 		Name: ":test",
118 118
 	}
... ...
@@ -161,14 +161,14 @@ func TestUserLookup(t *testing.T) {
161 161
 	}
162 162
 	expectedUser := api.User{
163 163
 		Name:     ":test",
164
-		UID:      actual.User.UID,
164
+		UserUID:  actual.User.UserUID,
165 165
 		FullName: "Mr. Test",
166 166
 	}
167 167
 	expected := &api.UserIdentityMapping{
168 168
 		Identity: mapping.Identity,
169 169
 		User:     expectedUser,
170 170
 	}
171
-	actual.JSONBase = kapi.JSONBase{}
171
+	actual.TypeMeta = kapi.TypeMeta{}
172 172
 	if !reflect.DeepEqual(expected, actual) {
173 173
 		t.Errorf("expected\n %#v,\n got\n %#v", expected, actual)
174 174
 	}
... ...
@@ -178,7 +178,7 @@ func TestUserLookup(t *testing.T) {
178 178
 	if err != nil {
179 179
 		t.Fatalf("unexpected error: %v", err)
180 180
 	}
181
-	user.JSONBase = kapi.JSONBase{}
181
+	user.TypeMeta = kapi.TypeMeta{}
182 182
 	if !reflect.DeepEqual(&expected.User, user) {
183 183
 		t.Errorf("expected\n %#v,\n got\n %#v", &expected.User, user)
184 184
 	}
... ...
@@ -188,7 +188,7 @@ func TestUserLookup(t *testing.T) {
188 188
 	if err != nil {
189 189
 		t.Fatalf("unexpected error: %v", err)
190 190
 	}
191
-	actualUser.JSONBase = kapi.JSONBase{}
191
+	actualUser.TypeMeta = kapi.TypeMeta{}
192 192
 	if !reflect.DeepEqual(&expected.User, actualUser) {
193 193
 		t.Errorf("expected\n %#v,\n got\n %#v", &expected.User, actualUser)
194 194
 	}
... ...
@@ -198,7 +198,7 @@ func TestUserLookup(t *testing.T) {
198 198
 	if err != nil {
199 199
 		t.Fatalf("unexpected error: %v", err)
200 200
 	}
201
-	currentUser.JSONBase = kapi.JSONBase{}
201
+	currentUser.TypeMeta = kapi.TypeMeta{}
202 202
 	if !reflect.DeepEqual(&expected.User, currentUser) {
203 203
 		t.Errorf("expected %#v, got %#v", expected.User, currentUser)
204 204
 	}
... ...
@@ -40,7 +40,7 @@ func TestWebhookGithubPush(t *testing.T) {
40 40
 
41 41
 	// create buildconfig
42 42
 	buildConfig := &buildapi.BuildConfig{
43
-		JSONBase: kapi.JSONBase{
43
+		TypeMeta: kapi.TypeMeta{
44 44
 			ID: "pushbuild",
45 45
 		},
46 46
 		DesiredInput: buildapi.BuildInput{
... ...
@@ -81,7 +81,7 @@ func TestWebhookGithubPing(t *testing.T) {
81 81
 
82 82
 	// create buildconfig
83 83
 	buildConfig := &buildapi.BuildConfig{
84
-		JSONBase: kapi.JSONBase{
84
+		TypeMeta: kapi.TypeMeta{
85 85
 			ID: "pingbuild",
86 86
 		},
87 87
 		DesiredInput: buildapi.BuildInput{
... ...
@@ -112,12 +112,12 @@ func TestWebhookGithubPing(t *testing.T) {
112 112
 func setup(t *testing.T) (*osclient.Client, string) {
113 113
 	deleteAllEtcdKeys()
114 114
 	etcdClient := newEtcdClient()
115
-	helper, _ := master.NewEtcdHelper(etcdClient.GetCluster(), klatest.Version)
115
+	helper, _ := master.NewEtcdHelper(etcdClient, klatest.Version)
116 116
 	m := master.New(&master.Config{
117 117
 		EtcdHelper: helper,
118 118
 	})
119 119
 	interfaces, _ := latest.InterfacesFor(latest.Version)
120
-	buildRegistry := buildetcd.New(tools.EtcdHelper{etcdClient, interfaces.Codec, interfaces.ResourceVersioner})
120
+	buildRegistry := buildetcd.New(tools.EtcdHelper{etcdClient, interfaces.Codec, tools.RuntimeVersionAdapter{interfaces.ResourceVersioner}})
121 121
 	storage := map[string]apiserver.RESTStorage{
122 122
 		"builds":       buildregistry.NewREST(buildRegistry),
123 123
 		"buildConfigs": buildconfigregistry.NewREST(buildRegistry),