Browse code

Merge pull request #88 from abhgupta/abhgupta-dev

Merged by openshift-bot

OpenShift Bot authored on 2014/09/26 05:03:26
Showing 19 changed files
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	_ "github.com/openshift/origin/pkg/deploy/api"
10 10
 	_ "github.com/openshift/origin/pkg/image/api"
11 11
 	_ "github.com/openshift/origin/pkg/template/api"
12
+	_ "github.com/openshift/origin/pkg/route/api"
12 13
 )
13 14
 
14 15
 // Codec is the identity codec for this package - it can only convert itself
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	_ "github.com/openshift/origin/pkg/deploy/api/v1beta1"
10 10
 	_ "github.com/openshift/origin/pkg/image/api/v1beta1"
11 11
 	_ "github.com/openshift/origin/pkg/template/api/v1beta1"
12
+	_ "github.com/openshift/origin/pkg/route/api/v1beta1"
12 13
 )
13 14
 
14 15
 // Codec encodes internal objects to the v1beta1 scheme
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	buildapi "github.com/openshift/origin/pkg/build/api"
13 13
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
14 14
 	imageapi "github.com/openshift/origin/pkg/image/api"
15
+	routeapi "github.com/openshift/origin/pkg/route/api"
15 16
 )
16 17
 
17 18
 // Interface exposes methods on OpenShift resources.
... ...
@@ -23,6 +24,7 @@ type Interface interface {
23 23
 	ImageRepositoryMappingInterface
24 24
 	DeploymentInterface
25 25
 	DeploymentConfigInterface
26
+	RouteInterface
26 27
 }
27 28
 
28 29
 // BuildInterface exposes methods on Build resources.
... ...
@@ -81,6 +83,16 @@ type DeploymentInterface interface {
81 81
 	DeleteDeployment(string) error
82 82
 }
83 83
 
84
+// RouteInterface exposes methods on Route resources
85
+type RouteInterface interface {
86
+	ListRoutes(selector labels.Selector) (*routeapi.RouteList, error)
87
+	GetRoute(routeID string) (*routeapi.Route, error)
88
+	CreateRoute(route *routeapi.Route) (*routeapi.Route, error)
89
+	UpdateRoute(route *routeapi.Route) (*routeapi.Route, error)
90
+	DeleteRoute(routeID string) error
91
+	WatchRoutes(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error)
92
+}
93
+
84 94
 // Client is an OpenShift client object
85 95
 type Client struct {
86 96
 	*kubeclient.RESTClient
... ...
@@ -295,3 +307,43 @@ func (c *Client) UpdateDeployment(deployment *deployapi.Deployment) (result *dep
295 295
 func (c *Client) DeleteDeployment(id string) error {
296 296
 	return c.Delete().Path("deployments").Path(id).Do().Error()
297 297
 }
298
+
299
+// ListRoutes takes a selector, and returns the list of routes that match that selector
300
+func (c *Client) ListRoutes(selector labels.Selector) (result *routeapi.RouteList, err error) {
301
+	err = c.Get().Path("routes").SelectorParam("labels", selector).Do().Into(result)
302
+	return
303
+}
304
+
305
+// GetRoute takes the name of the route, and returns the corresponding Route object, and an error if it occurs
306
+func (c *Client) GetRoute(name string) (result *routeapi.Route, err error) {
307
+	err = c.Get().Path("routes").Path(name).Do().Into(result)
308
+	return
309
+}
310
+
311
+// DeleteRoute takes the name of the route, and returns an error if one occurs
312
+func (c *Client) DeleteRoute(name string) error {
313
+	return c.Delete().Path("routes").Path(name).Do().Error()
314
+}
315
+
316
+// CreateRoute takes the representation of a route.  Returns the server's representation of the route, and an error, if it occurs
317
+func (c *Client) CreateRoute(route *routeapi.Route) (result *routeapi.Route, err error) {
318
+	err = c.Post().Path("routes").Body(route).Do().Into(result)
319
+	return
320
+}
321
+
322
+// UpdateRoute takes the representation of a route to update.  Returns the server's representation of the route, and an error, if it occurs
323
+func (c *Client) UpdateRoute(route *routeapi.Route) (result *routeapi.Route, err error) {
324
+	err = c.Put().Path("routes").Path(route.ID).Body(route).Do().Into(result)
325
+	return
326
+}
327
+
328
+// WatchRoutes returns a watch.Interface that watches the requested routes.
329
+func (c *Client) WatchRoutes(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) {
330
+	return c.Get().
331
+		Path("watch").
332
+		Path("routes").
333
+		UintParam("resourceVersion", resourceVersion).
334
+		SelectorParam("labels", label).
335
+		SelectorParam("fields", field).
336
+		Watch()
337
+}
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	buildapi "github.com/openshift/origin/pkg/build/api"
8 8
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
9 9
 	imageapi "github.com/openshift/origin/pkg/image/api"
10
+	routeapi "github.com/openshift/origin/pkg/route/api"
10 11
 )
11 12
 
12 13
 type FakeAction struct {
... ...
@@ -160,3 +161,33 @@ func (c *Fake) DeleteDeployment(id string) error {
160 160
 	c.Actions = append(c.Actions, FakeAction{Action: "delete-deployment"})
161 161
 	return nil
162 162
 }
163
+
164
+func (c *Fake) ListRoutes(selector labels.Selector) (*routeapi.RouteList, error) {
165
+	c.Actions = append(c.Actions, FakeAction{Action: "list-routes"})
166
+	return &routeapi.RouteList{}, nil
167
+}
168
+
169
+func (c *Fake) GetRoute(id string) (*routeapi.Route, error) {
170
+	c.Actions = append(c.Actions, FakeAction{Action: "get-route"})
171
+	return &routeapi.Route{}, nil
172
+}
173
+
174
+func (c *Fake) CreateRoute(route *routeapi.Route) (*routeapi.Route, error) {
175
+	c.Actions = append(c.Actions, FakeAction{Action: "create-route"})
176
+	return &routeapi.Route{}, nil
177
+}
178
+
179
+func (c *Fake) UpdateRoute(route *routeapi.Route) (*routeapi.Route, error) {
180
+	c.Actions = append(c.Actions, FakeAction{Action: "update-route"})
181
+	return &routeapi.Route{}, nil
182
+}
183
+
184
+func (c *Fake) DeleteRoute(id string) error {
185
+	c.Actions = append(c.Actions, FakeAction{Action: "delete-route"})
186
+	return nil
187
+}
188
+
189
+func (c *Fake) WatchRoutes(field, label labels.Selector, resourceVersion uint64) (watch.Interface, error) {
190
+	c.Actions = append(c.Actions, FakeAction{Action: "watch-routes"})
191
+	return nil, nil
192
+}
... ...
@@ -44,11 +44,13 @@ import (
44 44
 	. "github.com/openshift/origin/pkg/cmd/client/api"
45 45
 	"github.com/openshift/origin/pkg/cmd/client/build"
46 46
 	"github.com/openshift/origin/pkg/cmd/client/image"
47
+	"github.com/openshift/origin/pkg/cmd/client/route"
47 48
 	"github.com/openshift/origin/pkg/config"
48 49
 	configapi "github.com/openshift/origin/pkg/config/api"
49 50
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
50 51
 	deployclient "github.com/openshift/origin/pkg/deploy/client"
51 52
 	imageapi "github.com/openshift/origin/pkg/image/api"
53
+	routeapi "github.com/openshift/origin/pkg/route/api"
52 54
 )
53 55
 
54 56
 type KubeConfig struct {
... ...
@@ -126,6 +128,7 @@ var parser = kubecfg.NewParser(map[string]runtime.Object{
126 126
 	"config":                  &configapi.Config{},
127 127
 	"deployments":             &deployapi.Deployment{},
128 128
 	"deploymentConfigs":       &deployapi.DeploymentConfig{},
129
+	"routes":                  &routeapi.Route{},
129 130
 })
130 131
 
131 132
 func prettyWireStorage() string {
... ...
@@ -275,6 +278,7 @@ func (c *KubeConfig) Run() {
275 275
 		"imageRepositoryMappings": {"ImageRepositoryMapping", client.RESTClient, latest.Codec},
276 276
 		"deployments":             {"Deployment", client.RESTClient, latest.Codec},
277 277
 		"deploymentConfigs":       {"DeploymentConfig", client.RESTClient, latest.Codec},
278
+		"routes":                  {"Route", client.RESTClient, latest.Codec},
278 279
 	}
279 280
 
280 281
 	matchFound := c.executeConfigRequest(method, clients) || c.executeTemplateRequest(method, client) || c.executeControllerRequest(method, kubeClient) || c.executeAPIRequest(method, clients)
... ...
@@ -520,6 +524,7 @@ func humanReadablePrinter() *kubecfg.HumanReadablePrinter {
520 520
 	build.RegisterPrintHandlers(printer)
521 521
 	image.RegisterPrintHandlers(printer)
522 522
 	deployclient.RegisterPrintHandlers(printer)
523
+	route.RegisterPrintHandlers(printer)
523 524
 
524 525
 	return printer
525 526
 }
526 527
new file mode 100644
... ...
@@ -0,0 +1,33 @@
0
+package route
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
8
+
9
+	"github.com/openshift/origin/pkg/route/api"
10
+)
11
+
12
+var routeColumns = []string{"ID", "Host/Port", "Path", "Service", "Labels"}
13
+
14
+// RegisterPrintHandlers registers HumanReadablePrinter handlers
15
+func RegisterPrintHandlers(printer *kubecfg.HumanReadablePrinter) {
16
+	printer.Handler(routeColumns, printRoute)
17
+	printer.Handler(routeColumns, printRouteList)
18
+}
19
+
20
+func printRoute(route *api.Route, w io.Writer) error {
21
+	_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", route.ID, route.Host, route.Path, route.ServiceName, labels.Set(route.Labels))
22
+	return err
23
+}
24
+
25
+func printRouteList(routeList *api.RouteList, w io.Writer) error {
26
+	for _, route := range routeList.Items {
27
+		if err := printRoute(&route, w); err != nil {
28
+			return err
29
+		}
30
+	}
31
+	return nil
32
+}
... ...
@@ -50,6 +50,8 @@ import (
50 50
 	"github.com/openshift/origin/pkg/image/registry/image"
51 51
 	"github.com/openshift/origin/pkg/image/registry/imagerepository"
52 52
 	"github.com/openshift/origin/pkg/image/registry/imagerepositorymapping"
53
+	routeregistry "github.com/openshift/origin/pkg/route/registry/route"
54
+	routeetcd "github.com/openshift/origin/pkg/route/registry/etcd"
53 55
 	"github.com/openshift/origin/pkg/template"
54 56
 	"github.com/openshift/origin/pkg/version"
55 57
 
... ...
@@ -57,6 +59,7 @@ import (
57 57
 	_ "github.com/openshift/origin/pkg/config/api/v1beta1"
58 58
 	_ "github.com/openshift/origin/pkg/image/api/v1beta1"
59 59
 	_ "github.com/openshift/origin/pkg/template/api/v1beta1"
60
+	_ "github.com/openshift/origin/pkg/route/api/v1beta1"
60 61
 )
61 62
 
62 63
 func NewCommandStartAllInOne(name string) *cobra.Command {
... ...
@@ -214,6 +217,7 @@ func (c *config) runApiserver() {
214 214
 	buildRegistry := buildetcd.New(etcdHelper)
215 215
 	imageRegistry := imageetcd.New(etcdHelper)
216 216
 	deployEtcd := deployetcd.New(etcdHelper)
217
+	routeEtcd := routeetcd.New(etcdHelper)
217 218
 
218 219
 	// initialize OpenShift API
219 220
 	storage := map[string]apiserver.RESTStorage{
... ...
@@ -225,6 +229,7 @@ func (c *config) runApiserver() {
225 225
 		"deployments":             deployregistry.NewREST(deployEtcd),
226 226
 		"deploymentConfigs":       deployconfigregistry.NewREST(deployEtcd),
227 227
 		"templateConfigs":         template.NewStorage(),
228
+		"routes":                  routeregistry.NewREST(routeEtcd),
228 229
 	}
229 230
 
230 231
 	osMux := http.NewServeMux()
231 232
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+package api
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+)
5
+
6
+func init() {
7
+	api.Scheme.AddKnownTypes("",
8
+		&Route{},
9
+		&RouteList{},
10
+	)
11
+}
12
+
13
+func (*Route) IsAnAPIObject() {}
14
+func (*RouteList) IsAnAPIObject() {}
0 15
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+package api
1
+
2
+import (
3
+	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+)
5
+
6
+// Route encapsulates the inputs needed to connect a DNS/alias to a service proxy.
7
+type Route struct {
8
+	kubeapi.JSONBase         `json:",inline" yaml:",inline"`
9
+
10
+	// Required: Alias/DNS that points to the service
11
+	// Can be host or host:port
12
+	// host and port are combined to follow the net/url URL struct
13
+	Host string              `json:"host" yaml:"host"`
14
+	// Optional: Path that the router watches for, to route traffic for to the service
15
+	Path string              `json:"path,omitempty" yaml:"path,omitempty"`
16
+
17
+	// the name of the service that this route points to
18
+	ServiceName string       `json:"serviceName" yaml:"serviceName"`
19
+	Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
20
+}
21
+
22
+// RouteList is a collection of Routes.
23
+type RouteList struct {
24
+	kubeapi.JSONBase `json:",inline" yaml:",inline"`
25
+	Items []Route    `json:"items,omitempty" yaml:"items,omitempty"`
26
+}
0 27
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+package v1beta1
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+)
5
+
6
+func init() {
7
+	api.Scheme.AddKnownTypes("v1beta1",
8
+		&Route{},
9
+		&RouteList{},
10
+	)
11
+}
12
+
13
+func (*Route) IsAnAPIObject() {}
14
+func (*RouteList) IsAnAPIObject() {}
0 15
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+package v1beta1
1
+
2
+import (
3
+	v1beta1 "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
4
+)
5
+
6
+// Route encapsulates the inputs needed to connect a DNS/alias to a service proxy.
7
+type Route struct {
8
+	v1beta1.JSONBase         `json:",inline" yaml:",inline"`
9
+
10
+	// Required: Alias/DNS that points to the service
11
+	// Can be host or host:port
12
+	// host and port are combined to follow the net/url URL struct
13
+	Host string              `json:"host" yaml:"host"`
14
+	// Optional: Path that the router watches for, to route traffic for to the service
15
+	Path string              `json:"path,omitempty" yaml:"path,omitempty"`
16
+
17
+	// the name of the service that this route points to
18
+	ServiceName string       `json:"serviceName" yaml:"serviceName"`
19
+	Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
20
+}
21
+
22
+// RouteList is a collection of Routes.
23
+type RouteList struct {
24
+	v1beta1.JSONBase `json:",inline" yaml:",inline"`
25
+	Items []Route    `json:"items,omitempty" yaml:"items,omitempty"`
26
+}
0 27
new file mode 100644
... ...
@@ -0,0 +1,19 @@
0
+package validation
1
+
2
+import (
3
+	errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
4
+	routeapi "github.com/openshift/origin/pkg/route/api"
5
+)
6
+
7
+// ValidateRoute tests if required fields in the route are set.
8
+func ValidateRoute(route *routeapi.Route) errs.ErrorList {
9
+	result := errs.ErrorList{}
10
+
11
+	if len(route.Host) == 0 {
12
+		result = append(result, errs.NewFieldRequired("host", ""))
13
+	}
14
+	if len(route.ServiceName) == 0 {
15
+		result = append(result, errs.NewFieldRequired("serviceName", ""))
16
+	}
17
+	return result
18
+}
0 19
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+/*
1
+Package route provides support for managing and watching routes. 
2
+It defines a Route resource type, along with associated storage.
3
+
4
+A Route object allows the user to specify a DNS / alias for a Kubernetes service.
5
+It stores the ID of the Service (ServiceName) and the DNS/alias (Name).
6
+The Route can be used to specify just the DNS/alias or it could also include 
7
+port and/or the path.
8
+
9
+The Route model includes the following attributes to specify the frontend URL:
10
+ - Host: Alias/DNS that points to the service. Can be host or host:port
11
+ - Path: Path allows the router to perform fine-grained routing
12
+
13
+The Route resources can be used by routers and load balancers to route external inbound 
14
+traffic. The proxy is expected to have frontend mappings for the Route.Name in its 
15
+configuration. For its endpoints, a proxy could either forward the traffic to the 
16
+Kubernetes Service port and let it do the load balancing and routing. Alternately, 
17
+a more meaningful implementation of a router could take the endpoints for the service
18
+and route/load balance the incoming requests to the corresponding service endpoints. 
19
+*/
20
+
21
+package route
0 22
new file mode 100644
... ...
@@ -0,0 +1,89 @@
0
+package etcd
1
+
2
+import (
3
+	"fmt"
4
+
5
+	etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
9
+
10
+	"github.com/openshift/origin/pkg/route/api"
11
+)
12
+
13
+// Etcd implements route.Registry backed by etcd.
14
+type Etcd struct {
15
+	tools.EtcdHelper
16
+}
17
+
18
+// New creates an etcd registry.
19
+func New(helper tools.EtcdHelper) *Etcd {
20
+	return &Etcd{
21
+		EtcdHelper: helper,
22
+	}
23
+}
24
+
25
+func makeRouteKey(id string) string {
26
+	return "/routes/" + id
27
+}
28
+
29
+// ListRoutes obtains a list of Routes.
30
+func (registry *Etcd) ListRoutes(selector labels.Selector) (*api.RouteList, error) {
31
+	allRoutes := api.RouteList{}
32
+	err := registry.ExtractList("/routes", &allRoutes.Items, &allRoutes.ResourceVersion)
33
+	if err != nil {
34
+		return nil, err
35
+	}
36
+	filtered := []api.Route{}
37
+	for _, route := range allRoutes.Items {
38
+		if selector.Matches(labels.Set(route.Labels)) {
39
+			filtered = append(filtered, route)
40
+		}
41
+	}
42
+	allRoutes.Items = filtered
43
+	return &allRoutes, nil
44
+
45
+}
46
+
47
+// GetRoute gets a specific Route specified by its ID.
48
+func (registry *Etcd) GetRoute(routeID string) (*api.Route, error) {
49
+	route := api.Route{}
50
+	err := registry.ExtractObj(makeRouteKey(routeID), &route, false)
51
+	if err != nil {
52
+		return nil, etcderr.InterpretGetError(err, "route", routeID)
53
+	}
54
+	return &route, nil
55
+}
56
+
57
+// CreateRoute creates a new Route.
58
+func (registry *Etcd) CreateRoute(route *api.Route) error {
59
+	err := registry.CreateObj(makeRouteKey(route.ID), route)
60
+	return etcderr.InterpretCreateError(err, "route", route.ID)
61
+}
62
+
63
+// UpdateRoute replaces an existing Route.
64
+func (registry *Etcd) UpdateRoute(route *api.Route) error {
65
+	err := registry.SetObj(makeRouteKey(route.ID), route)
66
+	return etcderr.InterpretUpdateError(err, "route", route.ID)
67
+}
68
+
69
+// DeleteRoute deletes a Route specified by its ID.
70
+func (registry *Etcd) DeleteRoute(routeID string) error {
71
+	key := makeRouteKey(routeID)
72
+	err := registry.Delete(key, true)
73
+	return etcderr.InterpretDeleteError(err, "route", routeID)
74
+}
75
+
76
+// WatchRoutes begins watching for new, changed, or deleted route configurations.
77
+func (registry *Etcd) WatchRoutes(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) {
78
+	if !label.Empty() {
79
+		return nil, fmt.Errorf("label selectors are not supported on routes yet")
80
+	}
81
+	if value, found := field.RequiresExactMatch("ID"); found {
82
+		return registry.Watch(makeRouteKey(value), resourceVersion)
83
+	}
84
+	if field.Empty() {
85
+		return registry.WatchList("/routes", resourceVersion, tools.Everything)
86
+	}
87
+	return nil, fmt.Errorf("only the 'ID' and default (everything) field selectors are supported")
88
+}
0 89
new file mode 100644
... ...
@@ -0,0 +1,264 @@
0
+package etcd
1
+
2
+import (
3
+	"fmt"
4
+	"testing"
5
+
6
+	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
11
+	"github.com/coreos/go-etcd/etcd"
12
+
13
+	"github.com/openshift/origin/pkg/api/latest"
14
+	"github.com/openshift/origin/pkg/route/api"
15
+	_ "github.com/openshift/origin/pkg/route/api/v1beta1"
16
+)
17
+
18
+func NewTestEtcd(client tools.EtcdClient) *Etcd {
19
+	return New(tools.EtcdHelper{client, latest.Codec, latest.ResourceVersioner})
20
+}
21
+
22
+func TestEtcdListEmptyRoutes(t *testing.T) {
23
+	fakeClient := tools.NewFakeEtcdClient(t)
24
+	key := "/routes"
25
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
26
+		R: &etcd.Response{
27
+			Node: &etcd.Node{
28
+				Nodes: []*etcd.Node{},
29
+			},
30
+		},
31
+		E: nil,
32
+	}
33
+	registry := NewTestEtcd(fakeClient)
34
+	routes, err := registry.ListRoutes(labels.Everything())
35
+	if err != nil {
36
+		t.Errorf("unexpected error: %v", err)
37
+	}
38
+
39
+	if len(routes.Items) != 0 {
40
+		t.Errorf("Unexpected routes list: %#v", routes)
41
+	}
42
+}
43
+
44
+func TestEtcdListErrorRoutes(t *testing.T) {
45
+	fakeClient := tools.NewFakeEtcdClient(t)
46
+	key := "/routes"
47
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
48
+		R: &etcd.Response{
49
+			Node: nil,
50
+		},
51
+		E: fmt.Errorf("some error"),
52
+	}
53
+	registry := NewTestEtcd(fakeClient)
54
+	routes, err := registry.ListRoutes(labels.Everything())
55
+	if err == nil {
56
+		t.Error("unexpected nil error")
57
+	}
58
+
59
+	if routes != nil {
60
+		t.Errorf("Unexpected non-nil routes: %#v", routes)
61
+	}
62
+}
63
+
64
+func TestEtcdListEverythingRoutes(t *testing.T) {
65
+	fakeClient := tools.NewFakeEtcdClient(t)
66
+	key := "/routes"
67
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
68
+		R: &etcd.Response{
69
+			Node: &etcd.Node{
70
+				Nodes: []*etcd.Node{
71
+					{
72
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Route{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
73
+					},
74
+					{
75
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Route{JSONBase: kubeapi.JSONBase{ID: "bar"}}),
76
+					},
77
+				},
78
+			},
79
+		},
80
+		E: nil,
81
+	}
82
+	registry := NewTestEtcd(fakeClient)
83
+	routes, err := registry.ListRoutes(labels.Everything())
84
+	if err != nil {
85
+		t.Errorf("unexpected error: %v", err)
86
+	}
87
+
88
+	if len(routes.Items) != 2 || routes.Items[0].ID != "foo" || routes.Items[1].ID != "bar" {
89
+		t.Errorf("Unexpected routes list: %#v", routes)
90
+	}
91
+}
92
+
93
+func TestEtcdListFilteredRoutes(t *testing.T) {
94
+	fakeClient := tools.NewFakeEtcdClient(t)
95
+	key := "/routes"
96
+	fakeClient.Data[key] = tools.EtcdResponseWithError{
97
+		R: &etcd.Response{
98
+			Node: &etcd.Node{
99
+				Nodes: []*etcd.Node{
100
+					{
101
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Route{
102
+							JSONBase: kubeapi.JSONBase{ID: "foo"},
103
+							Labels:   map[string]string{"env": "prod"},
104
+						}),
105
+					},
106
+					{
107
+						Value: runtime.EncodeOrDie(latest.Codec, &api.Route{
108
+							JSONBase: kubeapi.JSONBase{ID: "bar"},
109
+							Labels:   map[string]string{"env": "dev"},
110
+						}),
111
+					},
112
+				},
113
+			},
114
+		},
115
+		E: nil,
116
+	}
117
+	registry := NewTestEtcd(fakeClient)
118
+	routes, err := registry.ListRoutes(labels.SelectorFromSet(labels.Set{"env": "dev"}))
119
+	if err != nil {
120
+		t.Errorf("unexpected error: %v", err)
121
+	}
122
+
123
+	if len(routes.Items) != 1 || routes.Items[0].ID != "bar" {
124
+		t.Errorf("Unexpected routes list: %#v", routes)
125
+	}
126
+}
127
+
128
+func TestEtcdGetRoutes(t *testing.T) {
129
+	fakeClient := tools.NewFakeEtcdClient(t)
130
+	fakeClient.Set("/routes/foo", runtime.EncodeOrDie(latest.Codec, &api.Route{JSONBase: kubeapi.JSONBase{ID: "foo"}}), 0)
131
+	registry := NewTestEtcd(fakeClient)
132
+	route, err := registry.GetRoute("foo")
133
+	if err != nil {
134
+		t.Errorf("unexpected error: %v", err)
135
+	}
136
+
137
+	if route.ID != "foo" {
138
+		t.Errorf("Unexpected route: %#v", route)
139
+	}
140
+}
141
+
142
+func TestEtcdGetNotFoundRoutes(t *testing.T) {
143
+	fakeClient := tools.NewFakeEtcdClient(t)
144
+	fakeClient.Data["/routes/foo"] = tools.EtcdResponseWithError{
145
+		R: &etcd.Response{
146
+			Node: nil,
147
+		},
148
+		E: tools.EtcdErrorNotFound,
149
+	}
150
+	registry := NewTestEtcd(fakeClient)
151
+	route, err := registry.GetRoute("foo")
152
+	if err == nil {
153
+		t.Errorf("Unexpected non-error.")
154
+	}
155
+	if route != nil {
156
+		t.Errorf("Unexpected route: %#v", route)
157
+	}
158
+}
159
+
160
+func TestEtcdCreateRoutes(t *testing.T) {
161
+	fakeClient := tools.NewFakeEtcdClient(t)
162
+	fakeClient.TestIndex = true
163
+	fakeClient.Data["/routes/foo"] = tools.EtcdResponseWithError{
164
+		R: &etcd.Response{
165
+			Node: nil,
166
+		},
167
+		E: tools.EtcdErrorNotFound,
168
+	}
169
+	registry := NewTestEtcd(fakeClient)
170
+	err := registry.CreateRoute(&api.Route{
171
+		JSONBase: kubeapi.JSONBase{
172
+			ID: "foo",
173
+		},
174
+	})
175
+	if err != nil {
176
+		t.Fatalf("unexpected error: %v", err)
177
+	}
178
+
179
+	resp, err := fakeClient.Get("/routes/foo", false, false)
180
+	if err != nil {
181
+		t.Fatalf("Unexpected error %v", err)
182
+	}
183
+	var route api.Route
184
+	err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &route)
185
+	if err != nil {
186
+		t.Errorf("unexpected error: %v", err)
187
+	}
188
+
189
+	if route.ID != "foo" {
190
+		t.Errorf("Unexpected route: %#v %s", route, resp.Node.Value)
191
+	}
192
+}
193
+
194
+func TestEtcdCreateAlreadyExistsRoutes(t *testing.T) {
195
+	fakeClient := tools.NewFakeEtcdClient(t)
196
+	fakeClient.Data["/routes/foo"] = tools.EtcdResponseWithError{
197
+		R: &etcd.Response{
198
+			Node: &etcd.Node{
199
+				Value: runtime.EncodeOrDie(latest.Codec, &api.Route{JSONBase: kubeapi.JSONBase{ID: "foo"}}),
200
+			},
201
+		},
202
+		E: nil,
203
+	}
204
+	registry := NewTestEtcd(fakeClient)
205
+	err := registry.CreateRoute(&api.Route{
206
+		JSONBase: kubeapi.JSONBase{
207
+			ID: "foo",
208
+		},
209
+	})
210
+	if err == nil {
211
+		t.Error("Unexpected non-error")
212
+	}
213
+	if !errors.IsAlreadyExists(err) {
214
+		t.Errorf("Expected 'already exists' error, got %#v", err)
215
+	}
216
+}
217
+
218
+func TestEtcdUpdateOkRoutes(t *testing.T) {
219
+	fakeClient := tools.NewFakeEtcdClient(t)
220
+	registry := NewTestEtcd(fakeClient)
221
+	err := registry.UpdateRoute(&api.Route{})
222
+	if err != nil {
223
+		t.Error("Unexpected error")
224
+	}
225
+}
226
+
227
+func TestEtcdDeleteNotFoundRoutes(t *testing.T) {
228
+	fakeClient := tools.NewFakeEtcdClient(t)
229
+	fakeClient.Err = tools.EtcdErrorNotFound
230
+	registry := NewTestEtcd(fakeClient)
231
+	err := registry.DeleteRoute("foo")
232
+	if err == nil {
233
+		t.Error("Unexpected non-error")
234
+	}
235
+	if !errors.IsNotFound(err) {
236
+		t.Errorf("Expected 'not found' error, got %#v", err)
237
+	}
238
+}
239
+
240
+func TestEtcdDeleteErrorRoutes(t *testing.T) {
241
+	fakeClient := tools.NewFakeEtcdClient(t)
242
+	fakeClient.Err = fmt.Errorf("Some error")
243
+	registry := NewTestEtcd(fakeClient)
244
+	err := registry.DeleteRoute("foo")
245
+	if err == nil {
246
+		t.Error("Unexpected non-error")
247
+	}
248
+}
249
+
250
+func TestEtcdDeleteOkRoutes(t *testing.T) {
251
+	fakeClient := tools.NewFakeEtcdClient(t)
252
+	registry := NewTestEtcd(fakeClient)
253
+	key := "/routes/foo"
254
+	err := registry.DeleteRoute("foo")
255
+	if err != nil {
256
+		t.Errorf("Unexpected error: %#v", err)
257
+	}
258
+	if len(fakeClient.DeletedKeys) != 1 {
259
+		t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
260
+	} else if fakeClient.DeletedKeys[0] != key {
261
+		t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
262
+	}
263
+}
0 264
new file mode 100644
... ...
@@ -0,0 +1,24 @@
0
+package route
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
5
+
6
+	"github.com/openshift/origin/pkg/route/api"
7
+)
8
+
9
+// Registry is an interface for things that know how to store Routes.
10
+type Registry interface {
11
+	// ListRoutes obtains list of routes that match a selector.
12
+	ListRoutes(selector labels.Selector) (*api.RouteList, error)
13
+	// GetRoute retrieves a specific route.
14
+	GetRoute(routeID string) (*api.Route, error)
15
+	// CreateRoute creates a new route.
16
+	CreateRoute(route *api.Route) error
17
+	// UpdateRoute updates a route.
18
+	UpdateRoute(route *api.Route) error
19
+	// DeleteRoute deletes a route.
20
+	DeleteRoute(routeID string) error
21
+	// WatchRoutes watches for new/modified/deleted routes.
22
+	WatchRoutes(labels, fields labels.Selector, resourceVersion uint64) (watch.Interface, error)
23
+}
0 24
new file mode 100644
... ...
@@ -0,0 +1,114 @@
0
+package route
1
+
2
+import (
3
+	"fmt"
4
+
5
+	"code.google.com/p/go-uuid/uuid"
6
+	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
11
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
12
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
13
+
14
+	"github.com/openshift/origin/pkg/route/api"
15
+	"github.com/openshift/origin/pkg/route/api/validation"
16
+)
17
+
18
+// REST is an implementation of RESTStorage for the api server.
19
+type REST struct {
20
+	registry Registry
21
+}
22
+
23
+func NewREST(registry Registry) *REST {
24
+	return &REST{
25
+		registry: registry,
26
+	}
27
+}
28
+
29
+func (rs *REST) New() runtime.Object {
30
+	return &api.Route{}
31
+}
32
+
33
+// List obtains a list of Routes that match selector.
34
+func (rs *REST) List(selector, fields labels.Selector) (runtime.Object, error) {
35
+	list, err := rs.registry.ListRoutes(selector)
36
+	if err != nil {
37
+		return nil, err
38
+	}
39
+	return list, err
40
+}
41
+
42
+// Get obtains the route specified by its id.
43
+func (rs *REST) Get(id string) (runtime.Object, error) {
44
+	route, err := rs.registry.GetRoute(id)
45
+	if err != nil {
46
+		return nil, err
47
+	}
48
+	return route, err
49
+}
50
+
51
+// Delete asynchronously deletes the Route specified by its id.
52
+func (rs *REST) Delete(id string) (<-chan runtime.Object, error) {
53
+	_, err := rs.registry.GetRoute(id)
54
+	if err != nil {
55
+		return nil, err
56
+	}
57
+	return apiserver.MakeAsync(func() (runtime.Object, error) {
58
+		return &kubeapi.Status{Status: kubeapi.StatusSuccess}, rs.registry.DeleteRoute(id)
59
+	}), nil
60
+}
61
+
62
+// Create registers a given new Route instance to rs.registry.
63
+func (rs *REST) Create(obj runtime.Object) (<-chan runtime.Object, error) {
64
+	route, ok := obj.(*api.Route)
65
+	if !ok {
66
+		return nil, fmt.Errorf("not a route: %#v", obj)
67
+	}
68
+	
69
+	if errs := validation.ValidateRoute(route); len(errs) > 0 {
70
+		return nil, errors.NewInvalid("route", route.ID, errs)
71
+	}
72
+	if len(route.ID) == 0 {
73
+		route.ID = uuid.NewUUID().String()
74
+	}
75
+
76
+	route.CreationTimestamp = util.Now()
77
+
78
+	return apiserver.MakeAsync(func() (runtime.Object, error) {
79
+		err := rs.registry.CreateRoute(route)
80
+		if err != nil {
81
+			return nil, err
82
+		}
83
+		return rs.registry.GetRoute(route.ID)
84
+	}), nil
85
+}
86
+
87
+// Update replaces a given Route instance with an existing instance in rs.registry.
88
+func (rs *REST) Update(obj runtime.Object) (<-chan runtime.Object, error) {
89
+	route, ok := obj.(*api.Route)
90
+	if !ok {
91
+		return nil, fmt.Errorf("not a route: %#v", obj)
92
+	}
93
+	if len(route.ID) == 0 {
94
+		return nil, fmt.Errorf("id is unspecified: %#v", route)
95
+	}
96
+
97
+	if errs := validation.ValidateRoute(route); len(errs) > 0 {
98
+		return nil, errors.NewInvalid("route", route.ID, errs)
99
+	}
100
+	return apiserver.MakeAsync(func() (runtime.Object, error) {
101
+		err := rs.registry.UpdateRoute(route)
102
+		if err != nil {
103
+			return nil, err
104
+		}
105
+		return rs.registry.GetRoute(route.ID)
106
+	}), nil
107
+}
108
+
109
+// Watch returns Routes events via a watch.Interface.
110
+// It implements apiserver.ResourceWatcher.
111
+func (rs *REST) Watch(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) {
112
+	return rs.registry.WatchRoutes(label, field, resourceVersion)
113
+}
0 114
new file mode 100644
... ...
@@ -0,0 +1,279 @@
0
+package route
1
+
2
+import (
3
+	"strings"
4
+	"testing"
5
+	"time"
6
+
7
+	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
9
+	"github.com/openshift/origin/pkg/route/api"
10
+	"github.com/openshift/origin/pkg/route/registry/test"
11
+)
12
+
13
+func TestListRoutesEmptyList(t *testing.T) {
14
+	mockRegistry := test.NewRouteRegistry()
15
+	mockRegistry.Routes = &api.RouteList{
16
+		Items: []api.Route{},
17
+	}
18
+
19
+	storage := REST{
20
+		registry: mockRegistry,
21
+	}
22
+
23
+	routes, err := storage.List(labels.Everything(), labels.Everything())
24
+	if err != nil {
25
+		t.Errorf("Unexpected non-nil error: %#v", err)
26
+	}
27
+
28
+	if len(routes.(*api.RouteList).Items) != 0 {
29
+		t.Errorf("Unexpected non-zero routes list: %#v", routes)
30
+	}
31
+}
32
+
33
+func TestListRoutesPopulatedList(t *testing.T) {
34
+	mockRegistry := test.NewRouteRegistry()
35
+	mockRegistry.Routes = &api.RouteList{
36
+		Items: []api.Route{
37
+			{
38
+				JSONBase: kubeapi.JSONBase{
39
+					ID: "foo",
40
+				},
41
+			},
42
+			{
43
+				JSONBase: kubeapi.JSONBase{
44
+					ID: "bar",
45
+				},
46
+			},
47
+		},
48
+	}
49
+
50
+	storage := REST{
51
+		registry: mockRegistry,
52
+	}
53
+
54
+	list, err := storage.List(labels.Everything(), labels.Everything())
55
+	if err != nil {
56
+		t.Errorf("Unexpected non-nil error: %#v", err)
57
+	}
58
+
59
+	routes := list.(*api.RouteList)
60
+
61
+	if e, a := 2, len(routes.Items); e != a {
62
+		t.Errorf("Expected %v, got %v", e, a)
63
+	}
64
+}
65
+
66
+func TestCreateRouteBadObject(t *testing.T) {
67
+	storage := REST{}
68
+
69
+	channel, err := storage.Create(&api.RouteList{})
70
+	if channel != nil {
71
+		t.Errorf("Expected nil, got %v", channel)
72
+	}
73
+	if strings.Index(err.Error(), "not a route") == -1 {
74
+		t.Errorf("Expected 'not a route' error, got '%v'", err.Error())
75
+	}
76
+}
77
+
78
+func TestCreateRouteOK(t *testing.T) {
79
+	mockRegistry := test.NewRouteRegistry()
80
+	storage := REST{registry: mockRegistry}
81
+
82
+	channel, err := storage.Create(&api.Route{
83
+		JSONBase: kubeapi.JSONBase{ID: "foo"},
84
+		Host: "www.frontend.com",
85
+		ServiceName: "myrubyservice",
86
+	})
87
+	if channel == nil {
88
+		t.Errorf("Expected nil channel, got %v", channel)
89
+	}
90
+	if err != nil {
91
+		t.Errorf("Unexpected non-nil error: %#v", err)
92
+	}
93
+
94
+	select {
95
+	case result := <-channel:
96
+		route, ok := result.(*api.Route)
97
+		if !ok {
98
+			t.Errorf("Expected route type, got: %#v", result)
99
+		}
100
+		if route.ID != "foo" {
101
+			t.Errorf("Unexpected route: %#v", route)
102
+		}
103
+	case <-time.After(50 * time.Millisecond):
104
+		t.Errorf("Timed out waiting for result")
105
+	default:
106
+	}
107
+}
108
+
109
+func TestGetRouteError(t *testing.T) {
110
+	mockRegistry := test.NewRouteRegistry()
111
+	storage := REST{registry: mockRegistry}
112
+
113
+	route, err := storage.Get("foo")
114
+	if route != nil {
115
+		t.Errorf("Unexpected non-nil route: %#v", route)
116
+	}
117
+	expectedError := "Route foo not found"
118
+	if err.Error() != expectedError {
119
+		t.Errorf("Expected %#v, got %#v", expectedError, err.Error())
120
+	}
121
+}
122
+
123
+func TestGetRouteOK(t *testing.T) {
124
+	mockRegistry := test.NewRouteRegistry()
125
+	mockRegistry.Routes = &api.RouteList{
126
+		Items: []api.Route{
127
+			{
128
+				JSONBase: kubeapi.JSONBase{ID: "foo"},
129
+			},
130
+		},
131
+	}
132
+	storage := REST{registry: mockRegistry}
133
+
134
+	route, err := storage.Get("foo")
135
+	if route == nil {
136
+		t.Error("Unexpected nil route")
137
+	}
138
+	if err != nil {
139
+		t.Errorf("Unexpected non-nil error", err)
140
+	}
141
+	if route.(*api.Route).ID != "foo" {
142
+		t.Errorf("Unexpected route: %#v", route)
143
+	}
144
+}
145
+
146
+func TestUpdateRouteBadObject(t *testing.T) {
147
+	storage := REST{}
148
+
149
+	channel, err := storage.Update(&api.RouteList{})
150
+	if channel != nil {
151
+		t.Errorf("Expected nil, got %v", channel)
152
+	}
153
+	if strings.Index(err.Error(), "not a route:") == -1 {
154
+		t.Errorf("Expected 'not a route' error, got %v", err)
155
+	}
156
+}
157
+
158
+func TestUpdateRouteMissingID(t *testing.T) {
159
+	storage := REST{}
160
+
161
+	channel, err := storage.Update(&api.Route{})
162
+	if channel != nil {
163
+		t.Errorf("Expected nil, got %v", channel)
164
+	}
165
+	if strings.Index(err.Error(), "id is unspecified:") == -1 {
166
+		t.Errorf("Expected 'id is unspecified' error, got %v", err)
167
+	}
168
+}
169
+
170
+func TestUpdateRegistryErrorSaving(t *testing.T) {
171
+	mockRepositoryRegistry := test.NewRouteRegistry()
172
+	storage := REST{registry: mockRepositoryRegistry}
173
+
174
+	channel, err := storage.Update(&api.Route{
175
+		JSONBase: kubeapi.JSONBase{ID: "foo"},
176
+		Host: "www.frontend.com",
177
+		ServiceName: "rubyservice",
178
+	})
179
+	if err != nil {
180
+		t.Errorf("Unexpected non-nil error: %#v", err)
181
+	}
182
+	result := <-channel
183
+	status, ok := result.(*kubeapi.Status)
184
+	if !ok {
185
+		t.Errorf("Expected status, got %#v", result)
186
+	}
187
+	if status.Status != "failure" || status.Message != "Route foo not found" {
188
+		t.Errorf("Expected status=failure, message=Route foo not found, got %#v", status)
189
+	}
190
+}
191
+
192
+func TestUpdateRouteOK(t *testing.T) {
193
+	mockRepositoryRegistry := test.NewRouteRegistry()
194
+	mockRepositoryRegistry.Routes = &api.RouteList{
195
+		Items: []api.Route{
196
+			{
197
+				JSONBase: kubeapi.JSONBase{ID: "bar"},
198
+				Host: "www.frontend.com",
199
+				ServiceName: "rubyservice",
200
+			},
201
+		},
202
+	}
203
+
204
+	storage := REST{registry: mockRepositoryRegistry}
205
+
206
+	channel, err := storage.Update(&api.Route{
207
+		JSONBase: kubeapi.JSONBase{ID: "bar"},
208
+		Host: "www.newfrontend.com",
209
+		ServiceName: "newrubyservice",
210
+	})
211
+									
212
+	if err != nil {
213
+		t.Errorf("Unexpected non-nil error: %#v", err)
214
+	}
215
+	result := <-channel
216
+	route, ok := result.(*api.Route)
217
+	if !ok {
218
+		t.Errorf("Expected Route, got %#v", result)
219
+	}
220
+	if route == nil {
221
+		t.Errorf("Nil route returned: %#v", route)
222
+		t.Errorf("Expected Route, got %#v", result)
223
+	}
224
+	if route.ID != "bar" {
225
+		t.Errorf("Unexpected route returned: %#v", route)
226
+	}
227
+	if route.Host != "www.newfrontend.com" {
228
+		t.Errorf("Updated route not returned: %#v", route)
229
+	}
230
+	if route.ServiceName != "newrubyservice" {
231
+		t.Errorf("Updated route not returned: %#v", route)
232
+	}
233
+}
234
+
235
+func TestDeleteRouteError(t *testing.T) {
236
+	mockRegistry := test.NewRouteRegistry()
237
+	storage := REST{registry: mockRegistry}
238
+	_, err := storage.Delete("foo")
239
+	if err == nil {
240
+		t.Errorf("Unexpected nil error: %#v", err)
241
+	}
242
+	if err.Error() != "Route foo not found" {
243
+		t.Errorf("Expected %#v, got %#v", "Route foo not found", err.Error())
244
+	}
245
+}
246
+
247
+func TestDeleteRouteOk(t *testing.T) {
248
+	mockRegistry := test.NewRouteRegistry()
249
+	mockRegistry.Routes = &api.RouteList{
250
+		Items: []api.Route{
251
+			{
252
+				JSONBase: kubeapi.JSONBase{ID: "foo"},
253
+			},
254
+		},
255
+	}
256
+	storage := REST{registry: mockRegistry}
257
+	channel, err := storage.Delete("foo")
258
+	if channel == nil {
259
+		t.Error("Unexpected nil channel")
260
+	}
261
+	if err != nil {
262
+		t.Errorf("Unexpected non-nil error: %#v", err)
263
+	}
264
+
265
+	select {
266
+	case result := <-channel:
267
+		status, ok := result.(*kubeapi.Status)
268
+		if !ok {
269
+			t.Errorf("Expected status type, got: %#v", result)
270
+		}
271
+		if status.Status != "success" {
272
+			t.Errorf("Expected status=success, got: %#v", status)
273
+		}
274
+	case <-time.After(50 * time.Millisecond):
275
+		t.Errorf("Timed out waiting for result")
276
+	default:
277
+	}
278
+}
0 279
new file mode 100644
... ...
@@ -0,0 +1,85 @@
0
+package test
1
+
2
+import (
3
+	"errors"
4
+	
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
7
+	routeapi "github.com/openshift/origin/pkg/route/api"
8
+)
9
+
10
+type RouteRegistry struct {
11
+	Routes         *routeapi.RouteList
12
+}
13
+
14
+func NewRouteRegistry() *RouteRegistry {
15
+	return &RouteRegistry{}
16
+}
17
+
18
+func (r *RouteRegistry) ListRoutes(labels labels.Selector) (*routeapi.RouteList, error) {
19
+	return r.Routes, nil
20
+}
21
+
22
+func (r *RouteRegistry) GetRoute(id string) (*routeapi.Route, error) {
23
+	if r.Routes != nil {
24
+		for _, route := range r.Routes.Items {
25
+			if route.ID == id {
26
+				return &route, nil
27
+			}
28
+		}
29
+	}
30
+	return nil, errors.New("Route " + id + " not found")
31
+}
32
+
33
+func (r *RouteRegistry) CreateRoute(route *routeapi.Route) error {
34
+	if r.Routes == nil {
35
+		r.Routes = &routeapi.RouteList{}
36
+	}
37
+	newList := []routeapi.Route{}
38
+	for _, curRoute := range r.Routes.Items {
39
+		newList = append(newList, curRoute)
40
+	}
41
+	newList = append(newList, *route)												
42
+	r.Routes.Items = newList
43
+	return nil
44
+}
45
+
46
+func (r *RouteRegistry) UpdateRoute(route *routeapi.Route) error {
47
+	if r.Routes == nil {
48
+		r.Routes = &routeapi.RouteList{}
49
+	}
50
+	newList := []routeapi.Route{}
51
+	found := false
52
+	for _, curRoute := range r.Routes.Items {
53
+		if curRoute.ID == route.ID {
54
+			// route to be updated exists
55
+			found = true
56
+		} else {
57
+			newList = append(newList, curRoute)
58
+		}
59
+	}
60
+	if !found {
61
+		return errors.New("Route " + route.ID + " not found")
62
+	}
63
+	newList = append(newList, *route)
64
+	r.Routes.Items = newList
65
+	return nil
66
+}
67
+
68
+func (r *RouteRegistry) DeleteRoute(id string) error {
69
+	if r.Routes == nil {
70
+		r.Routes = &routeapi.RouteList{}
71
+	}
72
+	newList := []routeapi.Route{}
73
+	for _, curRoute := range r.Routes.Items {
74
+		if curRoute.ID != id {
75
+			newList = append(newList, curRoute)
76
+		}
77
+	}
78
+	r.Routes.Items = newList
79
+	return nil
80
+}
81
+
82
+func (r *RouteRegistry) WatchRoutes(labels, fields labels.Selector, resourceVersion uint64) (watch.Interface, error) {
83
+	return nil, nil
84
+}