提交 e153a8ce 编写于 作者: P Piotr Bryk

Merge pull request #731 from bryk/generic-delete-backend

Backend for generic delete code
......@@ -52,6 +52,7 @@ type ApiHandler struct {
client *client.Client
heapsterClient HeapsterClient
clientConfig clientcmd.ClientConfig
verber common.ResourceVerber
}
// Web-service filter function used for request and response logging.
......@@ -83,7 +84,8 @@ func FormatResponseLog(resp *restful.Response, req *restful.Request) string {
func CreateHttpApiHandler(client *client.Client, heapsterClient HeapsterClient,
clientConfig clientcmd.ClientConfig) http.Handler {
apiHandler := ApiHandler{client, heapsterClient, clientConfig}
verber := common.NewResourceVerber(client.RESTClient)
apiHandler := ApiHandler{client, heapsterClient, clientConfig, verber}
wsContainer := restful.NewContainer()
deployWs := new(restful.WebService)
......@@ -270,6 +272,16 @@ func CreateHttpApiHandler(client *client.Client, heapsterClient HeapsterClient,
Writes(resourceService.ServiceDetail{}))
wsContainer.Add(servicesWs)
resourceVerberWs := new(restful.WebService)
resourceVerberWs.Filter(wsLogger)
resourceVerberWs.Path("/api/v1").
Consumes(restful.MIME_JSON).
Produces(restful.MIME_JSON)
resourceVerberWs.Route(
resourceVerberWs.DELETE("/{kind}/namespace/{namespace}/name/{name}").
To(apiHandler.handleDeleteResource))
wsContainer.Add(resourceVerberWs)
return wsContainer
}
......@@ -527,6 +539,20 @@ func (apiHandler *ApiHandler) handleDeleteReplicationController(
response.WriteHeader(http.StatusOK)
}
func (apiHandler *ApiHandler) handleDeleteResource(
request *restful.Request, response *restful.Response) {
kind := request.PathParameter("kind")
namespace := request.PathParameter("namespace")
name := request.PathParameter("name")
if err := apiHandler.verber.Delete(kind, namespace, name); err != nil {
handleInternalError(response, err)
return
}
response.WriteHeader(http.StatusOK)
}
// Handles get Replication Controller Pods API call.
func (apiHandler *ApiHandler) handleGetReplicationControllerPods(
request *restful.Request, response *restful.Response) {
......
......@@ -21,28 +21,7 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned"
)
type ResourceType string
const (
ResourceTypeReplicaSet = "replicaset"
ResourceTypeReplicationController = "replicationcontroller"
)
func CreateObjectMeta(k8SObjectMeta api.ObjectMeta) ObjectMeta {
return ObjectMeta{
Name: k8SObjectMeta.Name,
Namespace: k8SObjectMeta.Namespace,
Labels: k8SObjectMeta.Labels,
CreationTimestamp: k8SObjectMeta.CreationTimestamp,
}
}
func CreateTypeMeta(k8STypeMeta unversioned.TypeMeta) TypeMeta {
return TypeMeta{
Kind: k8STypeMeta.Kind,
}
}
// ObjectMeta is metadata about an instance of a resource.
type ObjectMeta struct {
// Name is unique within a namespace. Name is primarily intended for creation
// idempotence and configuration definition.
......@@ -83,7 +62,7 @@ type TypeMeta struct {
// Servers may infer this from the endpoint the client submits requests to.
// In smalllettercase.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
Kind string `json:"kind,omitempty"`
Kind ResourceKind `json:"kind,omitempty"`
}
// Endpoint describes an endpoint that is host and a list of available ports for that host.
......@@ -148,6 +127,49 @@ type Event struct {
}
// Returns internal endpoint name for the given service properties, e.g.,
// NewObjectMeta creates a new instance of ObjectMeta struct based on K8s object meta.
func NewObjectMeta(k8SObjectMeta api.ObjectMeta) ObjectMeta {
return ObjectMeta{
Name: k8SObjectMeta.Name,
Namespace: k8SObjectMeta.Namespace,
Labels: k8SObjectMeta.Labels,
CreationTimestamp: k8SObjectMeta.CreationTimestamp,
}
}
// NewTypeMeta creates new type mete for the resource kind.
func NewTypeMeta(kind ResourceKind) TypeMeta {
return TypeMeta{
Kind: kind,
}
}
// ResourceKind is an unique name for each resource. It can used for API discovery and generic
// code that does things based on the kind. For example, there may be a generic "deleter"
// that based on resource kind, name and namespace deletes it.
type ResourceKind string
// List of all resource kinds supported by the UI.
const (
ResourceKindReplicaSet = "replicaset"
ResourceKindService = "service"
ResourceKindDeployment = "deployment"
ResourceKindPod = "pod"
ResourceKindReplicationController = "replicationcontroller"
)
// Mapping from resource kind to K8s apiserver API path. This is mostly pluralization, because
// K8s apiserver uses plural paths and this project singular.
// Must be kept in sync with the list of supported kinds.
var kindToAPIPathMapping = map[string]string{
ResourceKindService: "services",
ResourceKindPod: "pods",
ResourceKindReplicationController: "replicationcontrollers",
ResourceKindDeployment: "deployments",
ResourceKindReplicaSet: "replicasets",
}
// GetInternalEndpoint returns internal endpoint name for the given service properties, e.g.,
// "my-service.namespace 80/TCP" or "my-service 53/TCP,53/UDP".
func GetInternalEndpoint(serviceName, namespace string, ports []api.ServicePort) Endpoint {
......@@ -165,7 +187,7 @@ func GetInternalEndpoint(serviceName, namespace string, ports []api.ServicePort)
}
}
// Gets human readable name for the given service ports list.
// GetServicePorts returns human readable name for the given service ports list.
func GetServicePorts(apiPorts []api.ServicePort) []ServicePort {
var ports []ServicePort
for _, port := range apiPorts {
......
// Copyright 2015 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 common
import (
"errors"
"fmt"
"k8s.io/kubernetes/pkg/client/restclient"
)
// ResourceVerber is a struct responsible for doing common verb operations on resources, like
// DELETE, PUT, UPDATE.
type ResourceVerber struct {
client RESTClient
}
type RESTClient interface {
Delete() *restclient.Request
}
// NewResourceVerber creates a new resource verber that uses the given client for performing
// operations.
func NewResourceVerber(client RESTClient) ResourceVerber {
return ResourceVerber{client}
}
// Delete deletes the resource of the given kind in the given namespace with the given name.
func (verber *ResourceVerber) Delete(kind string, namespace string, name string) error {
apiPath, ok := kindToAPIPathMapping[kind]
if !ok {
return errors.New(fmt.Sprintf("Unknown resource kind: %s", kind))
}
return verber.client.Delete().
Namespace(namespace).
Resource(apiPath).
Name(name).
Do().
Error()
}
......@@ -116,8 +116,8 @@ func getDeploymentList(deployments []extensions.Deployment,
deploymentList.Deployments = append(deploymentList.Deployments,
Deployment{
ObjectMeta: common.CreateObjectMeta(deployment.ObjectMeta),
TypeMeta: common.CreateTypeMeta(deployment.TypeMeta),
ObjectMeta: common.NewObjectMeta(deployment.ObjectMeta),
TypeMeta: common.NewTypeMeta(common.ResourceKindDeployment),
ContainerImages: replicationcontroller.GetContainerImages(&deployment.Spec.Template.Spec),
Pods: podInfo,
})
......
......@@ -92,8 +92,8 @@ func CreatePodList(pods []api.Pod, heapsterClient client.HeapsterClient) PodList
for _, pod := range pods {
podDetail := Pod{
ObjectMeta: common.CreateObjectMeta(pod.ObjectMeta),
TypeMeta: common.CreateTypeMeta(pod.TypeMeta),
ObjectMeta: common.NewObjectMeta(pod.ObjectMeta),
TypeMeta: common.NewTypeMeta(common.ResourceKindPod),
PodPhase: pod.Status.Phase,
PodIP: pod.Status.PodIP,
RestartCount: getRestartCount(pod),
......
......@@ -86,8 +86,8 @@ func getReplicaSetDetail(replicaSet *extensions.ReplicaSet, heapsterClient clien
podInfo := getPodInfo(replicaSet, matchingPods)
return ReplicaSetDetail{
ObjectMeta: common.CreateObjectMeta(replicaSet.ObjectMeta),
TypeMeta: common.CreateTypeMeta(replicaSet.TypeMeta),
ObjectMeta: common.NewObjectMeta(replicaSet.ObjectMeta),
TypeMeta: common.NewTypeMeta(common.ResourceKindReplicaSet),
ContainerImages: replicationcontroller.GetContainerImages(&replicaSet.Spec.Template.Spec),
PodInfo: podInfo,
PodList: pod.CreatePodList(matchingPods, heapsterClient),
......
......@@ -116,8 +116,8 @@ func getReplicaSetList(replicaSets []extensions.ReplicaSet,
replicaSetList.ReplicaSets = append(replicaSetList.ReplicaSets,
ReplicaSet{
ObjectMeta: common.CreateObjectMeta(replicaSet.ObjectMeta),
TypeMeta: common.CreateTypeMeta(replicaSet.TypeMeta),
ObjectMeta: common.NewObjectMeta(replicaSet.ObjectMeta),
TypeMeta: common.NewTypeMeta(common.ResourceKindReplicaSet),
ContainerImages: replicationcontroller.GetContainerImages(&replicaSet.Spec.Template.Spec),
Pods: podInfo,
})
......
......@@ -85,8 +85,8 @@ func GetReplicationControllerDetail(client k8sClient.Interface, heapsterClient c
}
replicationControllerDetail := &ReplicationControllerDetail{
ObjectMeta: common.CreateObjectMeta(replicationController.ObjectMeta),
TypeMeta: common.CreateTypeMeta(replicationController.TypeMeta),
ObjectMeta: common.NewObjectMeta(replicationController.ObjectMeta),
TypeMeta: common.NewTypeMeta(common.ResourceKindReplicationController),
LabelSelector: replicationController.Spec.Selector,
PodInfo: getReplicationPodInfo(replicationController, pods.Items),
ServiceList: resourceService.ServiceList{Services: make([]resourceService.Service, 0)},
......
......@@ -135,8 +135,8 @@ func getReplicationControllerList(replicationControllers []api.ReplicationContro
replicationControllerList.ReplicationControllers = append(replicationControllerList.ReplicationControllers,
ReplicationController{
ObjectMeta: common.CreateObjectMeta(replicationController.ObjectMeta),
TypeMeta: common.CreateTypeMeta(replicationController.TypeMeta),
ObjectMeta: common.NewObjectMeta(replicationController.ObjectMeta),
TypeMeta: common.NewTypeMeta(common.ResourceKindReplicationController),
Pods: podInfo,
ContainerImages: GetContainerImages(&replicationController.Spec.Template.Spec),
InternalEndpoints: internalEndpoints,
......
......@@ -23,8 +23,8 @@ import (
// ToService returns api service object based on kubernetes service object
func ToService(service *api.Service) Service {
return Service{
ObjectMeta: common.CreateObjectMeta(service.ObjectMeta),
TypeMeta: common.CreateTypeMeta(service.TypeMeta),
ObjectMeta: common.NewObjectMeta(service.ObjectMeta),
TypeMeta: common.NewTypeMeta(common.ResourceKindService),
InternalEndpoint: common.GetInternalEndpoint(service.Name, service.Namespace, service.Spec.Ports),
// TODO(maciaszczykm): Fill ExternalEndpoints with data.
Selector: service.Spec.Selector,
......@@ -35,8 +35,8 @@ func ToService(service *api.Service) Service {
// ToServiceDetails returns api service object based on kubernetes service object
func ToServiceDetail(service *api.Service) ServiceDetail {
return ServiceDetail{
ObjectMeta: common.CreateObjectMeta(service.ObjectMeta),
TypeMeta: common.CreateTypeMeta(service.TypeMeta),
ObjectMeta: common.NewObjectMeta(service.ObjectMeta),
TypeMeta: common.NewTypeMeta(common.ResourceKindService),
InternalEndpoint: common.GetInternalEndpoint(service.Name, service.Namespace, service.Spec.Ports),
// TODO(maciaszczykm): Fill ExternalEndpoints with data.
Selector: service.Spec.Selector,
......
package common
import (
"errors"
"net/http"
"reflect"
"testing"
"k8s.io/kubernetes/pkg/client/restclient"
)
type clientFunc func(req *http.Request) (*http.Response, error)
func (f clientFunc) Do(req *http.Request) (*http.Response, error) {
return f(req)
}
type FakeRESTClient struct {
response *http.Response
err error
}
func (c *FakeRESTClient) Delete() *restclient.Request {
return restclient.NewRequest(clientFunc(func(req *http.Request) (*http.Response, error) {
return c.response, c.err
}), "DELETE", nil, "/api/v1", restclient.ContentConfig{}, nil, nil)
}
func TestDeleteShouldPropagateErrors(t *testing.T) {
verber := ResourceVerber{client: &FakeRESTClient{err: errors.New("err")}}
err := verber.Delete("replicaset", "bar", "baz")
if !reflect.DeepEqual(err, errors.New("err")) {
t.Fatalf("Expected error on verber delete but got %#v", err)
}
}
func TestDeleteShouldThrowErrorOnUnknownResourceKind(t *testing.T) {
verber := ResourceVerber{client: &FakeRESTClient{}}
err := verber.Delete("foo", "bar", "baz")
if !reflect.DeepEqual(err, errors.New("Unknown resource kind: foo")) {
t.Fatalf("Expected error on verber delete but got %#v", err)
}
}
......@@ -122,6 +122,7 @@ func TestGetDeploymentListFromChannels(t *testing.T) {
Labels: map[string]string{"key": "value"},
CreationTimestamp: unversioned.Unix(111, 222),
},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindDeployment},
Pods: common.PodInfo{
Current: 7,
Desired: 21,
......
......@@ -59,6 +59,7 @@ func TestGetReplicaSetDetail(t *testing.T) {
},
&ReplicaSetDetail{
ObjectMeta: common.ObjectMeta{Name: "test-replicaset"},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindReplicaSet},
PodInfo: common.PodInfo{Warnings: []common.Event{}},
PodList: pod.PodList{Pods: []pod.Pod{}},
EventList: common.EventList{Events: []common.Event{}},
......
......@@ -122,6 +122,7 @@ func TestGetReplicaSetListFromChannels(t *testing.T) {
Labels: map[string]string{"key": "value"},
CreationTimestamp: unversioned.Unix(111, 222),
},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindReplicaSet},
Pods: common.PodInfo{
Current: 7,
Desired: 21,
......
......@@ -191,6 +191,7 @@ func TestGetReplicationControllerList(t *testing.T) {
Name: "my-app-1",
Namespace: "namespace-1",
},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindReplicationController},
ContainerImages: []string{"my-container-image-1"},
InternalEndpoints: []common.Endpoint{{Host: "my-app-1.namespace-1"}},
Pods: common.PodInfo{
......@@ -204,6 +205,7 @@ func TestGetReplicationControllerList(t *testing.T) {
Name: "my-app-2",
Namespace: "namespace-2",
},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindReplicationController},
ContainerImages: []string{"my-container-image-2"},
InternalEndpoints: []common.Endpoint{{Host: "my-app-2.namespace-2"}},
Pods: common.PodInfo{
......
......@@ -29,7 +29,9 @@ func TestToServiceDetail(t *testing.T) {
expected ServiceDetail
}{
{
service: &api.Service{}, expected: ServiceDetail{},
service: &api.Service{}, expected: ServiceDetail{
TypeMeta: common.TypeMeta{Kind: common.ResourceKindService},
},
}, {
service: &api.Service{
ObjectMeta: api.ObjectMeta{
......@@ -40,6 +42,7 @@ func TestToServiceDetail(t *testing.T) {
Name: "test-service",
Namespace: "test-namespace",
},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindService},
InternalEndpoint: common.Endpoint{Host: "test-service.test-namespace"},
},
},
......@@ -61,7 +64,9 @@ func TestToService(t *testing.T) {
expected Service
}{
{
service: &api.Service{}, expected: Service{},
service: &api.Service{}, expected: Service{
TypeMeta: common.TypeMeta{Kind: common.ResourceKindService},
},
}, {
service: &api.Service{
ObjectMeta: api.ObjectMeta{
......@@ -72,6 +77,7 @@ func TestToService(t *testing.T) {
Name: "test-service",
Namespace: "test-namespace",
},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindService},
InternalEndpoint: common.Endpoint{Host: "test-service.test-namespace"},
},
},
......
......@@ -35,7 +35,9 @@ func TestGetServiceDetail(t *testing.T) {
service: &api.Service{},
namespace: "test-namespace", name: "test-name",
expectedActions: []string{"get"},
expected: &ServiceDetail{},
expected: &ServiceDetail{
TypeMeta: common.TypeMeta{Kind: common.ResourceKindService},
},
}, {
service: &api.Service{ObjectMeta: api.ObjectMeta{
Name: "test-service", Namespace: "test-namespace",
......@@ -47,6 +49,7 @@ func TestGetServiceDetail(t *testing.T) {
Name: "test-service",
Namespace: "test-namespace",
},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindService},
InternalEndpoint: common.Endpoint{Host: "test-service.test-namespace"},
},
},
......
......@@ -18,10 +18,9 @@ import (
"reflect"
"testing"
"github.com/kubernetes/dashboard/resource/common"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
"github.com/kubernetes/dashboard/resource/common"
)
func TestGetServiceList(t *testing.T) {
......@@ -49,6 +48,7 @@ func TestGetServiceList(t *testing.T) {
Name: "test-service",
Namespace: "test-namespace",
},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindService},
InternalEndpoint: common.Endpoint{Host: "test-service.test-namespace"},
},
},
......@@ -76,7 +76,7 @@ func TestGetServiceList(t *testing.T) {
}
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("GetServiceList(client) == \ngot %#v, \nexpected %#v", actual, c.expected)
t.Errorf("GetServiceList(client) == got\n%#v, expected\n %#v", actual, c.expected)
}
}
}
......@@ -69,6 +69,7 @@ func TestGetWorkloadsFromChannels(t *testing.T) {
ObjectMeta: common.ObjectMeta{
Name: "rc-name",
},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindReplicationController},
Pods: common.PodInfo{
Warnings: []common.Event{},
},
......@@ -77,6 +78,7 @@ func TestGetWorkloadsFromChannels(t *testing.T) {
ObjectMeta: common.ObjectMeta{
Name: "rs-name",
},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindReplicaSet},
Pods: common.PodInfo{
Warnings: []common.Event{},
},
......@@ -85,6 +87,7 @@ func TestGetWorkloadsFromChannels(t *testing.T) {
ObjectMeta: common.ObjectMeta{
Name: "deployment-name",
},
TypeMeta: common.TypeMeta{Kind: common.ResourceKindDeployment},
Pods: common.PodInfo{
Warnings: []common.Event{},
},
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册