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,
"description": "Number of pods desired in the set"
},
"replicasInSet": {
"replicaSelector": {
"type": "object",
"required": false,
"description": "Required labels used to identify pods in the set"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,4 +4,7 @@
"labels": {
"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">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">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>}</span>,
"<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">description</span>": <span class="hljs-value"><span class="hljs-string">"Number of pods desired in the set"</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">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>
@ -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">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">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">desiredState</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">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">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">desiredState</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">description</span>": <span class="hljs-value"><span class="hljs-string">"Number of pods desired in the set"</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">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>
@ -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">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">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">desiredState</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.")
httpServer = flag.String("h", "", "The host to connect to.")
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")
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'")
@ -143,8 +143,8 @@ func executeAPIRequest(method string, auth *kube_client.AuthInfo) bool {
switch method {
case "get", "list":
url := readUrl(parseStorage())
if len(*labelQuery) > 0 && method == "list" {
url = url + "?labels=" + *labelQuery
if len(*selector) > 0 && method == "list" {
url = url + "?labels=" + *selector
}
request, err = http.NewRequest("GET", url, nil)
case "delete":

View File

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

View File

@ -73,18 +73,20 @@ CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
### 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.
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
{
"id": "redismaster",
"port": 10000,
"labels": {
"selector": {
"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:
```shell
@ -106,7 +108,7 @@ Create a file named `redis-slave-controller.json` that contains:
"id": "redisSlaveController",
"desiredState": {
"replicas": 2,
"replicasInSet": {"name": "redis-slave"},
"replicaSelector": {"name": "redis-slave"},
"podTemplate": {
"desiredState": {
"manifest": {
@ -126,7 +128,7 @@ Then you can create the service by running:
```shell
$ 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
```
@ -160,11 +162,14 @@ Just like the master, we want to have a service to proxy connections to the read
"port": 10001,
"labels": {
"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:
@ -186,7 +191,7 @@ Create a file named `frontend-controller.json`:
"id": "frontendController",
"desiredState": {
"replicas": 3,
"replicasInSet": {"name": "frontend"},
"replicaSelector": {"name": "frontend"},
"podTemplate": {
"desiredState": {
"manifest": {
@ -206,7 +211,7 @@ With this file, you can turn up your frontend with:
```shell
$ 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
```

View File

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

View File

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

View File

@ -4,4 +4,7 @@
"labels": {
"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)
type ReplicationControllerState struct {
Replicas int `json:"replicas" yaml:"replicas"`
ReplicasInSet map[string]string `json:"replicasInSet,omitempty" yaml:"replicasInSet,omitempty"`
PodTemplate PodTemplate `json:"podTemplate,omitempty" yaml:"podTemplate,omitempty"`
Replicas int `json:"replicas" yaml:"replicas"`
ReplicaSelector map[string]string `json:"replicaSelector,omitempty" yaml:"replicaSelector,omitempty"`
PodTemplate PodTemplate `json:"podTemplate,omitempty" yaml:"podTemplate,omitempty"`
}
type ReplicationControllerList struct {
@ -138,11 +138,17 @@ type ServiceList struct {
}
// 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 {
JSONBase `json:",inline" yaml:",inline"`
Port int `json:"port,omitempty" yaml:"port,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
JSONBase `json:",inline" yaml:",inline"`
Port int `json:"port,omitempty" yaml:"port,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"`
}

View File

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

View File

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

View File

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

View File

@ -117,13 +117,13 @@ func TestListPodsLabels(t *testing.T) {
client := Client{
Host: testServer.URL,
}
query := map[string]string{"foo": "bar", "name": "baz"}
receivedPodList, err := client.ListPods(query)
selector := map[string]string{"foo": "bar", "name": "baz"}
receivedPodList, err := client.ListPods(selector)
fakeHandler.ValidateRequest(t, makeUrl("/pods"), "GET", nil)
queryString := fakeHandler.RequestReceived.URL.Query().Get("labels")
queryString, _ = url.QueryUnescape(queryString)
parsedQueryString := DecodeLabelQuery(queryString)
expectEqual(t, query, parsedQueryString)
selectorString := fakeHandler.RequestReceived.URL.Query().Get("labels")
selectorString, _ = url.QueryUnescape(selectorString)
parsedSelectorString := DecodeSelector(selectorString)
expectEqual(t, selector, parsedSelectorString)
if err != nil {
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 {
return err
}
labels := controller.DesiredState.ReplicasInSet
labels := controller.DesiredState.ReplicaSelector
podList, err := client.ListPods(labels)
if err != nil {
@ -200,7 +200,7 @@ func RunController(image, name string, replicas int, client client.ClientInterfa
},
DesiredState: api.ReplicationControllerState{
Replicas: replicas,
ReplicasInSet: map[string]string{
ReplicaSelector: map[string]string{
"name": name,
},
PodTemplate: api.PodTemplate{
@ -255,6 +255,9 @@ func createService(name string, port int, client client.ClientInterface) (api.Se
Labels: map[string]string{
"name": name,
},
Selector: map[string]string{
"name": name,
},
}
svc, err := client.CreateService(svc)
return svc, err

View File

@ -46,7 +46,7 @@ type FakeKubeClient struct {
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"})
return client.pods, nil
}

View File

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

View File

@ -24,6 +24,7 @@ import (
"text/tabwriter"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"gopkg.in/v1/yaml"
)
@ -61,8 +62,8 @@ func (y *YAMLPrinter) Print(data string, w io.Writer) error {
type HumanReadablePrinter struct{}
var podColumns = []string{"Name", "Image(s)", "Host", "Labels"}
var replicationControllerColumns = []string{"Name", "Image(s)", "Label Query", "Replicas"}
var serviceColumns = []string{"Name", "Label Query", "Port"}
var replicationControllerColumns = []string{"Name", "Image(s)", "Selector", "Replicas"}
var serviceColumns = []string{"Name", "Labels", "Selector", "Port"}
func (h *HumanReadablePrinter) unknown(data string, w io.Writer) error {
_, err := fmt.Fprintf(w, "Unknown object: %s", data)
@ -89,17 +90,9 @@ func (h *HumanReadablePrinter) makeImageList(manifest api.ContainerManifest) str
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 {
_, 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
}
@ -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 {
_, 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
}
@ -128,7 +121,7 @@ func (h *HumanReadablePrinter) printReplicationControllerList(list api.Replicati
}
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
}

View File

@ -177,7 +177,7 @@ func (rm *ReplicationManager) filterActivePods(pods []api.Pod) []api.Pod {
}
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 {
return err
}

View File

@ -68,7 +68,7 @@ func (s *KubeletServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
container := u.Query().Get("container")
if len(container) == 0 {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, "Missing container query arg.")
fmt.Fprint(w, "Missing container selector arg.")
return
}
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.
*/
// Package labels implements a simple label system, parsing and matching queries
// with sets of labels.
// Package labels implements a simple label system, parsing and matching
// selectors with sets of labels.
package labels

View File

@ -30,18 +30,23 @@ type Labels interface {
type Set map[string]string
// All labels listed as a human readable string. Conveniently, exactly the format
// that ParseQuery takes.
// that ParseSelector takes.
func (ls Set) String() string {
query := make([]string, 0, len(ls))
selector := make([]string, 0, len(ls))
for key, value := range ls {
query = append(query, key+"="+value)
selector = append(selector, key+"="+value)
}
// Sort for determinism.
sort.StringSlice(query).Sort()
return strings.Join(query, ",")
sort.StringSlice(selector).Sort()
return strings.Join(selector, ",")
}
// Implement Labels interface.
func (ls Set) Get(label string) string {
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"
)
// Represents a label query.
type Query interface {
// Returns true if this query matches the given set of labels.
// Represents a selector.
type Selector interface {
// Returns true if this selector matches the given set of labels.
Matches(Labels) bool
// Prints a human readable version of this label query.
// Prints a human readable version of this selector.
String() string
}
// Everything returns a query that matches all labels.
func Everything() Query {
// Everything returns a selector that matches all labels.
func Everything() Selector {
return andTerm{}
}
@ -59,7 +59,7 @@ func (t *notHasTerm) String() string {
return fmt.Sprintf("%v!=%v", t.label, t.value)
}
type andTerm []Query
type andTerm []Selector
func (t andTerm) Matches(ls Labels) bool {
for _, q := range t {
@ -78,17 +78,17 @@ func (t andTerm) String() string {
return strings.Join(terms, ",")
}
func try(queryPiece, op string) (lhs, rhs string, ok bool) {
pieces := strings.Split(queryPiece, op)
func try(selectorPiece, op string) (lhs, rhs string, ok bool) {
pieces := strings.Split(selectorPiece, op)
if len(pieces) == 2 {
return pieces[0], pieces[1], true
}
return "", "", false
}
// Given a Set, return a Query which will match exactly that Set.
func QueryFromSet(ls Set) Query {
var items []Query
// Given a Set, return a Selector which will match exactly that Set.
func SelectorFromSet(ls Set) Selector {
var items []Selector
for label, value := range ls {
items = append(items, &hasTerm{label: label, value: value})
}
@ -98,10 +98,10 @@ func QueryFromSet(ls Set) Query {
return andTerm(items)
}
// Takes a string repsenting a label query and returns an object suitable for matching, or an error.
func ParseQuery(query string) (Query, error) {
parts := strings.Split(query, ",")
var items []Query
// Takes a string repsenting a selector and returns an object suitable for matching, or an error.
func ParseSelector(selector string) (Selector, error) {
parts := strings.Split(selector, ",")
var items []Selector
for _, part := range parts {
if part == "" {
continue
@ -113,7 +113,7 @@ func ParseQuery(query string) (Query, error) {
} else if lhs, rhs, ok := try(part, "="); ok {
items = append(items, &hasTerm{label: lhs, value: rhs})
} 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 {

View File

@ -20,7 +20,7 @@ import (
"testing"
)
func TestQueryParse(t *testing.T) {
func TestSelectorParse(t *testing.T) {
testGoodStrings := []string{
"x=a,y=b,z=c",
"",
@ -31,7 +31,7 @@ func TestQueryParse(t *testing.T) {
"x==a==b",
}
for _, test := range testGoodStrings {
lq, err := ParseQuery(test)
lq, err := ParseSelector(test)
if err != nil {
t.Errorf("%v: error %v (%#v)\n", test, err, err)
}
@ -40,42 +40,42 @@ func TestQueryParse(t *testing.T) {
}
}
for _, test := range testBadStrings {
_, err := ParseQuery(test)
_, err := ParseSelector(test)
if err == nil {
t.Errorf("%v: did not get expected error\n", test)
}
}
}
func expectMatch(t *testing.T, query string, ls Set) {
lq, err := ParseQuery(query)
func expectMatch(t *testing.T, selector string, ls Set) {
lq, err := ParseSelector(selector)
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
}
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) {
lq, err := ParseQuery(query)
func expectNoMatch(t *testing.T, selector string, ls Set) {
lq, err := ParseSelector(selector)
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
}
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) {
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, "x=y", Set{"x": "y"})
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)
}
func expectMatchDirect(t *testing.T, query, ls Set) {
if !QueryFromSet(query).Matches(ls) {
t.Errorf("Wanted %s to match '%s', but it did not.\n", query, ls)
func expectMatchDirect(t *testing.T, selector, ls Set) {
if !SelectorFromSet(selector).Matches(ls) {
t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls)
}
}
func expectNoMatchDirect(t *testing.T, query, ls Set) {
if QueryFromSet(query).Matches(ls) {
t.Errorf("Wanted '%s' to not match '%s', but it did.", query, ls)
func expectNoMatchDirect(t *testing.T, selector, ls Set) {
if SelectorFromSet(selector).Matches(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"}}
controllers, err := storage.registry.ListControllers()
if err == nil {
for _, controller := range controllers {
if query.Matches(labels.Set(controller.Labels)) {
if selector.Matches(labels.Set(controller.Labels)) {
result.Items = append(result.Items, controller)
}
}

View File

@ -139,7 +139,7 @@ func TestControllerParsing(t *testing.T) {
},
DesiredState: api.ReplicationControllerState{
Replicas: 2,
ReplicasInSet: map[string]string{
ReplicaSelector: map[string]string{
"name": "nginx",
},
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
limitations under the License.
*/
package registry
import (
@ -42,7 +43,7 @@ func (e *EndpointController) SyncServiceEndpoints() error {
}
var resultErr error
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 {
log.Printf("Error syncing service: %#v, skipping.", service)
resultErr = err

View File

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

View File

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

View File

@ -604,16 +604,21 @@ func TestEtcdUpdateService(t *testing.T) {
fakeClient := util.MakeFakeEtcdClient(t)
fakeClient.Set("/registry/services/specs/foo", util.MakeJSONString(api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0)
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
err := registry.UpdateService(api.Service{
testService := api.Service{
JSONBase: api.JSONBase{ID: "foo"},
Labels: map[string]string{
"baz": "bar",
},
})
Selector: map[string]string{
"baz": "bar",
},
}
err := registry.UpdateService(testService)
expectNoError(t, err)
svc, err := registry.GetService("foo")
if svc.Labels["baz"] != "bar" {
t.Errorf("Unexpected service: %#v", svc)
expectNoError(t, err)
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.
type PodRegistry interface {
// ListPods obtains a list of pods that match query.
ListPods(query labels.Query) ([]api.Pod, error)
// ListPods obtains a list of pods that match selector.
ListPods(selector labels.Selector) ([]api.Pod, error)
// Get a specific pod
GetPod(podID string) (*api.Pod, error)
// 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{}
for _, value := range registry.podData {
if query.Matches(labels.Set(value.Labels)) {
if selector.Matches(labels.Set(value.Labels)) {
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 {
return registry.pods, registry.err
}
var filtered []api.Pod
for _, pod := range registry.pods {
if query.Matches(labels.Set(pod.Labels)) {
if selector.Matches(labels.Set(pod.Labels)) {
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
pods, err := storage.registry.ListPods(query)
pods, err := storage.registry.ListPods(selector)
if err == nil {
result.Items = pods
// Get cached info for the list currently.

View File

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