Merge pull request #164 from lavalamp/master

Standardize terminology on "selector"
pull/6/head
brendandburns 2014-06-19 13:55:27 -07:00
commit e031c193a3
40 changed files with 179 additions and 138 deletions

View File

@ -30,7 +30,7 @@
"required": false, "required": false,
"description": "Number of pods desired in the set" "description": "Number of pods desired in the set"
}, },
"replicasInSet": { "replicaSelector": {
"type": "object", "type": "object",
"required": false, "required": false,
"description": "Required labels used to identify pods in the set" "description": "Required labels used to identify pods in the set"

View File

@ -32,5 +32,9 @@
"type": "object", "type": "object",
"required": false "required": false
} }
"selector": {
"type": "object",
"required": false
}
} }
} }

View File

@ -4,7 +4,7 @@
"id": "testRun", "id": "testRun",
"desiredState": { "desiredState": {
"replicas": 2, "replicas": 2,
"replicasInSet": { "replicaSelector": {
"name": "testRun" "name": "testRun"
}, },
"podTemplate": { "podTemplate": {

View File

@ -2,7 +2,7 @@
"id": "nginxController", "id": "nginxController",
"desiredState": { "desiredState": {
"replicas": 2, "replicas": 2,
"replicasInSet": {"name": "nginx"}, "replicaSelector": {"name": "nginx"},
"podTemplate": { "podTemplate": {
"desiredState": { "desiredState": {
"manifest": { "manifest": {

View File

@ -4,5 +4,8 @@
"labels": { "labels": {
"name": "nginx" "name": "nginx"
}, },
"selector": {
"name": "nginx"
},
"createExternalLoadBalancer": true "createExternalLoadBalancer": true
} }

View File

@ -6,6 +6,9 @@
"labels": { "labels": {
"name": "nginx" "name": "nginx"
} }
"selector": {
"name": "nginx"
}
}, },
{ {
"id": "example2", "id": "example2",
@ -14,6 +17,10 @@
"env": "prod", "env": "prod",
"name": "jetty" "name": "jetty"
} }
"selector": {
"env": "prod",
"name": "jetty"
}
} }
] ]
} }

View File

@ -4,4 +4,7 @@
"labels": { "labels": {
"name": "nginx" "name": "nginx"
} }
"selector": {
"name": "nginx"
}
} }

View File

@ -970,7 +970,7 @@ implemented, either.</p>
"<span class="hljs-attribute">id</span>": <span class="hljs-value"><span class="hljs-string">"testRun"</span></span>, "<span class="hljs-attribute">id</span>": <span class="hljs-value"><span class="hljs-string">"testRun"</span></span>,
"<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{ "<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{
"<span class="hljs-attribute">replicas</span>": <span class="hljs-value"><span class="hljs-number">2</span></span>, "<span class="hljs-attribute">replicas</span>": <span class="hljs-value"><span class="hljs-number">2</span></span>,
"<span class="hljs-attribute">replicasInSet</span>": <span class="hljs-value">{ "<span class="hljs-attribute">replicaSelector</span>": <span class="hljs-value">{
"<span class="hljs-attribute">name</span>": <span class="hljs-value"><span class="hljs-string">"testRun"</span> "<span class="hljs-attribute">name</span>": <span class="hljs-value"><span class="hljs-string">"testRun"</span>
</span>}</span>, </span>}</span>,
"<span class="hljs-attribute">podTemplate</span>": <span class="hljs-value">{ "<span class="hljs-attribute">podTemplate</span>": <span class="hljs-value">{
@ -1074,7 +1074,7 @@ implemented, either.</p>
"<span class="hljs-attribute">required</span>": <span class="hljs-value"><span class="hljs-literal">false</span></span>, "<span class="hljs-attribute">required</span>": <span class="hljs-value"><span class="hljs-literal">false</span></span>,
"<span class="hljs-attribute">description</span>": <span class="hljs-value"><span class="hljs-string">"Number of pods desired in the set"</span> "<span class="hljs-attribute">description</span>": <span class="hljs-value"><span class="hljs-string">"Number of pods desired in the set"</span>
</span>}</span>, </span>}</span>,
"<span class="hljs-attribute">replicasInSet</span>": <span class="hljs-value">{ "<span class="hljs-attribute">replicaSelector</span>": <span class="hljs-value">{
"<span class="hljs-attribute">type</span>": <span class="hljs-value"><span class="hljs-string">"object"</span></span>, "<span class="hljs-attribute">type</span>": <span class="hljs-value"><span class="hljs-string">"object"</span></span>,
"<span class="hljs-attribute">required</span>": <span class="hljs-value"><span class="hljs-literal">false</span></span>, "<span class="hljs-attribute">required</span>": <span class="hljs-value"><span class="hljs-literal">false</span></span>,
"<span class="hljs-attribute">description</span>": <span class="hljs-value"><span class="hljs-string">"Required labels used to identify pods in the set"</span> "<span class="hljs-attribute">description</span>": <span class="hljs-value"><span class="hljs-string">"Required labels used to identify pods in the set"</span>
@ -1100,7 +1100,7 @@ implemented, either.</p>
"<span class="hljs-attribute">id</span>": <span class="hljs-value"><span class="hljs-string">"nginxController"</span></span>, "<span class="hljs-attribute">id</span>": <span class="hljs-value"><span class="hljs-string">"nginxController"</span></span>,
"<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{ "<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{
"<span class="hljs-attribute">replicas</span>": <span class="hljs-value"><span class="hljs-number">2</span></span>, "<span class="hljs-attribute">replicas</span>": <span class="hljs-value"><span class="hljs-number">2</span></span>,
"<span class="hljs-attribute">replicasInSet</span>": <span class="hljs-value">{"<span class="hljs-attribute">name</span>": <span class="hljs-value"><span class="hljs-string">"nginx"</span></span>}</span>, "<span class="hljs-attribute">replicaSelector</span>": <span class="hljs-value">{"<span class="hljs-attribute">name</span>": <span class="hljs-value"><span class="hljs-string">"nginx"</span></span>}</span>,
"<span class="hljs-attribute">podTemplate</span>": <span class="hljs-value">{ "<span class="hljs-attribute">podTemplate</span>": <span class="hljs-value">{
"<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{ "<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{
"<span class="hljs-attribute">manifest</span>": <span class="hljs-value">{ "<span class="hljs-attribute">manifest</span>": <span class="hljs-value">{
@ -1231,7 +1231,7 @@ implemented, either.</p>
"<span class="hljs-attribute">id</span>": <span class="hljs-value"><span class="hljs-string">"nginxController"</span></span>, "<span class="hljs-attribute">id</span>": <span class="hljs-value"><span class="hljs-string">"nginxController"</span></span>,
"<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{ "<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{
"<span class="hljs-attribute">replicas</span>": <span class="hljs-value"><span class="hljs-number">2</span></span>, "<span class="hljs-attribute">replicas</span>": <span class="hljs-value"><span class="hljs-number">2</span></span>,
"<span class="hljs-attribute">replicasInSet</span>": <span class="hljs-value">{"<span class="hljs-attribute">name</span>": <span class="hljs-value"><span class="hljs-string">"nginx"</span></span>}</span>, "<span class="hljs-attribute">replicaSelector</span>": <span class="hljs-value">{"<span class="hljs-attribute">name</span>": <span class="hljs-value"><span class="hljs-string">"nginx"</span></span>}</span>,
"<span class="hljs-attribute">podTemplate</span>": <span class="hljs-value">{ "<span class="hljs-attribute">podTemplate</span>": <span class="hljs-value">{
"<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{ "<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{
"<span class="hljs-attribute">manifest</span>": <span class="hljs-value">{ "<span class="hljs-attribute">manifest</span>": <span class="hljs-value">{
@ -1333,7 +1333,7 @@ implemented, either.</p>
"<span class="hljs-attribute">required</span>": <span class="hljs-value"><span class="hljs-literal">false</span></span>, "<span class="hljs-attribute">required</span>": <span class="hljs-value"><span class="hljs-literal">false</span></span>,
"<span class="hljs-attribute">description</span>": <span class="hljs-value"><span class="hljs-string">"Number of pods desired in the set"</span> "<span class="hljs-attribute">description</span>": <span class="hljs-value"><span class="hljs-string">"Number of pods desired in the set"</span>
</span>}</span>, </span>}</span>,
"<span class="hljs-attribute">replicasInSet</span>": <span class="hljs-value">{ "<span class="hljs-attribute">replicaSelector</span>": <span class="hljs-value">{
"<span class="hljs-attribute">type</span>": <span class="hljs-value"><span class="hljs-string">"object"</span></span>, "<span class="hljs-attribute">type</span>": <span class="hljs-value"><span class="hljs-string">"object"</span></span>,
"<span class="hljs-attribute">required</span>": <span class="hljs-value"><span class="hljs-literal">false</span></span>, "<span class="hljs-attribute">required</span>": <span class="hljs-value"><span class="hljs-literal">false</span></span>,
"<span class="hljs-attribute">description</span>": <span class="hljs-value"><span class="hljs-string">"Required labels used to identify pods in the set"</span> "<span class="hljs-attribute">description</span>": <span class="hljs-value"><span class="hljs-string">"Required labels used to identify pods in the set"</span>
@ -1359,7 +1359,7 @@ implemented, either.</p>
"<span class="hljs-attribute">id</span>": <span class="hljs-value"><span class="hljs-string">"nginxController"</span></span>, "<span class="hljs-attribute">id</span>": <span class="hljs-value"><span class="hljs-string">"nginxController"</span></span>,
"<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{ "<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{
"<span class="hljs-attribute">replicas</span>": <span class="hljs-value"><span class="hljs-number">2</span></span>, "<span class="hljs-attribute">replicas</span>": <span class="hljs-value"><span class="hljs-number">2</span></span>,
"<span class="hljs-attribute">replicasInSet</span>": <span class="hljs-value">{"<span class="hljs-attribute">name</span>": <span class="hljs-value"><span class="hljs-string">"nginx"</span></span>}</span>, "<span class="hljs-attribute">replicaSelector</span>": <span class="hljs-value">{"<span class="hljs-attribute">name</span>": <span class="hljs-value"><span class="hljs-string">"nginx"</span></span>}</span>,
"<span class="hljs-attribute">podTemplate</span>": <span class="hljs-value">{ "<span class="hljs-attribute">podTemplate</span>": <span class="hljs-value">{
"<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{ "<span class="hljs-attribute">desiredState</span>": <span class="hljs-value">{
"<span class="hljs-attribute">manifest</span>": <span class="hljs-value">{ "<span class="hljs-attribute">manifest</span>": <span class="hljs-value">{

View File

@ -39,7 +39,7 @@ var (
versionFlag = flag.Bool("v", false, "Print the version number.") versionFlag = flag.Bool("v", false, "Print the version number.")
httpServer = flag.String("h", "", "The host to connect to.") httpServer = flag.String("h", "", "The host to connect to.")
config = flag.String("c", "", "Path to the config file.") config = flag.String("c", "", "Path to the config file.")
labelQuery = flag.String("l", "", "Label query to use for listing") selector = flag.String("l", "", "Selector (label query) to use for listing")
updatePeriod = flag.Duration("u", 60*time.Second, "Update interarrival period") updatePeriod = flag.Duration("u", 60*time.Second, "Update interarrival period")
portSpec = flag.String("p", "", "The port spec, comma-separated list of <external>:<internal>,...") portSpec = flag.String("p", "", "The port spec, comma-separated list of <external>:<internal>,...")
servicePort = flag.Int("s", -1, "If positive, create and run a corresponding service on this port, only used with 'run'") servicePort = flag.Int("s", -1, "If positive, create and run a corresponding service on this port, only used with 'run'")
@ -143,8 +143,8 @@ func executeAPIRequest(method string, auth *kube_client.AuthInfo) bool {
switch method { switch method {
case "get", "list": case "get", "list":
url := readUrl(parseStorage()) url := readUrl(parseStorage())
if len(*labelQuery) > 0 && method == "list" { if len(*selector) > 0 && method == "list" {
url = url + "?labels=" + *labelQuery url = url + "?labels=" + *selector
} }
request, err = http.NewRequest("GET", url, nil) request, err = http.NewRequest("GET", url, nil)
case "delete": case "delete":

View File

@ -2,7 +2,7 @@
"id": "frontendController", "id": "frontendController",
"desiredState": { "desiredState": {
"replicas": 1, "replicas": 1,
"replicasInSet": {"name": "frontend"}, "replicaSelector": {"name": "frontend"},
"podTemplate": { "podTemplate": {
"desiredState": { "desiredState": {
"manifest": { "manifest": {

View File

@ -73,18 +73,20 @@ CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
### Step Two: Turn up the master service. ### Step Two: Turn up the master service.
A Kubernetes 'service' is a named load balancer that proxies traffic to one or more containers. The services in a Kubernetes cluster are discoverable inside other containers via environment variables. Services find the containers to load balance based on pod labels. A Kubernetes 'service' is a named load balancer that proxies traffic to one or more containers. The services in a Kubernetes cluster are discoverable inside other containers via environment variables. Services find the containers to load balance based on pod labels.
The pod that you created in Step One has the label `name=redis-master`, so the corresponding service is defined by that label. Create a file named `redis-master-service.json` that contains: The pod that you created in Step One has the label `name=redis-master`. The selector field of the service determines which pods will receive the traffic sent to the service. Create a file named `redis-master-service.json` that contains:
```js ```js
{ {
"id": "redismaster", "id": "redismaster",
"port": 10000, "port": 10000,
"labels": { "selector": {
"name": "redis-master" "name": "redis-master"
} }
} }
``` ```
This will cause all pods to see the redis master apparently running on localhost:10000.
Once you have that service description, you can create the service with the `cloudcfg` cli: Once you have that service description, you can create the service with the `cloudcfg` cli:
```shell ```shell
@ -106,7 +108,7 @@ Create a file named `redis-slave-controller.json` that contains:
"id": "redisSlaveController", "id": "redisSlaveController",
"desiredState": { "desiredState": {
"replicas": 2, "replicas": 2,
"replicasInSet": {"name": "redis-slave"}, "replicaSelector": {"name": "redis-slave"},
"podTemplate": { "podTemplate": {
"desiredState": { "desiredState": {
"manifest": { "manifest": {
@ -126,7 +128,7 @@ Then you can create the service by running:
```shell ```shell
$ cluster/cloudcfg.sh -c examples/guestbook/redis-slave-controller.json create /replicationControllers $ cluster/cloudcfg.sh -c examples/guestbook/redis-slave-controller.json create /replicationControllers
Name Image(s) Label Query Replicas Name Image(s) Selector Replicas
---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
redisSlaveController brendanburns/redis-slave name=redisslave 2 redisSlaveController brendanburns/redis-slave name=redisslave 2
``` ```
@ -160,11 +162,14 @@ Just like the master, we want to have a service to proxy connections to the read
"port": 10001, "port": 10001,
"labels": { "labels": {
"name": "redis-slave" "name": "redis-slave"
},
"selector": {
"name": "redis-slave"
} }
} }
``` ```
This time the label query for the service is `name=redis-slave`. This time the selector for the service is `name=redis-slave`, because that identifies the pods running redis slaves. It may also be helpful to set labels on your service itself--as we've done here--to make it easy to locate them with the `cloudcfg -l "label=value" list sevices` command.
Now that you have created the service specification, create it in your cluster with the `cloudcfg` CLI: Now that you have created the service specification, create it in your cluster with the `cloudcfg` CLI:
@ -186,7 +191,7 @@ Create a file named `frontend-controller.json`:
"id": "frontendController", "id": "frontendController",
"desiredState": { "desiredState": {
"replicas": 3, "replicas": 3,
"replicasInSet": {"name": "frontend"}, "replicaSelector": {"name": "frontend"},
"podTemplate": { "podTemplate": {
"desiredState": { "desiredState": {
"manifest": { "manifest": {
@ -206,7 +211,7 @@ With this file, you can turn up your frontend with:
```shell ```shell
$ cluster/cloudcfg.sh -c examples/guestbook/frontend-controller.json create /replicationControllers $ cluster/cloudcfg.sh -c examples/guestbook/frontend-controller.json create /replicationControllers
Name Image(s) Label Query Replicas Name Image(s) Selector Replicas
---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
frontendController brendanburns/php-redis name=frontend 3 frontendController brendanburns/php-redis name=frontend 3
``` ```

View File

@ -1,7 +1,7 @@
{ {
"id": "redismaster", "id": "redismaster",
"port": 10000, "port": 10000,
"labels": { "selector": {
"name": "redis-master" "name": "redis-master"
} }
} }

View File

@ -2,7 +2,7 @@
"id": "redisSlaveController", "id": "redisSlaveController",
"desiredState": { "desiredState": {
"replicas": 2, "replicas": 2,
"replicasInSet": {"name": "redisslave"}, "replicaSelector": {"name": "redisslave"},
"podTemplate": { "podTemplate": {
"desiredState": { "desiredState": {
"manifest": { "manifest": {

View File

@ -4,4 +4,7 @@
"labels": { "labels": {
"name": "redisslave" "name": "redisslave"
} }
"selector": {
"name": "redisslave"
}
} }

View File

@ -108,9 +108,9 @@ type Pod struct {
// ReplicationControllerState is the state of a replication controller, either input (create, update) or as output (list, get) // ReplicationControllerState is the state of a replication controller, either input (create, update) or as output (list, get)
type ReplicationControllerState struct { type ReplicationControllerState struct {
Replicas int `json:"replicas" yaml:"replicas"` Replicas int `json:"replicas" yaml:"replicas"`
ReplicasInSet map[string]string `json:"replicasInSet,omitempty" yaml:"replicasInSet,omitempty"` ReplicaSelector map[string]string `json:"replicaSelector,omitempty" yaml:"replicaSelector,omitempty"`
PodTemplate PodTemplate `json:"podTemplate,omitempty" yaml:"podTemplate,omitempty"` PodTemplate PodTemplate `json:"podTemplate,omitempty" yaml:"podTemplate,omitempty"`
} }
type ReplicationControllerList struct { type ReplicationControllerList struct {
@ -138,11 +138,17 @@ type ServiceList struct {
} }
// Defines a service abstraction by a name (for example, mysql) consisting of local port // Defines a service abstraction by a name (for example, mysql) consisting of local port
// (for example 3306) that the proxy listens on, and the labels that define the service. // (for example 3306) that the proxy listens on, and the selector that determines which pods
// will answer requests sent through the proxy.
type Service struct { type Service struct {
JSONBase `json:",inline" yaml:",inline"` JSONBase `json:",inline" yaml:",inline"`
Port int `json:"port,omitempty" yaml:"port,omitempty"` Port int `json:"port,omitempty" yaml:"port,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
// This service's labels.
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
// This service will route traffic to pods having labels matching this selector.
Selector map[string]string `json:"selector,omitempty" yaml:"selector,omitempty"`
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" yaml:"createExternalLoadBalancer,omitempty"` CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" yaml:"createExternalLoadBalancer,omitempty"`
} }

View File

@ -31,7 +31,7 @@ import (
// RESTStorage is a generic interface for RESTful storage services // RESTStorage is a generic interface for RESTful storage services
type RESTStorage interface { type RESTStorage interface {
List(labels.Query) (interface{}, error) List(labels.Selector) (interface{}, error)
Get(id string) (interface{}, error) Get(id string) (interface{}, error)
Delete(id string) error Delete(id string) error
Extract(body string) (interface{}, error) Extract(body string) (interface{}, error)
@ -149,12 +149,12 @@ func (server *ApiServer) handleREST(parts []string, requestUrl *url.URL, req *ht
case "GET": case "GET":
switch len(parts) { switch len(parts) {
case 1: case 1:
query, err := labels.ParseQuery(requestUrl.Query().Get("labels")) selector, err := labels.ParseSelector(requestUrl.Query().Get("labels"))
if err != nil { if err != nil {
server.error(err, w) server.error(err, w)
return return
} }
controllers, err := storage.List(query) controllers, err := storage.List(selector)
if err != nil { if err != nil {
server.error(err, w) server.error(err, w)
return return

View File

@ -51,7 +51,7 @@ type SimpleRESTStorage struct {
updated Simple updated Simple
} }
func (storage *SimpleRESTStorage) List(labels.Query) (interface{}, error) { func (storage *SimpleRESTStorage) List(labels.Selector) (interface{}, error) {
result := SimpleList{ result := SimpleList{
Items: storage.list, Items: storage.list,
} }

View File

@ -33,7 +33,7 @@ import (
// ClientInterface holds the methods for clients of Kubenetes, an interface to allow mock testing // ClientInterface holds the methods for clients of Kubenetes, an interface to allow mock testing
type ClientInterface interface { type ClientInterface interface {
ListPods(labelQuery map[string]string) (api.PodList, error) ListPods(selector map[string]string) (api.PodList, error)
GetPod(name string) (api.Pod, error) GetPod(name string) (api.Pod, error)
DeletePod(name string) error DeletePod(name string) error
CreatePod(api.Pod) (api.Pod, error) CreatePod(api.Pod) (api.Pod, error)
@ -112,40 +112,40 @@ func (client Client) makeURL(path string) string {
return client.Host + "/api/v1beta1/" + path return client.Host + "/api/v1beta1/" + path
} }
// EncodeLabelQuery transforms a label query expressed as a key/value map, into a // EncodeSelector transforms a selector expressed as a key/value map, into a
// comma separated, key=value encoding. // comma separated, key=value encoding.
func EncodeLabelQuery(labelQuery map[string]string) string { func EncodeSelector(selector map[string]string) string {
query := make([]string, 0, len(labelQuery)) parts := make([]string, 0, len(selector))
for key, value := range labelQuery { for key, value := range selector {
query = append(query, key+"="+value) parts = append(parts, key+"="+value)
} }
return url.QueryEscape(strings.Join(query, ",")) return url.QueryEscape(strings.Join(parts, ","))
} }
// DecodeLabelQuery transforms a label query from a comma separated, key=value format into // DecodeSelector transforms a selector from a comma separated, key=value format into
// a key/value map. // a key/value map.
func DecodeLabelQuery(labelQuery string) map[string]string { func DecodeSelector(selector string) map[string]string {
result := map[string]string{} result := map[string]string{}
if len(labelQuery) == 0 { if len(selector) == 0 {
return result return result
} }
parts := strings.Split(labelQuery, ",") parts := strings.Split(selector, ",")
for _, part := range parts { for _, part := range parts {
pieces := strings.Split(part, "=") pieces := strings.Split(part, "=")
if len(pieces) == 2 { if len(pieces) == 2 {
result[pieces[0]] = pieces[1] result[pieces[0]] = pieces[1]
} else { } else {
log.Printf("Invalid label query: %s", labelQuery) log.Printf("Invalid selector: %s", selector)
} }
} }
return result return result
} }
// ListPods takes a label query, and returns the list of pods that match that query // ListPods takes a selector, and returns the list of pods that match that selector
func (client Client) ListPods(labelQuery map[string]string) (api.PodList, error) { func (client Client) ListPods(selector map[string]string) (api.PodList, error) {
path := "pods" path := "pods"
if labelQuery != nil && len(labelQuery) > 0 { if selector != nil && len(selector) > 0 {
path += "?labels=" + EncodeLabelQuery(labelQuery) path += "?labels=" + EncodeSelector(selector)
} }
var result api.PodList var result api.PodList
_, err := client.rawRequest("GET", path, nil, &result) _, err := client.rawRequest("GET", path, nil, &result)

View File

@ -117,13 +117,13 @@ func TestListPodsLabels(t *testing.T) {
client := Client{ client := Client{
Host: testServer.URL, Host: testServer.URL,
} }
query := map[string]string{"foo": "bar", "name": "baz"} selector := map[string]string{"foo": "bar", "name": "baz"}
receivedPodList, err := client.ListPods(query) receivedPodList, err := client.ListPods(selector)
fakeHandler.ValidateRequest(t, makeUrl("/pods"), "GET", nil) fakeHandler.ValidateRequest(t, makeUrl("/pods"), "GET", nil)
queryString := fakeHandler.RequestReceived.URL.Query().Get("labels") selectorString := fakeHandler.RequestReceived.URL.Query().Get("labels")
queryString, _ = url.QueryUnescape(queryString) selectorString, _ = url.QueryUnescape(selectorString)
parsedQueryString := DecodeLabelQuery(queryString) parsedSelectorString := DecodeSelector(selectorString)
expectEqual(t, query, parsedQueryString) expectEqual(t, selector, parsedSelectorString)
if err != nil { if err != nil {
t.Errorf("Unexpected error in listing pods: %#v", err) t.Errorf("Unexpected error in listing pods: %#v", err)
} }

View File

@ -74,7 +74,7 @@ func Update(name string, client client.ClientInterface, updatePeriod time.Durati
if err != nil { if err != nil {
return err return err
} }
labels := controller.DesiredState.ReplicasInSet labels := controller.DesiredState.ReplicaSelector
podList, err := client.ListPods(labels) podList, err := client.ListPods(labels)
if err != nil { if err != nil {
@ -200,7 +200,7 @@ func RunController(image, name string, replicas int, client client.ClientInterfa
}, },
DesiredState: api.ReplicationControllerState{ DesiredState: api.ReplicationControllerState{
Replicas: replicas, Replicas: replicas,
ReplicasInSet: map[string]string{ ReplicaSelector: map[string]string{
"name": name, "name": name,
}, },
PodTemplate: api.PodTemplate{ PodTemplate: api.PodTemplate{
@ -255,6 +255,9 @@ func createService(name string, port int, client client.ClientInterface) (api.Se
Labels: map[string]string{ Labels: map[string]string{
"name": name, "name": name,
}, },
Selector: map[string]string{
"name": name,
},
} }
svc, err := client.CreateService(svc) svc, err := client.CreateService(svc)
return svc, err return svc, err

View File

@ -46,7 +46,7 @@ type FakeKubeClient struct {
ctrl api.ReplicationController ctrl api.ReplicationController
} }
func (client *FakeKubeClient) ListPods(labelQuery map[string]string) (api.PodList, error) { func (client *FakeKubeClient) ListPods(selector map[string]string) (api.PodList, error) {
client.actions = append(client.actions, Action{action: "list-pods"}) client.actions = append(client.actions, Action{action: "list-pods"})
return client.pods, nil return client.pods, nil
} }

View File

@ -63,6 +63,9 @@ func TestParseService(t *testing.T) {
Labels: map[string]string{ Labels: map[string]string{
"area": "staging", "area": "staging",
}, },
Selector: map[string]string{
"area": "staging",
},
}) })
} }

View File

@ -24,6 +24,7 @@ import (
"text/tabwriter" "text/tabwriter"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"gopkg.in/v1/yaml" "gopkg.in/v1/yaml"
) )
@ -61,8 +62,8 @@ func (y *YAMLPrinter) Print(data string, w io.Writer) error {
type HumanReadablePrinter struct{} type HumanReadablePrinter struct{}
var podColumns = []string{"Name", "Image(s)", "Host", "Labels"} var podColumns = []string{"Name", "Image(s)", "Host", "Labels"}
var replicationControllerColumns = []string{"Name", "Image(s)", "Label Query", "Replicas"} var replicationControllerColumns = []string{"Name", "Image(s)", "Selector", "Replicas"}
var serviceColumns = []string{"Name", "Label Query", "Port"} var serviceColumns = []string{"Name", "Labels", "Selector", "Port"}
func (h *HumanReadablePrinter) unknown(data string, w io.Writer) error { func (h *HumanReadablePrinter) unknown(data string, w io.Writer) error {
_, err := fmt.Fprintf(w, "Unknown object: %s", data) _, err := fmt.Fprintf(w, "Unknown object: %s", data)
@ -89,17 +90,9 @@ func (h *HumanReadablePrinter) makeImageList(manifest api.ContainerManifest) str
return strings.Join(images, ",") return strings.Join(images, ",")
} }
func (h *HumanReadablePrinter) makeLabelsList(labels map[string]string) string {
var vals []string
for key, value := range labels {
vals = append(vals, fmt.Sprintf("%s=%s", key, value))
}
return strings.Join(vals, ",")
}
func (h *HumanReadablePrinter) printPod(pod api.Pod, w io.Writer) error { func (h *HumanReadablePrinter) printPod(pod api.Pod, w io.Writer) error {
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n",
pod.ID, h.makeImageList(pod.DesiredState.Manifest), pod.CurrentState.Host+"/"+pod.CurrentState.HostIP, h.makeLabelsList(pod.Labels)) pod.ID, h.makeImageList(pod.DesiredState.Manifest), pod.CurrentState.Host+"/"+pod.CurrentState.HostIP, labels.Set(pod.Labels))
return err return err
} }
@ -114,7 +107,7 @@ func (h *HumanReadablePrinter) printPodList(podList api.PodList, w io.Writer) er
func (h *HumanReadablePrinter) printReplicationController(ctrl api.ReplicationController, w io.Writer) error { func (h *HumanReadablePrinter) printReplicationController(ctrl api.ReplicationController, w io.Writer) error {
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\n", _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\n",
ctrl.ID, h.makeImageList(ctrl.DesiredState.PodTemplate.DesiredState.Manifest), h.makeLabelsList(ctrl.DesiredState.ReplicasInSet), ctrl.DesiredState.Replicas) ctrl.ID, h.makeImageList(ctrl.DesiredState.PodTemplate.DesiredState.Manifest), labels.Set(ctrl.DesiredState.ReplicaSelector), ctrl.DesiredState.Replicas)
return err return err
} }
@ -128,7 +121,7 @@ func (h *HumanReadablePrinter) printReplicationControllerList(list api.Replicati
} }
func (h *HumanReadablePrinter) printService(svc api.Service, w io.Writer) error { func (h *HumanReadablePrinter) printService(svc api.Service, w io.Writer) error {
_, err := fmt.Fprintf(w, "%s\t%s\t%d\n", svc.ID, h.makeLabelsList(svc.Labels), svc.Port) _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\n", svc.ID, labels.Set(svc.Labels), labels.Set(svc.Selector), svc.Port)
return err return err
} }

View File

@ -177,7 +177,7 @@ func (rm *ReplicationManager) filterActivePods(pods []api.Pod) []api.Pod {
} }
func (rm *ReplicationManager) syncReplicationController(controllerSpec api.ReplicationController) error { func (rm *ReplicationManager) syncReplicationController(controllerSpec api.ReplicationController) error {
podList, err := rm.kubeClient.ListPods(controllerSpec.DesiredState.ReplicasInSet) podList, err := rm.kubeClient.ListPods(controllerSpec.DesiredState.ReplicaSelector)
if err != nil { if err != nil {
return err return err
} }

View File

@ -68,7 +68,7 @@ func (s *KubeletServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
container := u.Query().Get("container") container := u.Query().Get("container")
if len(container) == 0 { if len(container) == 0 {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, "Missing container query arg.") fmt.Fprint(w, "Missing container selector arg.")
return return
} }
id, found, err := s.Kubelet.GetContainerID(container) id, found, err := s.Kubelet.GetContainerID(container)

View File

@ -14,6 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// Package labels implements a simple label system, parsing and matching queries // Package labels implements a simple label system, parsing and matching
// with sets of labels. // selectors with sets of labels.
package labels package labels

View File

@ -30,18 +30,23 @@ type Labels interface {
type Set map[string]string type Set map[string]string
// All labels listed as a human readable string. Conveniently, exactly the format // All labels listed as a human readable string. Conveniently, exactly the format
// that ParseQuery takes. // that ParseSelector takes.
func (ls Set) String() string { func (ls Set) String() string {
query := make([]string, 0, len(ls)) selector := make([]string, 0, len(ls))
for key, value := range ls { for key, value := range ls {
query = append(query, key+"="+value) selector = append(selector, key+"="+value)
} }
// Sort for determinism. // Sort for determinism.
sort.StringSlice(query).Sort() sort.StringSlice(selector).Sort()
return strings.Join(query, ",") return strings.Join(selector, ",")
} }
// Implement Labels interface. // Implement Labels interface.
func (ls Set) Get(label string) string { func (ls Set) Get(label string) string {
return ls[label] return ls[label]
} }
// Convenience function: convert these labels to a selector.
func (ls Set) AsSelector() Selector {
return SelectorFromSet(ls)
}

View File

@ -21,17 +21,17 @@ import (
"strings" "strings"
) )
// Represents a label query. // Represents a selector.
type Query interface { type Selector interface {
// Returns true if this query matches the given set of labels. // Returns true if this selector matches the given set of labels.
Matches(Labels) bool Matches(Labels) bool
// Prints a human readable version of this label query. // Prints a human readable version of this selector.
String() string String() string
} }
// Everything returns a query that matches all labels. // Everything returns a selector that matches all labels.
func Everything() Query { func Everything() Selector {
return andTerm{} return andTerm{}
} }
@ -59,7 +59,7 @@ func (t *notHasTerm) String() string {
return fmt.Sprintf("%v!=%v", t.label, t.value) return fmt.Sprintf("%v!=%v", t.label, t.value)
} }
type andTerm []Query type andTerm []Selector
func (t andTerm) Matches(ls Labels) bool { func (t andTerm) Matches(ls Labels) bool {
for _, q := range t { for _, q := range t {
@ -78,17 +78,17 @@ func (t andTerm) String() string {
return strings.Join(terms, ",") return strings.Join(terms, ",")
} }
func try(queryPiece, op string) (lhs, rhs string, ok bool) { func try(selectorPiece, op string) (lhs, rhs string, ok bool) {
pieces := strings.Split(queryPiece, op) pieces := strings.Split(selectorPiece, op)
if len(pieces) == 2 { if len(pieces) == 2 {
return pieces[0], pieces[1], true return pieces[0], pieces[1], true
} }
return "", "", false return "", "", false
} }
// Given a Set, return a Query which will match exactly that Set. // Given a Set, return a Selector which will match exactly that Set.
func QueryFromSet(ls Set) Query { func SelectorFromSet(ls Set) Selector {
var items []Query var items []Selector
for label, value := range ls { for label, value := range ls {
items = append(items, &hasTerm{label: label, value: value}) items = append(items, &hasTerm{label: label, value: value})
} }
@ -98,10 +98,10 @@ func QueryFromSet(ls Set) Query {
return andTerm(items) return andTerm(items)
} }
// Takes a string repsenting a label query and returns an object suitable for matching, or an error. // Takes a string repsenting a selector and returns an object suitable for matching, or an error.
func ParseQuery(query string) (Query, error) { func ParseSelector(selector string) (Selector, error) {
parts := strings.Split(query, ",") parts := strings.Split(selector, ",")
var items []Query var items []Selector
for _, part := range parts { for _, part := range parts {
if part == "" { if part == "" {
continue continue
@ -113,7 +113,7 @@ func ParseQuery(query string) (Query, error) {
} else if lhs, rhs, ok := try(part, "="); ok { } else if lhs, rhs, ok := try(part, "="); ok {
items = append(items, &hasTerm{label: lhs, value: rhs}) items = append(items, &hasTerm{label: lhs, value: rhs})
} else { } else {
return nil, fmt.Errorf("invalid label query: '%s'; can't understand '%s'", query, part) return nil, fmt.Errorf("invalid selector: '%s'; can't understand '%s'", selector, part)
} }
} }
if len(items) == 1 { if len(items) == 1 {

View File

@ -20,7 +20,7 @@ import (
"testing" "testing"
) )
func TestQueryParse(t *testing.T) { func TestSelectorParse(t *testing.T) {
testGoodStrings := []string{ testGoodStrings := []string{
"x=a,y=b,z=c", "x=a,y=b,z=c",
"", "",
@ -31,7 +31,7 @@ func TestQueryParse(t *testing.T) {
"x==a==b", "x==a==b",
} }
for _, test := range testGoodStrings { for _, test := range testGoodStrings {
lq, err := ParseQuery(test) lq, err := ParseSelector(test)
if err != nil { if err != nil {
t.Errorf("%v: error %v (%#v)\n", test, err, err) t.Errorf("%v: error %v (%#v)\n", test, err, err)
} }
@ -40,42 +40,42 @@ func TestQueryParse(t *testing.T) {
} }
} }
for _, test := range testBadStrings { for _, test := range testBadStrings {
_, err := ParseQuery(test) _, err := ParseSelector(test)
if err == nil { if err == nil {
t.Errorf("%v: did not get expected error\n", test) t.Errorf("%v: did not get expected error\n", test)
} }
} }
} }
func expectMatch(t *testing.T, query string, ls Set) { func expectMatch(t *testing.T, selector string, ls Set) {
lq, err := ParseQuery(query) lq, err := ParseSelector(selector)
if err != nil { if err != nil {
t.Errorf("Unable to parse %v as a query\n", query) t.Errorf("Unable to parse %v as a selector\n", selector)
return return
} }
if !lq.Matches(ls) { if !lq.Matches(ls) {
t.Errorf("Wanted %s to match '%s', but it did not.\n", query, ls) t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls)
} }
} }
func expectNoMatch(t *testing.T, query string, ls Set) { func expectNoMatch(t *testing.T, selector string, ls Set) {
lq, err := ParseQuery(query) lq, err := ParseSelector(selector)
if err != nil { if err != nil {
t.Errorf("Unable to parse %v as a query\n", query) t.Errorf("Unable to parse %v as a selector\n", selector)
return return
} }
if lq.Matches(ls) { if lq.Matches(ls) {
t.Errorf("Wanted '%s' to not match '%s', but it did.", query, ls) t.Errorf("Wanted '%s' to not match '%s', but it did.", selector, ls)
} }
} }
func TestEverything(t *testing.T) { func TestEverything(t *testing.T) {
if !Everything().Matches(Set{"x": "y"}) { if !Everything().Matches(Set{"x": "y"}) {
t.Errorf("Nil query didn't match") t.Errorf("Nil selector didn't match")
} }
} }
func TestLabelQueryMatches(t *testing.T) { func TestSelectorMatches(t *testing.T) {
expectMatch(t, "", Set{"x": "y"}) expectMatch(t, "", Set{"x": "y"})
expectMatch(t, "x=y", Set{"x": "y"}) expectMatch(t, "x=y", Set{"x": "y"})
expectMatch(t, "x=y,z=w", Set{"x": "y", "z": "w"}) expectMatch(t, "x=y,z=w", Set{"x": "y", "z": "w"})
@ -96,15 +96,15 @@ func TestLabelQueryMatches(t *testing.T) {
expectNoMatch(t, "foo=bar,foobar=bar,baz=blah", labelset) expectNoMatch(t, "foo=bar,foobar=bar,baz=blah", labelset)
} }
func expectMatchDirect(t *testing.T, query, ls Set) { func expectMatchDirect(t *testing.T, selector, ls Set) {
if !QueryFromSet(query).Matches(ls) { if !SelectorFromSet(selector).Matches(ls) {
t.Errorf("Wanted %s to match '%s', but it did not.\n", query, ls) t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls)
} }
} }
func expectNoMatchDirect(t *testing.T, query, ls Set) { func expectNoMatchDirect(t *testing.T, selector, ls Set) {
if QueryFromSet(query).Matches(ls) { if SelectorFromSet(selector).Matches(ls) {
t.Errorf("Wanted '%s' to not match '%s', but it did.", query, ls) t.Errorf("Wanted '%s' to not match '%s', but it did.", selector, ls)
} }
} }

View File

@ -35,12 +35,12 @@ func MakeControllerRegistryStorage(registry ControllerRegistry) apiserver.RESTSt
} }
} }
func (storage *ControllerRegistryStorage) List(query labels.Query) (interface{}, error) { func (storage *ControllerRegistryStorage) List(selector labels.Selector) (interface{}, error) {
result := api.ReplicationControllerList{JSONBase: api.JSONBase{Kind: "cluster#replicationControllerList"}} result := api.ReplicationControllerList{JSONBase: api.JSONBase{Kind: "cluster#replicationControllerList"}}
controllers, err := storage.registry.ListControllers() controllers, err := storage.registry.ListControllers()
if err == nil { if err == nil {
for _, controller := range controllers { for _, controller := range controllers {
if query.Matches(labels.Set(controller.Labels)) { if selector.Matches(labels.Set(controller.Labels)) {
result.Items = append(result.Items, controller) result.Items = append(result.Items, controller)
} }
} }

View File

@ -139,7 +139,7 @@ func TestControllerParsing(t *testing.T) {
}, },
DesiredState: api.ReplicationControllerState{ DesiredState: api.ReplicationControllerState{
Replicas: 2, Replicas: 2,
ReplicasInSet: map[string]string{ ReplicaSelector: map[string]string{
"name": "nginx", "name": "nginx",
}, },
PodTemplate: api.PodTemplate{ PodTemplate: api.PodTemplate{

View File

@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package registry package registry
import ( import (
@ -42,7 +43,7 @@ func (e *EndpointController) SyncServiceEndpoints() error {
} }
var resultErr error var resultErr error
for _, service := range services.Items { for _, service := range services.Items {
pods, err := e.podRegistry.ListPods(labels.QueryFromSet(labels.Set(service.Labels))) pods, err := e.podRegistry.ListPods(labels.Set(service.Selector).AsSelector())
if err != nil { if err != nil {
log.Printf("Error syncing service: %#v, skipping.", service) log.Printf("Error syncing service: %#v, skipping.", service)
resultErr = err resultErr = err

View File

@ -49,7 +49,7 @@ func TestSyncEndpointsItems(t *testing.T) {
list: api.ServiceList{ list: api.ServiceList{
Items: []api.Service{ Items: []api.Service{
{ {
Labels: map[string]string{ Selector: map[string]string{
"foo": "bar", "foo": "bar",
}, },
}, },
@ -92,7 +92,7 @@ func TestSyncEndpointsPodError(t *testing.T) {
list: api.ServiceList{ list: api.ServiceList{
Items: []api.Service{ Items: []api.Service{
{ {
Labels: map[string]string{ Selector: map[string]string{
"foo": "bar", "foo": "bar",
}, },
}, },

View File

@ -59,7 +59,7 @@ func (registry *EtcdRegistry) helper() *util.EtcdHelper {
return &util.EtcdHelper{registry.etcdClient} return &util.EtcdHelper{registry.etcdClient}
} }
func (registry *EtcdRegistry) ListPods(query labels.Query) ([]api.Pod, error) { func (registry *EtcdRegistry) ListPods(selector labels.Selector) ([]api.Pod, error) {
pods := []api.Pod{} pods := []api.Pod{}
for _, machine := range registry.machines { for _, machine := range registry.machines {
var machinePods []api.Pod var machinePods []api.Pod
@ -68,7 +68,7 @@ func (registry *EtcdRegistry) ListPods(query labels.Query) ([]api.Pod, error) {
return pods, err return pods, err
} }
for _, pod := range machinePods { for _, pod := range machinePods {
if query.Matches(labels.Set(pod.Labels)) { if selector.Matches(labels.Set(pod.Labels)) {
pod.CurrentState.Host = machine pod.CurrentState.Host = machine
pods = append(pods, pod) pods = append(pods, pod)
} }

View File

@ -604,16 +604,21 @@ func TestEtcdUpdateService(t *testing.T) {
fakeClient := util.MakeFakeEtcdClient(t) fakeClient := util.MakeFakeEtcdClient(t)
fakeClient.Set("/registry/services/specs/foo", util.MakeJSONString(api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0) fakeClient.Set("/registry/services/specs/foo", util.MakeJSONString(api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0)
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"}) registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
err := registry.UpdateService(api.Service{ testService := api.Service{
JSONBase: api.JSONBase{ID: "foo"}, JSONBase: api.JSONBase{ID: "foo"},
Labels: map[string]string{ Labels: map[string]string{
"baz": "bar", "baz": "bar",
}, },
}) Selector: map[string]string{
"baz": "bar",
},
}
err := registry.UpdateService(testService)
expectNoError(t, err) expectNoError(t, err)
svc, err := registry.GetService("foo") svc, err := registry.GetService("foo")
if svc.Labels["baz"] != "bar" { expectNoError(t, err)
t.Errorf("Unexpected service: %#v", svc) if !reflect.DeepEqual(*svc, testService) {
t.Errorf("Unexpected service: got %#v, wanted %#v", svc, testService)
} }
} }

View File

@ -22,8 +22,8 @@ import (
// PodRegistry is an interface implemented by things that know how to store Pod objects. // PodRegistry is an interface implemented by things that know how to store Pod objects.
type PodRegistry interface { type PodRegistry interface {
// ListPods obtains a list of pods that match query. // ListPods obtains a list of pods that match selector.
ListPods(query labels.Query) ([]api.Pod, error) ListPods(selector labels.Selector) ([]api.Pod, error)
// Get a specific pod // Get a specific pod
GetPod(podID string) (*api.Pod, error) GetPod(podID string) (*api.Pod, error)
// Create a pod based on a specification, schedule it onto a specific machine. // Create a pod based on a specification, schedule it onto a specific machine.

View File

@ -36,10 +36,10 @@ func MakeMemoryRegistry() *MemoryRegistry {
} }
} }
func (registry *MemoryRegistry) ListPods(query labels.Query) ([]api.Pod, error) { func (registry *MemoryRegistry) ListPods(selector labels.Selector) ([]api.Pod, error) {
result := []api.Pod{} result := []api.Pod{}
for _, value := range registry.podData { for _, value := range registry.podData {
if query.Matches(labels.Set(value.Labels)) { if selector.Matches(labels.Set(value.Labels)) {
result = append(result, value) result = append(result, value)
} }
} }

View File

@ -33,13 +33,13 @@ func MakeMockPodRegistry(pods []api.Pod) *MockPodRegistry {
} }
} }
func (registry *MockPodRegistry) ListPods(query labels.Query) ([]api.Pod, error) { func (registry *MockPodRegistry) ListPods(selector labels.Selector) ([]api.Pod, error) {
if registry.err != nil { if registry.err != nil {
return registry.pods, registry.err return registry.pods, registry.err
} }
var filtered []api.Pod var filtered []api.Pod
for _, pod := range registry.pods { for _, pod := range registry.pods {
if query.Matches(labels.Set(pod.Labels)) { if selector.Matches(labels.Set(pod.Labels)) {
filtered = append(filtered, pod) filtered = append(filtered, pod)
} }
} }

View File

@ -54,9 +54,9 @@ func MakePodRegistryStorage(registry PodRegistry, containerInfo client.Container
} }
} }
func (storage *PodRegistryStorage) List(query labels.Query) (interface{}, error) { func (storage *PodRegistryStorage) List(selector labels.Selector) (interface{}, error) {
var result api.PodList var result api.PodList
pods, err := storage.registry.ListPods(query) pods, err := storage.registry.ListPods(selector)
if err == nil { if err == nil {
result.Items = pods result.Items = pods
// Get cached info for the list currently. // Get cached info for the list currently.

View File

@ -59,7 +59,7 @@ func GetServiceEnvironmentVariables(registry ServiceRegistry, machine string) ([
return result, nil return result, nil
} }
func (sr *ServiceRegistryStorage) List(query labels.Query) (interface{}, error) { func (sr *ServiceRegistryStorage) List(selector labels.Selector) (interface{}, error) {
list, err := sr.registry.ListServices() list, err := sr.registry.ListServices()
if err != nil { if err != nil {
return nil, err return nil, err
@ -67,7 +67,7 @@ func (sr *ServiceRegistryStorage) List(query labels.Query) (interface{}, error)
list.Kind = "cluster#serviceList" list.Kind = "cluster#serviceList"
var filtered []api.Service var filtered []api.Service
for _, service := range list.Items { for _, service := range list.Items {
if query.Matches(labels.Set(service.Labels)) { if selector.Matches(labels.Set(service.Labels)) {
filtered = append(filtered, service) filtered = append(filtered, service)
} }
} }