From 79b7976cbf5fd1e41875a320d9e3dd688942e96b Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Sun, 8 Jun 2014 21:42:09 -0700 Subject: [PATCH] Add pod registry. --- pkg/registry/pod_registry.go | 121 ++++++++++++++++++ pkg/registry/pod_registry_test.go | 204 ++++++++++++++++++++++++++++++ 2 files changed, 325 insertions(+) create mode 100644 pkg/registry/pod_registry.go create mode 100644 pkg/registry/pod_registry_test.go diff --git a/pkg/registry/pod_registry.go b/pkg/registry/pod_registry.go new file mode 100644 index 00000000000..60efe2551c3 --- /dev/null +++ b/pkg/registry/pod_registry.go @@ -0,0 +1,121 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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 ( + "encoding/json" + "fmt" + "net/url" + + . "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" + "github.com/GoogleCloudPlatform/kubernetes/pkg/client" +) + +// TaskRegistryStorage implements the RESTStorage interface in terms of a TaskRegistry +type TaskRegistryStorage struct { + registry PodRegistry + containerInfo client.ContainerInfo + scheduler Scheduler +} + +func MakeTaskRegistryStorage(registry PodRegistry, containerInfo client.ContainerInfo, scheduler Scheduler) apiserver.RESTStorage { + return &TaskRegistryStorage{ + registry: registry, + containerInfo: containerInfo, + scheduler: scheduler, + } +} + +// LabelMatch tests to see if a Task's labels map contains 'key' mapping to 'value' +func LabelMatch(task Pod, queryKey, queryValue string) bool { + for key, value := range task.Labels { + if queryKey == key && queryValue == value { + return true + } + } + return false +} + +// LabelMatch tests to see if a Task's labels map contains all key/value pairs in 'labelQuery' +func LabelsMatch(task Pod, labelQuery *map[string]string) bool { + if labelQuery == nil { + return true + } + for key, value := range *labelQuery { + if !LabelMatch(task, key, value) { + return false + } + } + return true +} + +func (storage *TaskRegistryStorage) List(url *url.URL) (interface{}, error) { + var result PodList + var query *map[string]string + if url != nil { + queryMap := client.DecodeLabelQuery(url.Query().Get("labels")) + query = &queryMap + } + tasks, err := storage.registry.ListTasks(query) + if err == nil { + result = PodList{ + Items: tasks, + } + } + result.Kind = "cluster#taskList" + return result, err +} + +func (storage *TaskRegistryStorage) Get(id string) (interface{}, error) { + task, err := storage.registry.GetTask(id) + if err != nil { + return task, err + } + info, err := storage.containerInfo.GetContainerInfo(task.CurrentState.Host, id) + if err != nil { + return task, err + } + task.CurrentState.Info = info + task.Kind = "cluster#task" + return task, err +} + +func (storage *TaskRegistryStorage) Delete(id string) error { + return storage.registry.DeleteTask(id) +} + +func (storage *TaskRegistryStorage) Extract(body string) (interface{}, error) { + task := Pod{} + err := json.Unmarshal([]byte(body), &task) + return task, err +} + +func (storage *TaskRegistryStorage) Create(task interface{}) error { + taskObj := task.(Pod) + if len(taskObj.ID) == 0 { + return fmt.Errorf("ID is unspecified: %#v", task) + } + machine, err := storage.scheduler.Schedule(taskObj) + if err != nil { + return err + } + return storage.registry.CreateTask(machine, taskObj) +} + +func (storage *TaskRegistryStorage) Update(task interface{}) error { + return storage.registry.UpdateTask(task.(Pod)) +} diff --git a/pkg/registry/pod_registry_test.go b/pkg/registry/pod_registry_test.go new file mode 100644 index 00000000000..819ae6df3b9 --- /dev/null +++ b/pkg/registry/pod_registry_test.go @@ -0,0 +1,204 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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 ( + "encoding/json" + "fmt" + "testing" + + . "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +type MockPodRegistry struct { + err error + pods []Pod +} + +func expectNoError(t *testing.T, err error) { + if err != nil { + t.Errorf("Unexpected error: %#v", err) + } +} + +func (registry *MockPodRegistry) ListTasks(*map[string]string) ([]Pod, error) { + return registry.pods, registry.err +} + +func (registry *MockPodRegistry) GetTask(podId string) (*Pod, error) { + return &Pod{}, registry.err +} + +func (registry *MockPodRegistry) CreateTask(machine string, pod Pod) error { + return registry.err +} + +func (registry *MockPodRegistry) UpdateTask(pod Pod) error { + return registry.err +} +func (registry *MockPodRegistry) DeleteTask(podId string) error { + return registry.err +} + +func TestListTasksError(t *testing.T) { + mockRegistry := MockPodRegistry{ + err: fmt.Errorf("Test Error"), + } + storage := TaskRegistryStorage{ + registry: &mockRegistry, + } + pods, err := storage.List(nil) + if err != mockRegistry.err { + t.Errorf("Expected %#v, Got %#v", mockRegistry.err, err) + } + if len(pods.(PodList).Items) != 0 { + t.Errorf("Unexpected non-zero pod list: %#v", pods) + } +} + +func TestListEmptyTaskList(t *testing.T) { + mockRegistry := MockPodRegistry{} + storage := TaskRegistryStorage{ + registry: &mockRegistry, + } + pods, err := storage.List(nil) + expectNoError(t, err) + if len(pods.(PodList).Items) != 0 { + t.Errorf("Unexpected non-zero pod list: %#v", pods) + } +} + +func TestListTaskList(t *testing.T) { + mockRegistry := MockPodRegistry{ + pods: []Pod{ + Pod{ + JSONBase: JSONBase{ + ID: "foo", + }, + }, + Pod{ + JSONBase: JSONBase{ + ID: "bar", + }, + }, + }, + } + storage := TaskRegistryStorage{ + registry: &mockRegistry, + } + podsObj, err := storage.List(nil) + pods := podsObj.(PodList) + expectNoError(t, err) + if len(pods.Items) != 2 { + t.Errorf("Unexpected pod list: %#v", pods) + } + if pods.Items[0].ID != "foo" { + t.Errorf("Unexpected pod: %#v", pods.Items[0]) + } + if pods.Items[1].ID != "bar" { + t.Errorf("Unexpected pod: %#v", pods.Items[1]) + } +} + +func TestExtractJson(t *testing.T) { + mockRegistry := MockPodRegistry{} + storage := TaskRegistryStorage{ + registry: &mockRegistry, + } + pod := Pod{ + JSONBase: JSONBase{ + ID: "foo", + }, + } + body, err := json.Marshal(pod) + expectNoError(t, err) + podOut, err := storage.Extract(string(body)) + expectNoError(t, err) + jsonOut, err := json.Marshal(podOut) + expectNoError(t, err) + if string(body) != string(jsonOut) { + t.Errorf("Expected %#v, found %#v", pod, podOut) + } +} + +func expectLabelMatch(t *testing.T, pod Pod, key, value string) { + if !LabelMatch(pod, key, value) { + t.Errorf("Unexpected match failure: %#v %s %s", pod, key, value) + } +} + +func expectNoLabelMatch(t *testing.T, pod Pod, key, value string) { + if LabelMatch(pod, key, value) { + t.Errorf("Unexpected match success: %#v %s %s", pod, key, value) + } +} + +func expectLabelsMatch(t *testing.T, pod Pod, query *map[string]string) { + if !LabelsMatch(pod, query) { + t.Errorf("Unexpected match failure: %#v %#v", pod, *query) + } +} + +func expectNoLabelsMatch(t *testing.T, pod Pod, query *map[string]string) { + if LabelsMatch(pod, query) { + t.Errorf("Unexpected match success: %#v %#v", pod, *query) + } +} + +func TestLabelMatch(t *testing.T) { + pod := Pod{ + Labels: map[string]string{ + "foo": "bar", + "baz": "blah", + }, + } + expectLabelMatch(t, pod, "foo", "bar") + expectLabelMatch(t, pod, "baz", "blah") + expectNoLabelMatch(t, pod, "foo", "blah") + expectNoLabelMatch(t, pod, "baz", "bar") +} + +func TestLabelsMatch(t *testing.T) { + pod := Pod{ + Labels: map[string]string{ + "foo": "bar", + "baz": "blah", + }, + } + expectLabelsMatch(t, pod, &map[string]string{}) + expectLabelsMatch(t, pod, &map[string]string{ + "foo": "bar", + }) + expectLabelsMatch(t, pod, &map[string]string{ + "baz": "blah", + }) + expectLabelsMatch(t, pod, &map[string]string{ + "foo": "bar", + "baz": "blah", + }) + expectNoLabelsMatch(t, pod, &map[string]string{ + "foo": "blah", + }) + expectNoLabelsMatch(t, pod, &map[string]string{ + "baz": "bar", + }) + expectNoLabelsMatch(t, pod, &map[string]string{ + "foo": "bar", + "foobar": "bar", + "baz": "blah", + }) + +}