Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resize verb #1

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions pkg/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ func (g *APIGroupVersion) InstallREST(container *restful.Container, root string,
redirectHandler := &RedirectHandler{g.handler.storage, g.handler.codec}
opHandler := &OperationHandler{g.handler.ops, g.handler.codec}

resizeHandler := &ResizeHandler{
canonicalPrefix: prefix + "/resize/",
codec: g.handler.codec,
storage: g.handler.storage,
ops: g.handler.ops,
timeout: g.handler.asyncOpWait,
}

// Create a new WebService for this APIGroupVersion at the specified path prefix
// TODO: Pass in more descriptive documentation
ws := new(restful.WebService)
Expand Down Expand Up @@ -233,6 +241,7 @@ func (g *APIGroupVersion) InstallREST(container *restful.Container, root string,
mux.Handle(prefix+"/redirect/", http.StripPrefix(prefix+"/redirect/", redirectHandler))
mux.Handle(prefix+"/operations", http.StripPrefix(prefix+"/operations", opHandler))
mux.Handle(prefix+"/operations/", http.StripPrefix(prefix+"/operations/", opHandler))
mux.Handle(prefix+"/resize/", http.StripPrefix(prefix+"/resize/", resizeHandler))

container.Add(ws)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/apiserver/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var specialVerbs = map[string]bool{
"proxy": true,
"redirect": true,
"watch": true,
"resize": true,
}

// KindFromRequest returns Kind if Kind can be extracted from the request. Otherwise, the empty string.
Expand Down
4 changes: 4 additions & 0 deletions pkg/apiserver/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ type RESTStorage interface {
Update(ctx api.Context, obj runtime.Object) (<-chan RESTResult, error)
}

type Resizable interface {
Resize(ctx api.Context, id string, size int) (<-chan RESTResult, error)
}

// RESTResult indicates the result of a REST transformation.
type RESTResult struct {
// The result of this operation. May be nil if the operation has no meaningful
Expand Down
98 changes: 98 additions & 0 deletions pkg/apiserver/resize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package apiserver

import (
"fmt"
"net/http"
"strconv"
"strings"
"time"

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)

type ResizeHandler struct {
canonicalPrefix string
codec runtime.Codec
storage map[string]RESTStorage
ops *Operations
timeout time.Duration
}

var supportedResizables = map[string]bool{
"replicationControllers": true,
}

const (
paramInc string = "inc"
paramNamespace string = "namespace"
urlDelimiter string = "/"
)

func (h *ResizeHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
pathParts := strings.SplitN(req.URL.Path, urlDelimiter, 3)

if len(pathParts) < 2 {
notFound(w, req)
return
}

resizableType := pathParts[0]
resizableName := pathParts[1]
ctx := h.contextFromRequest(req)
inc := req.URL.Query().Get(paramInc)

if !h.isSupportedType(resizableType) {
badRequest("The requested resource does not support resizing", w)
return
}

storage := h.storage[resizableType]
if storage == nil {
httplog.LogOf(req, w).Addf("'%v' has no storage object", resizableType)
notFound(w, req)
return
}

resizableStorage, ok := storage.(Resizable)

//shouldn't ever get here if the supported types are correct, but check anyway
if !ok {
badRequest("The requested resource does not support resizing", w)
return
}

i, err := strconv.ParseInt(inc, 10, 32)

if err != nil {
badRequest(fmt.Sprintf("Unable to convert %v to an integer", inc), w)
return
}

out, err := resizableStorage.Resize(ctx, resizableName, int(i))

if err != nil {
errorJSON(err, h.codec, w)
return
}

op := createOperation(h.ops, out, true, h.timeout, nil, 0)
finishReq(op, req, w, h.codec)
}

func (h *ResizeHandler) contextFromRequest(req *http.Request) api.Context {
namespace := req.URL.Query().Get(paramNamespace)
ctx := api.WithNamespaceDefaultIfNone(api.WithNamespace(api.NewDefaultContext(), namespace))
return ctx
}

func (h *ResizeHandler) isSupportedType(s string) bool {
_, ok := supportedResizables[s]
return ok
}

func badRequest(msg string, w http.ResponseWriter) {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(msg))
}
26 changes: 13 additions & 13 deletions pkg/apiserver/resthandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
errorJSON(err, h.codec, w)
return
}
op := h.createOperation(out, sync, timeout, curry(h.setSelfLinkAddName, req))
h.finishReq(op, req, w)
op := createOperation(h.ops, out, sync, timeout, curry(h.setSelfLinkAddName, req), h.asyncOpWait)
finishReq(op, req, w, h.codec)

case "DELETE":
if len(parts) != 2 {
Expand All @@ -223,8 +223,8 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
errorJSON(err, h.codec, w)
return
}
op := h.createOperation(out, sync, timeout, nil)
h.finishReq(op, req, w)
op := createOperation(h.ops, out, sync, timeout, nil, h.asyncOpWait)
finishReq(op, req, w, h.codec)

case "PUT":
if len(parts) != 2 {
Expand All @@ -247,28 +247,28 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
errorJSON(err, h.codec, w)
return
}
op := h.createOperation(out, sync, timeout, curry(h.setSelfLink, req))
h.finishReq(op, req, w)
op := createOperation(h.ops, out, sync, timeout, curry(h.setSelfLink, req), h.asyncOpWait)
finishReq(op, req, w, h.codec)

default:
notFound(w, req)
}
}

// createOperation creates an operation to process a channel response.
func (h *RESTHandler) createOperation(out <-chan RESTResult, sync bool, timeout time.Duration, onReceive func(RESTResult)) *Operation {
op := h.ops.NewOperation(out, onReceive)
func createOperation(ops *Operations, out <-chan RESTResult, sync bool, timeout time.Duration, onReceive func(RESTResult), asyncOpWait time.Duration) *Operation {
op := ops.NewOperation(out, onReceive)
if sync {
op.WaitFor(timeout)
} else if h.asyncOpWait != 0 {
op.WaitFor(h.asyncOpWait)
} else if asyncOpWait != 0 {
op.WaitFor(asyncOpWait)
}
return op
}

// finishReq finishes up a request, waiting until the operation finishes or, after a timeout, creating an
// Operation to receive the result and returning its ID down the writer.
func (h *RESTHandler) finishReq(op *Operation, req *http.Request, w http.ResponseWriter) {
func finishReq(op *Operation, req *http.Request, w http.ResponseWriter, codec runtime.Codec) {
result, complete := op.StatusOrResult()
obj := result.Object
if complete {
Expand All @@ -282,8 +282,8 @@ func (h *RESTHandler) finishReq(op *Operation, req *http.Request, w http.Respons
status = stat.Code
}
}
writeJSON(status, h.codec, obj, w)
writeJSON(status, codec, obj, w)
} else {
writeJSON(http.StatusAccepted, h.codec, obj, w)
writeJSON(http.StatusAccepted, codec, obj, w)
}
}
9 changes: 9 additions & 0 deletions pkg/registry/controller/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ func (rs *REST) Get(ctx api.Context, id string) (runtime.Object, error) {
return controller, err
}

func (rs *REST) Resize(ctx api.Context, id string, size int) (<-chan apiserver.RESTResult, error) {
controller, err := rs.registry.GetController(ctx, id)
if err != nil {
return nil, err
}
controller.Spec.Replicas = controller.Spec.Replicas + size
return rs.Update(ctx, controller)
}

// List obtains a list of ReplicationControllers that match selector.
func (rs *REST) List(ctx api.Context, label, field labels.Selector) (runtime.Object, error) {
if !field.Empty() {
Expand Down