From d6936ff5609ab346e7086e631b251efb35f76f07 Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Wed, 26 Jul 2017 10:53:02 +0200 Subject: [PATCH 01/13] tap go-swagger before installing it --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 76f0e6b93e..b76ea09e1a 100644 --- a/Makefile +++ b/Makefile @@ -52,5 +52,6 @@ ifndef HAS_GLIDE brew install glide endif ifndef HAS_SWAGGER + brew tap go-swagger/go-swagger brew install go-swagger endif From f5d5ef2c5381d79c7ba9ab342dfce98523d725ac Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Wed, 26 Jul 2017 11:21:55 +0200 Subject: [PATCH 02/13] delete cluster specified by name --- pkg/api/handlers/delete_cluster.go | 30 +++++ pkg/api/rest/configure_kubernikus.go | 1 + pkg/api/rest/operations/delete_cluster.go | 73 +++++++++++ .../operations/delete_cluster_parameters.go | 76 ++++++++++++ .../operations/delete_cluster_responses.go | 115 ++++++++++++++++++ .../operations/delete_cluster_urlbuilder.go | 95 +++++++++++++++ swagger.yml | 18 +++ 7 files changed, 408 insertions(+) create mode 100644 pkg/api/handlers/delete_cluster.go create mode 100644 pkg/api/rest/operations/delete_cluster.go create mode 100644 pkg/api/rest/operations/delete_cluster_parameters.go create mode 100644 pkg/api/rest/operations/delete_cluster_responses.go create mode 100644 pkg/api/rest/operations/delete_cluster_urlbuilder.go diff --git a/pkg/api/handlers/delete_cluster.go b/pkg/api/handlers/delete_cluster.go new file mode 100644 index 0000000000..3d24fbd7fb --- /dev/null +++ b/pkg/api/handlers/delete_cluster.go @@ -0,0 +1,30 @@ +package handlers + +import ( + "github.com/go-openapi/runtime/middleware" + "github.com/sapcc/kubernikus/pkg/api" + "github.com/sapcc/kubernikus/pkg/api/models" + "github.com/sapcc/kubernikus/pkg/api/rest/operations" + apierrors "k8s.io/apimachinery/pkg/api/errors" + + tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" +) + +func NewDeleteCluster(rt *api.Runtime) operations.DeleteClusterHandler { + return &deleteCluster{rt: rt} +} + +type deleteCluster struct { + rt *api.Runtime +} + +func (d *deleteCluster) Handle(params operations.DeleteClusterParams, principal *models.Principal) middleware.Responder { + if err := d.rt.Clients.TPRClient().Delete().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(params.Name).Do().Error(); err != nil { + if apierrors.IsNotFound(err) { + return operations.NewDeleteClusterDefault(404).WithPayload(modelsError(err)) + } + return operations.NewDeleteClusterDefault(0).WithPayload(modelsError(err)) + } + + return operations.NewDeleteClusterOK() +} diff --git a/pkg/api/rest/configure_kubernikus.go b/pkg/api/rest/configure_kubernikus.go index 35aeecd077..cce310c6e2 100644 --- a/pkg/api/rest/configure_kubernikus.go +++ b/pkg/api/rest/configure_kubernikus.go @@ -52,6 +52,7 @@ func configureAPI(api *operations.KubernikusAPI) http.Handler { api.ListClustersHandler = handlers.NewListClusters(rt) api.CreateClusterHandler = handlers.NewCreateCluster(rt) api.ShowClusterHandler = handlers.NewShowCluster(rt) + api.DeleteClusterHandler = handlers.NewDeleteCluster(rt) api.ServerShutdown = func() {} diff --git a/pkg/api/rest/operations/delete_cluster.go b/pkg/api/rest/operations/delete_cluster.go new file mode 100644 index 0000000000..827f5f0d58 --- /dev/null +++ b/pkg/api/rest/operations/delete_cluster.go @@ -0,0 +1,73 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + middleware "github.com/go-openapi/runtime/middleware" + + "github.com/sapcc/kubernikus/pkg/api/models" +) + +// DeleteClusterHandlerFunc turns a function with the right signature into a delete cluster handler +type DeleteClusterHandlerFunc func(DeleteClusterParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn DeleteClusterHandlerFunc) Handle(params DeleteClusterParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// DeleteClusterHandler interface for that can handle valid delete cluster params +type DeleteClusterHandler interface { + Handle(DeleteClusterParams, *models.Principal) middleware.Responder +} + +// NewDeleteCluster creates a new http.Handler for the delete cluster operation +func NewDeleteCluster(ctx *middleware.Context, handler DeleteClusterHandler) *DeleteCluster { + return &DeleteCluster{Context: ctx, Handler: handler} +} + +/*DeleteCluster swagger:route DELETE /api/v1/clusters/{name} deleteCluster + +Delete the specified cluser + +*/ +type DeleteCluster struct { + Context *middleware.Context + Handler DeleteClusterHandler +} + +func (o *DeleteCluster) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + r = rCtx + } + var Params = NewDeleteClusterParams() + + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + r = aCtx + } + var principal *models.Principal + if uprinc != nil { + principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/pkg/api/rest/operations/delete_cluster_parameters.go b/pkg/api/rest/operations/delete_cluster_parameters.go new file mode 100644 index 0000000000..e22ba964bf --- /dev/null +++ b/pkg/api/rest/operations/delete_cluster_parameters.go @@ -0,0 +1,76 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" + + strfmt "github.com/go-openapi/strfmt" +) + +// NewDeleteClusterParams creates a new DeleteClusterParams object +// with the default values initialized. +func NewDeleteClusterParams() DeleteClusterParams { + var () + return DeleteClusterParams{} +} + +// DeleteClusterParams contains all the bound params for the delete cluster operation +// typically these are obtained from a http.Request +// +// swagger:parameters DeleteCluster +type DeleteClusterParams struct { + + // HTTP Request Object + HTTPRequest *http.Request + + /* + Required: true + Unique: true + In: path + */ + Name string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls +func (o *DeleteClusterParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + o.HTTPRequest = r + + rName, rhkName, _ := route.Params.GetOK("name") + if err := o.bindName(rName, rhkName, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *DeleteClusterParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + o.Name = raw + + if err := o.validateName(formats); err != nil { + return err + } + + return nil +} + +func (o *DeleteClusterParams) validateName(formats strfmt.Registry) error { + + return nil +} diff --git a/pkg/api/rest/operations/delete_cluster_responses.go b/pkg/api/rest/operations/delete_cluster_responses.go new file mode 100644 index 0000000000..da467aa30e --- /dev/null +++ b/pkg/api/rest/operations/delete_cluster_responses.go @@ -0,0 +1,115 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/sapcc/kubernikus/pkg/api/models" +) + +// DeleteClusterOKCode is the HTTP code returned for type DeleteClusterOK +const DeleteClusterOKCode int = 200 + +/*DeleteClusterOK OK + +swagger:response deleteClusterOK +*/ +type DeleteClusterOK struct { + + /* + In: Body + */ + Payload *models.Cluster `json:"body,omitempty"` +} + +// NewDeleteClusterOK creates DeleteClusterOK with default headers values +func NewDeleteClusterOK() *DeleteClusterOK { + return &DeleteClusterOK{} +} + +// WithPayload adds the payload to the delete cluster o k response +func (o *DeleteClusterOK) WithPayload(payload *models.Cluster) *DeleteClusterOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the delete cluster o k response +func (o *DeleteClusterOK) SetPayload(payload *models.Cluster) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *DeleteClusterOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +/*DeleteClusterDefault Error + +swagger:response deleteClusterDefault +*/ +type DeleteClusterDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewDeleteClusterDefault creates DeleteClusterDefault with default headers values +func NewDeleteClusterDefault(code int) *DeleteClusterDefault { + if code <= 0 { + code = 500 + } + + return &DeleteClusterDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the delete cluster default response +func (o *DeleteClusterDefault) WithStatusCode(code int) *DeleteClusterDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the delete cluster default response +func (o *DeleteClusterDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the delete cluster default response +func (o *DeleteClusterDefault) WithPayload(payload *models.Error) *DeleteClusterDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the delete cluster default response +func (o *DeleteClusterDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *DeleteClusterDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/pkg/api/rest/operations/delete_cluster_urlbuilder.go b/pkg/api/rest/operations/delete_cluster_urlbuilder.go new file mode 100644 index 0000000000..264e540d89 --- /dev/null +++ b/pkg/api/rest/operations/delete_cluster_urlbuilder.go @@ -0,0 +1,95 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// DeleteClusterURL generates an URL for the delete cluster operation +type DeleteClusterURL struct { + Name string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *DeleteClusterURL) WithBasePath(bp string) *DeleteClusterURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *DeleteClusterURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *DeleteClusterURL) Build() (*url.URL, error) { + var result url.URL + + var _path = "/api/v1/clusters/{name}" + + name := o.Name + if name != "" { + _path = strings.Replace(_path, "{name}", name, -1) + } else { + return nil, errors.New("Name is required on DeleteClusterURL") + } + _basePath := o._basePath + result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *DeleteClusterURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *DeleteClusterURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *DeleteClusterURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on DeleteClusterURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on DeleteClusterURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *DeleteClusterURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/swagger.yml b/swagger.yml index 35e4a274d2..4920cc25df 100644 --- a/swagger.yml +++ b/swagger.yml @@ -85,6 +85,24 @@ paths: name: name required: true in: path + delete: + operationId: DeleteCluster + summary: Delete the specified cluser + responses: + 200: + description: OK + schema: + $ref: '#/definitions/Cluster' + default: + $ref: "#/responses/errorResponse" + security: + - keystone: [] + parameters: + - uniqueItems: true + type: string + name: name + required: true + in: path definitions: ApiVersions: From c25d29eab2b33b3095b79cc7d06629013c74a757 Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Thu, 3 Aug 2017 13:53:31 +0200 Subject: [PATCH 03/13] swagger --- pkg/api/rest/embedded_spec.go | 20 ++++++++++++++++++++ pkg/api/rest/operations/kubernikus_api.go | 14 ++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/pkg/api/rest/embedded_spec.go b/pkg/api/rest/embedded_spec.go index eeb5144eb2..891c761ad7 100644 --- a/pkg/api/rest/embedded_spec.go +++ b/pkg/api/rest/embedded_spec.go @@ -119,6 +119,26 @@ func init() { } } }, + "delete": { + "summary": "Delete the specified cluser", + "operationId": "DeleteCluster", + "security": [ + { + "keystone": [] + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Cluster" + } + }, + "default": { + "$ref": "#/responses/errorResponse" + } + } + }, "parameters": [ { "uniqueItems": true, diff --git a/pkg/api/rest/operations/kubernikus_api.go b/pkg/api/rest/operations/kubernikus_api.go index d0fe940117..b6b9ff9090 100644 --- a/pkg/api/rest/operations/kubernikus_api.go +++ b/pkg/api/rest/operations/kubernikus_api.go @@ -40,6 +40,9 @@ func NewKubernikusAPI(spec *loads.Document) *KubernikusAPI { CreateClusterHandler: CreateClusterHandlerFunc(func(params CreateClusterParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation CreateCluster has not yet been implemented") }), + DeleteClusterHandler: DeleteClusterHandlerFunc(func(params DeleteClusterParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation DeleteCluster has not yet been implemented") + }), ListAPIVersionsHandler: ListAPIVersionsHandlerFunc(func(params ListAPIVersionsParams) middleware.Responder { return middleware.NotImplemented("operation ListAPIVersions has not yet been implemented") }), @@ -89,6 +92,8 @@ type KubernikusAPI struct { // CreateClusterHandler sets the operation handler for the create cluster operation CreateClusterHandler CreateClusterHandler + // DeleteClusterHandler sets the operation handler for the delete cluster operation + DeleteClusterHandler DeleteClusterHandler // ListAPIVersionsHandler sets the operation handler for the list API versions operation ListAPIVersionsHandler ListAPIVersionsHandler // ListClustersHandler sets the operation handler for the list clusters operation @@ -166,6 +171,10 @@ func (o *KubernikusAPI) Validate() error { unregistered = append(unregistered, "CreateClusterHandler") } + if o.DeleteClusterHandler == nil { + unregistered = append(unregistered, "DeleteClusterHandler") + } + if o.ListAPIVersionsHandler == nil { unregistered = append(unregistered, "ListAPIVersionsHandler") } @@ -278,6 +287,11 @@ func (o *KubernikusAPI) initHandlerCache() { } o.handlers["POST"]["/api/v1/clusters"] = NewCreateCluster(o.context, o.CreateClusterHandler) + if o.handlers["DELETE"] == nil { + o.handlers["DELETE"] = make(map[string]http.Handler) + } + o.handlers["DELETE"]["/api/v1/clusters/{name}"] = NewDeleteCluster(o.context, o.DeleteClusterHandler) + if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } From b46c103789f8ec8b7183a167b6168dd11684069f Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Thu, 3 Aug 2017 14:06:24 +0200 Subject: [PATCH 04/13] rename delete -> terminate --- ...delete_cluster.go => terminate_cluster.go} | 15 +-- pkg/api/rest/configure_kubernikus.go | 2 +- pkg/api/rest/embedded_spec.go | 4 +- .../operations/delete_cluster_responses.go | 115 ------------------ pkg/api/rest/operations/kubernikus_api.go | 28 ++--- ...delete_cluster.go => terminate_cluster.go} | 30 ++--- ...ers.go => terminate_cluster_parameters.go} | 18 +-- .../operations/terminate_cluster_responses.go | 115 ++++++++++++++++++ ...der.go => terminate_cluster_urlbuilder.go} | 24 ++-- pkg/controller/ground/controller.go | 4 +- pkg/tpr/v1/types.go | 10 +- swagger.yml | 4 +- 12 files changed, 186 insertions(+), 183 deletions(-) rename pkg/api/handlers/{delete_cluster.go => terminate_cluster.go} (56%) delete mode 100644 pkg/api/rest/operations/delete_cluster_responses.go rename pkg/api/rest/operations/{delete_cluster.go => terminate_cluster.go} (51%) rename pkg/api/rest/operations/{delete_cluster_parameters.go => terminate_cluster_parameters.go} (66%) create mode 100644 pkg/api/rest/operations/terminate_cluster_responses.go rename pkg/api/rest/operations/{delete_cluster_urlbuilder.go => terminate_cluster_urlbuilder.go} (68%) diff --git a/pkg/api/handlers/delete_cluster.go b/pkg/api/handlers/terminate_cluster.go similarity index 56% rename from pkg/api/handlers/delete_cluster.go rename to pkg/api/handlers/terminate_cluster.go index 3d24fbd7fb..0de0f12e2d 100644 --- a/pkg/api/handlers/delete_cluster.go +++ b/pkg/api/handlers/terminate_cluster.go @@ -10,21 +10,22 @@ import ( tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" ) -func NewDeleteCluster(rt *api.Runtime) operations.DeleteClusterHandler { - return &deleteCluster{rt: rt} +func NewTerminateCluster(rt *api.Runtime) operations.TerminateClusterHandler { + return &terminateCluster{rt: rt} } -type deleteCluster struct { +type terminateCluster struct { rt *api.Runtime } -func (d *deleteCluster) Handle(params operations.DeleteClusterParams, principal *models.Principal) middleware.Responder { +func (d *terminateCluster) Handle(params operations.TerminateClusterParams, principal *models.Principal) middleware.Responder { + if err := d.rt.Clients.TPRClient().Delete().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(params.Name).Do().Error(); err != nil { if apierrors.IsNotFound(err) { - return operations.NewDeleteClusterDefault(404).WithPayload(modelsError(err)) + return operations.NewTerminateClusterDefault(404).WithPayload(modelsError(err)) } - return operations.NewDeleteClusterDefault(0).WithPayload(modelsError(err)) + return operations.NewTerminateClusterDefault(0).WithPayload(modelsError(err)) } - return operations.NewDeleteClusterOK() + return operations.NewTerminateClusterOK() } diff --git a/pkg/api/rest/configure_kubernikus.go b/pkg/api/rest/configure_kubernikus.go index cce310c6e2..2e78f40f1f 100644 --- a/pkg/api/rest/configure_kubernikus.go +++ b/pkg/api/rest/configure_kubernikus.go @@ -52,7 +52,7 @@ func configureAPI(api *operations.KubernikusAPI) http.Handler { api.ListClustersHandler = handlers.NewListClusters(rt) api.CreateClusterHandler = handlers.NewCreateCluster(rt) api.ShowClusterHandler = handlers.NewShowCluster(rt) - api.DeleteClusterHandler = handlers.NewDeleteCluster(rt) + api.TerminateClusterHandler = handlers.NewTerminateCluster(rt) api.ServerShutdown = func() {} diff --git a/pkg/api/rest/embedded_spec.go b/pkg/api/rest/embedded_spec.go index 891c761ad7..29b210a64c 100644 --- a/pkg/api/rest/embedded_spec.go +++ b/pkg/api/rest/embedded_spec.go @@ -120,8 +120,8 @@ func init() { } }, "delete": { - "summary": "Delete the specified cluser", - "operationId": "DeleteCluster", + "summary": "Terminate the specified cluser", + "operationId": "TerminateCluster", "security": [ { "keystone": [] diff --git a/pkg/api/rest/operations/delete_cluster_responses.go b/pkg/api/rest/operations/delete_cluster_responses.go deleted file mode 100644 index da467aa30e..0000000000 --- a/pkg/api/rest/operations/delete_cluster_responses.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package operations - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - "net/http" - - "github.com/go-openapi/runtime" - - "github.com/sapcc/kubernikus/pkg/api/models" -) - -// DeleteClusterOKCode is the HTTP code returned for type DeleteClusterOK -const DeleteClusterOKCode int = 200 - -/*DeleteClusterOK OK - -swagger:response deleteClusterOK -*/ -type DeleteClusterOK struct { - - /* - In: Body - */ - Payload *models.Cluster `json:"body,omitempty"` -} - -// NewDeleteClusterOK creates DeleteClusterOK with default headers values -func NewDeleteClusterOK() *DeleteClusterOK { - return &DeleteClusterOK{} -} - -// WithPayload adds the payload to the delete cluster o k response -func (o *DeleteClusterOK) WithPayload(payload *models.Cluster) *DeleteClusterOK { - o.Payload = payload - return o -} - -// SetPayload sets the payload to the delete cluster o k response -func (o *DeleteClusterOK) SetPayload(payload *models.Cluster) { - o.Payload = payload -} - -// WriteResponse to the client -func (o *DeleteClusterOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { - - rw.WriteHeader(200) - if o.Payload != nil { - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this - } - } -} - -/*DeleteClusterDefault Error - -swagger:response deleteClusterDefault -*/ -type DeleteClusterDefault struct { - _statusCode int - - /* - In: Body - */ - Payload *models.Error `json:"body,omitempty"` -} - -// NewDeleteClusterDefault creates DeleteClusterDefault with default headers values -func NewDeleteClusterDefault(code int) *DeleteClusterDefault { - if code <= 0 { - code = 500 - } - - return &DeleteClusterDefault{ - _statusCode: code, - } -} - -// WithStatusCode adds the status to the delete cluster default response -func (o *DeleteClusterDefault) WithStatusCode(code int) *DeleteClusterDefault { - o._statusCode = code - return o -} - -// SetStatusCode sets the status to the delete cluster default response -func (o *DeleteClusterDefault) SetStatusCode(code int) { - o._statusCode = code -} - -// WithPayload adds the payload to the delete cluster default response -func (o *DeleteClusterDefault) WithPayload(payload *models.Error) *DeleteClusterDefault { - o.Payload = payload - return o -} - -// SetPayload sets the payload to the delete cluster default response -func (o *DeleteClusterDefault) SetPayload(payload *models.Error) { - o.Payload = payload -} - -// WriteResponse to the client -func (o *DeleteClusterDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { - - rw.WriteHeader(o._statusCode) - if o.Payload != nil { - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this - } - } -} diff --git a/pkg/api/rest/operations/kubernikus_api.go b/pkg/api/rest/operations/kubernikus_api.go index b6b9ff9090..ae8eee71d1 100644 --- a/pkg/api/rest/operations/kubernikus_api.go +++ b/pkg/api/rest/operations/kubernikus_api.go @@ -40,9 +40,6 @@ func NewKubernikusAPI(spec *loads.Document) *KubernikusAPI { CreateClusterHandler: CreateClusterHandlerFunc(func(params CreateClusterParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation CreateCluster has not yet been implemented") }), - DeleteClusterHandler: DeleteClusterHandlerFunc(func(params DeleteClusterParams, principal *models.Principal) middleware.Responder { - return middleware.NotImplemented("operation DeleteCluster has not yet been implemented") - }), ListAPIVersionsHandler: ListAPIVersionsHandlerFunc(func(params ListAPIVersionsParams) middleware.Responder { return middleware.NotImplemented("operation ListAPIVersions has not yet been implemented") }), @@ -52,6 +49,9 @@ func NewKubernikusAPI(spec *loads.Document) *KubernikusAPI { ShowClusterHandler: ShowClusterHandlerFunc(func(params ShowClusterParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation ShowCluster has not yet been implemented") }), + TerminateClusterHandler: TerminateClusterHandlerFunc(func(params TerminateClusterParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation TerminateCluster has not yet been implemented") + }), // Applies when the "x-auth-token" header is set KeystoneAuth: func(token string) (*models.Principal, error) { @@ -92,14 +92,14 @@ type KubernikusAPI struct { // CreateClusterHandler sets the operation handler for the create cluster operation CreateClusterHandler CreateClusterHandler - // DeleteClusterHandler sets the operation handler for the delete cluster operation - DeleteClusterHandler DeleteClusterHandler // ListAPIVersionsHandler sets the operation handler for the list API versions operation ListAPIVersionsHandler ListAPIVersionsHandler // ListClustersHandler sets the operation handler for the list clusters operation ListClustersHandler ListClustersHandler // ShowClusterHandler sets the operation handler for the show cluster operation ShowClusterHandler ShowClusterHandler + // TerminateClusterHandler sets the operation handler for the terminate cluster operation + TerminateClusterHandler TerminateClusterHandler // ServeError is called when an error is received, there is a default handler // but you can set your own with this @@ -171,10 +171,6 @@ func (o *KubernikusAPI) Validate() error { unregistered = append(unregistered, "CreateClusterHandler") } - if o.DeleteClusterHandler == nil { - unregistered = append(unregistered, "DeleteClusterHandler") - } - if o.ListAPIVersionsHandler == nil { unregistered = append(unregistered, "ListAPIVersionsHandler") } @@ -187,6 +183,10 @@ func (o *KubernikusAPI) Validate() error { unregistered = append(unregistered, "ShowClusterHandler") } + if o.TerminateClusterHandler == nil { + unregistered = append(unregistered, "TerminateClusterHandler") + } + if len(unregistered) > 0 { return fmt.Errorf("missing registration: %s", strings.Join(unregistered, ", ")) } @@ -287,11 +287,6 @@ func (o *KubernikusAPI) initHandlerCache() { } o.handlers["POST"]["/api/v1/clusters"] = NewCreateCluster(o.context, o.CreateClusterHandler) - if o.handlers["DELETE"] == nil { - o.handlers["DELETE"] = make(map[string]http.Handler) - } - o.handlers["DELETE"]["/api/v1/clusters/{name}"] = NewDeleteCluster(o.context, o.DeleteClusterHandler) - if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } @@ -307,6 +302,11 @@ func (o *KubernikusAPI) initHandlerCache() { } o.handlers["GET"]["/api/v1/clusters/{name}"] = NewShowCluster(o.context, o.ShowClusterHandler) + if o.handlers["DELETE"] == nil { + o.handlers["DELETE"] = make(map[string]http.Handler) + } + o.handlers["DELETE"]["/api/v1/clusters/{name}"] = NewTerminateCluster(o.context, o.TerminateClusterHandler) + } // Serve creates a http handler to serve the API over HTTP diff --git a/pkg/api/rest/operations/delete_cluster.go b/pkg/api/rest/operations/terminate_cluster.go similarity index 51% rename from pkg/api/rest/operations/delete_cluster.go rename to pkg/api/rest/operations/terminate_cluster.go index 827f5f0d58..866da2f19f 100644 --- a/pkg/api/rest/operations/delete_cluster.go +++ b/pkg/api/rest/operations/terminate_cluster.go @@ -13,40 +13,40 @@ import ( "github.com/sapcc/kubernikus/pkg/api/models" ) -// DeleteClusterHandlerFunc turns a function with the right signature into a delete cluster handler -type DeleteClusterHandlerFunc func(DeleteClusterParams, *models.Principal) middleware.Responder +// TerminateClusterHandlerFunc turns a function with the right signature into a terminate cluster handler +type TerminateClusterHandlerFunc func(TerminateClusterParams, *models.Principal) middleware.Responder // Handle executing the request and returning a response -func (fn DeleteClusterHandlerFunc) Handle(params DeleteClusterParams, principal *models.Principal) middleware.Responder { +func (fn TerminateClusterHandlerFunc) Handle(params TerminateClusterParams, principal *models.Principal) middleware.Responder { return fn(params, principal) } -// DeleteClusterHandler interface for that can handle valid delete cluster params -type DeleteClusterHandler interface { - Handle(DeleteClusterParams, *models.Principal) middleware.Responder +// TerminateClusterHandler interface for that can handle valid terminate cluster params +type TerminateClusterHandler interface { + Handle(TerminateClusterParams, *models.Principal) middleware.Responder } -// NewDeleteCluster creates a new http.Handler for the delete cluster operation -func NewDeleteCluster(ctx *middleware.Context, handler DeleteClusterHandler) *DeleteCluster { - return &DeleteCluster{Context: ctx, Handler: handler} +// NewTerminateCluster creates a new http.Handler for the terminate cluster operation +func NewTerminateCluster(ctx *middleware.Context, handler TerminateClusterHandler) *TerminateCluster { + return &TerminateCluster{Context: ctx, Handler: handler} } -/*DeleteCluster swagger:route DELETE /api/v1/clusters/{name} deleteCluster +/*TerminateCluster swagger:route DELETE /api/v1/clusters/{name} terminateCluster -Delete the specified cluser +Terminate the specified cluser */ -type DeleteCluster struct { +type TerminateCluster struct { Context *middleware.Context - Handler DeleteClusterHandler + Handler TerminateClusterHandler } -func (o *DeleteCluster) ServeHTTP(rw http.ResponseWriter, r *http.Request) { +func (o *TerminateCluster) ServeHTTP(rw http.ResponseWriter, r *http.Request) { route, rCtx, _ := o.Context.RouteInfo(r) if rCtx != nil { r = rCtx } - var Params = NewDeleteClusterParams() + var Params = NewTerminateClusterParams() uprinc, aCtx, err := o.Context.Authorize(r, route) if err != nil { diff --git a/pkg/api/rest/operations/delete_cluster_parameters.go b/pkg/api/rest/operations/terminate_cluster_parameters.go similarity index 66% rename from pkg/api/rest/operations/delete_cluster_parameters.go rename to pkg/api/rest/operations/terminate_cluster_parameters.go index e22ba964bf..82c3e5994e 100644 --- a/pkg/api/rest/operations/delete_cluster_parameters.go +++ b/pkg/api/rest/operations/terminate_cluster_parameters.go @@ -14,18 +14,18 @@ import ( strfmt "github.com/go-openapi/strfmt" ) -// NewDeleteClusterParams creates a new DeleteClusterParams object +// NewTerminateClusterParams creates a new TerminateClusterParams object // with the default values initialized. -func NewDeleteClusterParams() DeleteClusterParams { +func NewTerminateClusterParams() TerminateClusterParams { var () - return DeleteClusterParams{} + return TerminateClusterParams{} } -// DeleteClusterParams contains all the bound params for the delete cluster operation +// TerminateClusterParams contains all the bound params for the terminate cluster operation // typically these are obtained from a http.Request // -// swagger:parameters DeleteCluster -type DeleteClusterParams struct { +// swagger:parameters TerminateCluster +type TerminateClusterParams struct { // HTTP Request Object HTTPRequest *http.Request @@ -40,7 +40,7 @@ type DeleteClusterParams struct { // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls -func (o *DeleteClusterParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { +func (o *TerminateClusterParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error o.HTTPRequest = r @@ -55,7 +55,7 @@ func (o *DeleteClusterParams) BindRequest(r *http.Request, route *middleware.Mat return nil } -func (o *DeleteClusterParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { +func (o *TerminateClusterParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string if len(rawData) > 0 { raw = rawData[len(rawData)-1] @@ -70,7 +70,7 @@ func (o *DeleteClusterParams) bindName(rawData []string, hasKey bool, formats st return nil } -func (o *DeleteClusterParams) validateName(formats strfmt.Registry) error { +func (o *TerminateClusterParams) validateName(formats strfmt.Registry) error { return nil } diff --git a/pkg/api/rest/operations/terminate_cluster_responses.go b/pkg/api/rest/operations/terminate_cluster_responses.go new file mode 100644 index 0000000000..bb9581859d --- /dev/null +++ b/pkg/api/rest/operations/terminate_cluster_responses.go @@ -0,0 +1,115 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/sapcc/kubernikus/pkg/api/models" +) + +// TerminateClusterOKCode is the HTTP code returned for type TerminateClusterOK +const TerminateClusterOKCode int = 200 + +/*TerminateClusterOK OK + +swagger:response terminateClusterOK +*/ +type TerminateClusterOK struct { + + /* + In: Body + */ + Payload *models.Cluster `json:"body,omitempty"` +} + +// NewTerminateClusterOK creates TerminateClusterOK with default headers values +func NewTerminateClusterOK() *TerminateClusterOK { + return &TerminateClusterOK{} +} + +// WithPayload adds the payload to the terminate cluster o k response +func (o *TerminateClusterOK) WithPayload(payload *models.Cluster) *TerminateClusterOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the terminate cluster o k response +func (o *TerminateClusterOK) SetPayload(payload *models.Cluster) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *TerminateClusterOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +/*TerminateClusterDefault Error + +swagger:response terminateClusterDefault +*/ +type TerminateClusterDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewTerminateClusterDefault creates TerminateClusterDefault with default headers values +func NewTerminateClusterDefault(code int) *TerminateClusterDefault { + if code <= 0 { + code = 500 + } + + return &TerminateClusterDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the terminate cluster default response +func (o *TerminateClusterDefault) WithStatusCode(code int) *TerminateClusterDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the terminate cluster default response +func (o *TerminateClusterDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the terminate cluster default response +func (o *TerminateClusterDefault) WithPayload(payload *models.Error) *TerminateClusterDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the terminate cluster default response +func (o *TerminateClusterDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *TerminateClusterDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/pkg/api/rest/operations/delete_cluster_urlbuilder.go b/pkg/api/rest/operations/terminate_cluster_urlbuilder.go similarity index 68% rename from pkg/api/rest/operations/delete_cluster_urlbuilder.go rename to pkg/api/rest/operations/terminate_cluster_urlbuilder.go index 264e540d89..4789d5ecc4 100644 --- a/pkg/api/rest/operations/delete_cluster_urlbuilder.go +++ b/pkg/api/rest/operations/terminate_cluster_urlbuilder.go @@ -12,8 +12,8 @@ import ( "strings" ) -// DeleteClusterURL generates an URL for the delete cluster operation -type DeleteClusterURL struct { +// TerminateClusterURL generates an URL for the terminate cluster operation +type TerminateClusterURL struct { Name string _basePath string @@ -24,7 +24,7 @@ type DeleteClusterURL struct { // WithBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string -func (o *DeleteClusterURL) WithBasePath(bp string) *DeleteClusterURL { +func (o *TerminateClusterURL) WithBasePath(bp string) *TerminateClusterURL { o.SetBasePath(bp) return o } @@ -32,12 +32,12 @@ func (o *DeleteClusterURL) WithBasePath(bp string) *DeleteClusterURL { // SetBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string -func (o *DeleteClusterURL) SetBasePath(bp string) { +func (o *TerminateClusterURL) SetBasePath(bp string) { o._basePath = bp } // Build a url path and query string -func (o *DeleteClusterURL) Build() (*url.URL, error) { +func (o *TerminateClusterURL) Build() (*url.URL, error) { var result url.URL var _path = "/api/v1/clusters/{name}" @@ -46,7 +46,7 @@ func (o *DeleteClusterURL) Build() (*url.URL, error) { if name != "" { _path = strings.Replace(_path, "{name}", name, -1) } else { - return nil, errors.New("Name is required on DeleteClusterURL") + return nil, errors.New("Name is required on TerminateClusterURL") } _basePath := o._basePath result.Path = golangswaggerpaths.Join(_basePath, _path) @@ -55,7 +55,7 @@ func (o *DeleteClusterURL) Build() (*url.URL, error) { } // Must is a helper function to panic when the url builder returns an error -func (o *DeleteClusterURL) Must(u *url.URL, err error) *url.URL { +func (o *TerminateClusterURL) Must(u *url.URL, err error) *url.URL { if err != nil { panic(err) } @@ -66,17 +66,17 @@ func (o *DeleteClusterURL) Must(u *url.URL, err error) *url.URL { } // String returns the string representation of the path with query string -func (o *DeleteClusterURL) String() string { +func (o *TerminateClusterURL) String() string { return o.Must(o.Build()).String() } // BuildFull builds a full url with scheme, host, path and query string -func (o *DeleteClusterURL) BuildFull(scheme, host string) (*url.URL, error) { +func (o *TerminateClusterURL) BuildFull(scheme, host string) (*url.URL, error) { if scheme == "" { - return nil, errors.New("scheme is required for a full url on DeleteClusterURL") + return nil, errors.New("scheme is required for a full url on TerminateClusterURL") } if host == "" { - return nil, errors.New("host is required for a full url on DeleteClusterURL") + return nil, errors.New("host is required for a full url on TerminateClusterURL") } base, err := o.Build() @@ -90,6 +90,6 @@ func (o *DeleteClusterURL) BuildFull(scheme, host string) (*url.URL, error) { } // StringFull returns the string representation of a complete url -func (o *DeleteClusterURL) StringFull(scheme, host string) string { +func (o *TerminateClusterURL) StringFull(scheme, host string) string { return o.Must(o.BuildFull(scheme, host)).String() } diff --git a/pkg/controller/ground/controller.go b/pkg/controller/ground/controller.go index 5a0119176c..4e0f378008 100644 --- a/pkg/controller/ground/controller.go +++ b/pkg/controller/ground/controller.go @@ -85,7 +85,7 @@ func New(options Options) *Operator { tprInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: operator.klusterAdd, UpdateFunc: operator.klusterUpdate, - DeleteFunc: operator.klusterDelete, + DeleteFunc: operator.klusterTerminate, }) operator.tprInformer = tprInformer @@ -187,7 +187,7 @@ func (op *Operator) klusterAdd(obj interface{}) { op.queue.Add(key) } -func (op *Operator) klusterDelete(obj interface{}) { +func (op *Operator) klusterTerminate(obj interface{}) { c := obj.(*tprv1.Kluster) key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(c) if err != nil { diff --git a/pkg/tpr/v1/types.go b/pkg/tpr/v1/types.go index 98ef948411..7825b54d7d 100644 --- a/pkg/tpr/v1/types.go +++ b/pkg/tpr/v1/types.go @@ -24,10 +24,12 @@ type Kluster struct { type KlusterState string const ( - KlusterPending KlusterState = "Pending" - KlusterCreating KlusterState = "Creating" - KlusterCreated KlusterState = "Created" - KlusterError KlusterState = "Error" + KlusterPending KlusterState = "Pending" + KlusterCreating KlusterState = "Creating" + KlusterCreated KlusterState = "Created" + KlusterTerminating KlusterState = "Terminating" + KlusterTerminated KlusterState = "Terminated" + KlusterError KlusterState = "Error" ) type KlusterStatus struct { diff --git a/swagger.yml b/swagger.yml index 4920cc25df..00d91bfbec 100644 --- a/swagger.yml +++ b/swagger.yml @@ -86,8 +86,8 @@ paths: required: true in: path delete: - operationId: DeleteCluster - summary: Delete the specified cluser + operationId: TerminateCluster + summary: Terminate the specified cluser responses: 200: description: OK From 1e118dce99a765f5f2a315bdd52bdd5c033bbb81 Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Thu, 3 Aug 2017 15:52:08 +0200 Subject: [PATCH 05/13] patch kluster state --- pkg/api/handlers/terminate_cluster.go | 24 +++++++++++++++++++++--- pkg/api/handlers/util.go | 27 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/pkg/api/handlers/terminate_cluster.go b/pkg/api/handlers/terminate_cluster.go index 0de0f12e2d..0c740e9b9b 100644 --- a/pkg/api/handlers/terminate_cluster.go +++ b/pkg/api/handlers/terminate_cluster.go @@ -5,9 +5,10 @@ import ( "github.com/sapcc/kubernikus/pkg/api" "github.com/sapcc/kubernikus/pkg/api/models" "github.com/sapcc/kubernikus/pkg/api/rest/operations" - apierrors "k8s.io/apimachinery/pkg/api/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" + "github.com/golang/glog" ) func NewTerminateCluster(rt *api.Runtime) operations.TerminateClusterHandler { @@ -19,13 +20,30 @@ type terminateCluster struct { } func (d *terminateCluster) Handle(params operations.TerminateClusterParams, principal *models.Principal) middleware.Responder { - - if err := d.rt.Clients.TPRClient().Delete().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(params.Name).Do().Error(); err != nil { + var oldKluster *tprv1.Kluster + if err := d.rt.Clients.TPRClient().Get().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(params.Name).Do().Into(oldKluster); err != nil { if apierrors.IsNotFound(err) { return operations.NewTerminateClusterDefault(404).WithPayload(modelsError(err)) } return operations.NewTerminateClusterDefault(0).WithPayload(modelsError(err)) } + copy, err := d.rt.Clients.TPRScheme().Copy(oldKluster) + if err != nil { + + } + cpKluster := copy.(*tprv1.Kluster) + cpKluster.Status.State = tprv1.KlusterTerminating + + patchBytes, patchType, err := createPatch(oldKluster,cpKluster) + if err != nil { + return operations.NewTerminateClusterDefault(0).WithPayload(modelsError(err)) + } + + if err := d.rt.Clients.TPRClient().Patch(patchType).Body(patchBytes).Do().Error(); err != nil { + glog.Errorf("Failed to patch %s/%s: %s",oldKluster.GetNamespace(),oldKluster.GetName(),err) + return operations.NewTerminateClusterDefault(0).WithPayload(modelsError(err)) + } + return operations.NewTerminateClusterOK() } diff --git a/pkg/api/handlers/util.go b/pkg/api/handlers/util.go index 2467a698ad..17007d66bb 100644 --- a/pkg/api/handlers/util.go +++ b/pkg/api/handlers/util.go @@ -1,9 +1,15 @@ package handlers import ( + "encoding/json" + "github.com/go-openapi/swag" "github.com/sapcc/kubernikus/pkg/api/models" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + + tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" ) func modelsError(err error) *models.Error { @@ -15,3 +21,24 @@ func modelsError(err error) *models.Error { func accountSelector(principal *models.Principal) labels.Selector { return labels.SelectorFromSet(map[string]string{"account": principal.Account}) } + +func createPatch(old, new *tprv1.Kluster) (patchBytes []byte, patchType types.PatchType, err error) { + + oldData, err := json.Marshal(old) + if err != nil { + return nil, types.StrategicMergePatchType, err + } + + newData, err := json.Marshal(new) + if err != nil { + return nil, types.StrategicMergePatchType, err + } + + patchBytes, err = strategicpatch.CreateTwoWayMergePatch(oldData, newData, tprv1.Kluster{}) + if err != nil { + return nil, types.StrategicMergePatchType, err + } + + return patchBytes, types.StrategicMergePatchType, nil + +} From dfb25778acf743dec160eaae96db85c97ea33c7b Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Thu, 3 Aug 2017 16:48:14 +0200 Subject: [PATCH 06/13] qualify name: - --- pkg/api/handlers/show_cluster.go | 2 +- pkg/api/handlers/terminate_cluster.go | 8 ++++---- pkg/api/handlers/util.go | 10 ++++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pkg/api/handlers/show_cluster.go b/pkg/api/handlers/show_cluster.go index f86eb0d507..5db75fc04b 100644 --- a/pkg/api/handlers/show_cluster.go +++ b/pkg/api/handlers/show_cluster.go @@ -20,7 +20,7 @@ type showCluster struct { func (d *showCluster) Handle(params operations.ShowClusterParams, principal *models.Principal) middleware.Responder { var tprCluster tprv1.Kluster - if err := d.rt.Clients.TPRClient().Get().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(params.Name).Do().Into(&tprCluster); err != nil { + if err := d.rt.Clients.TPRClient().Get().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(qualifiedName(params.Name,principal.Account)).Do().Into(&tprCluster); err != nil { if apierrors.IsNotFound(err) { return operations.NewShowClusterDefault(404).WithPayload(modelsError(err)) } diff --git a/pkg/api/handlers/terminate_cluster.go b/pkg/api/handlers/terminate_cluster.go index 0c740e9b9b..2c3a236a44 100644 --- a/pkg/api/handlers/terminate_cluster.go +++ b/pkg/api/handlers/terminate_cluster.go @@ -20,22 +20,22 @@ type terminateCluster struct { } func (d *terminateCluster) Handle(params operations.TerminateClusterParams, principal *models.Principal) middleware.Responder { - var oldKluster *tprv1.Kluster - if err := d.rt.Clients.TPRClient().Get().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(params.Name).Do().Into(oldKluster); err != nil { + var oldKluster tprv1.Kluster + if err := d.rt.Clients.TPRClient().Get().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(qualifiedName(params.Name,principal.Account)).Do().Into(&oldKluster); err != nil { if apierrors.IsNotFound(err) { return operations.NewTerminateClusterDefault(404).WithPayload(modelsError(err)) } return operations.NewTerminateClusterDefault(0).WithPayload(modelsError(err)) } - copy, err := d.rt.Clients.TPRScheme().Copy(oldKluster) + copy, err := d.rt.Clients.TPRScheme().Copy(&oldKluster) if err != nil { } cpKluster := copy.(*tprv1.Kluster) cpKluster.Status.State = tprv1.KlusterTerminating - patchBytes, patchType, err := createPatch(oldKluster,cpKluster) + patchBytes, patchType, err := createPatch(&oldKluster,cpKluster) if err != nil { return operations.NewTerminateClusterDefault(0).WithPayload(modelsError(err)) } diff --git a/pkg/api/handlers/util.go b/pkg/api/handlers/util.go index 17007d66bb..c31c56356f 100644 --- a/pkg/api/handlers/util.go +++ b/pkg/api/handlers/util.go @@ -10,6 +10,8 @@ import ( "k8s.io/apimachinery/pkg/util/strategicpatch" tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" + "fmt" + "strings" ) func modelsError(err error) *models.Error { @@ -22,6 +24,14 @@ func accountSelector(principal *models.Principal) labels.Selector { return labels.SelectorFromSet(map[string]string{"account": principal.Account}) } +// qualifiedName returns - +func qualifiedName(name string, accountId string) string { + if strings.Contains(name,accountId) { + return name + } + return fmt.Sprintf("%s-%s",name,accountId) +} + func createPatch(old, new *tprv1.Kluster) (patchBytes []byte, patchType types.PatchType, err error) { oldData, err := json.Marshal(old) From 087c49cd8197bc0b316b1758db79878b56d14afd Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Thu, 3 Aug 2017 17:09:31 +0200 Subject: [PATCH 07/13] patch existing cluster --- pkg/api/handlers/patch_cluster.go | 59 +++++++++ pkg/api/rest/configure_kubernikus.go | 1 + pkg/api/rest/embedded_spec.go | 28 +++++ pkg/api/rest/operations/kubernikus_api.go | 14 +++ pkg/api/rest/operations/patch_cluster.go | 73 +++++++++++ .../operations/patch_cluster_parameters.go | 109 +++++++++++++++++ .../operations/patch_cluster_responses.go | 115 ++++++++++++++++++ .../operations/patch_cluster_urlbuilder.go | 95 +++++++++++++++ .../terminate_cluster_parameters.go | 33 +++++ swagger.yml | 23 ++++ 10 files changed, 550 insertions(+) create mode 100644 pkg/api/handlers/patch_cluster.go create mode 100644 pkg/api/rest/operations/patch_cluster.go create mode 100644 pkg/api/rest/operations/patch_cluster_parameters.go create mode 100644 pkg/api/rest/operations/patch_cluster_responses.go create mode 100644 pkg/api/rest/operations/patch_cluster_urlbuilder.go diff --git a/pkg/api/handlers/patch_cluster.go b/pkg/api/handlers/patch_cluster.go new file mode 100644 index 0000000000..77e0241082 --- /dev/null +++ b/pkg/api/handlers/patch_cluster.go @@ -0,0 +1,59 @@ +package handlers + +import ( + "github.com/go-openapi/runtime/middleware" + "github.com/sapcc/kubernikus/pkg/api" + "github.com/sapcc/kubernikus/pkg/api/models" + "github.com/sapcc/kubernikus/pkg/api/rest/operations" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/golang/glog" + "fmt" +) + +func NewPatchCluster(rt *api.Runtime) operations.PatchClusterHandler { + return &patchCluster{rt: rt} +} + +type patchCluster struct { + rt *api.Runtime +} + +func (d *patchCluster) Handle(params operations.PatchClusterParams, principal *models.Principal) middleware.Responder { + var oldKluster tprv1.Kluster + if err := d.rt.Clients.TPRClient().Get().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(qualifiedName(params.Name,principal.Account)).Do().Into(&oldKluster); err != nil { + if apierrors.IsNotFound(err) { + return operations.NewPatchClusterDefault(404).WithPayload(modelsError(err)) + } + return operations.NewPatchClusterDefault(0).WithPayload(modelsError(err)) + } + + kluster := &tprv1.Kluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s", params.Body.Name, principal.Account), + Labels: map[string]string{"account": principal.Account}, + Annotations: map[string]string{"creator": principal.Name}, + }, + Spec: tprv1.KlusterSpec{ + Name: params.Body.Name, + Account: principal.Account, + }, + Status: tprv1.KlusterStatus{ + State: oldKluster.Status.State, + }, + } + + patchBytes, patchType, err := createPatch(&oldKluster,kluster) + if err != nil { + return operations.NewPatchClusterDefault(0).WithPayload(modelsError(err)) + } + + if err := d.rt.Clients.TPRClient().Patch(patchType).Body(patchBytes).Do().Error(); err != nil { + glog.Errorf("Failed to patch %s/%s: %s",oldKluster.GetNamespace(),oldKluster.GetName(),err) + return operations.NewPatchClusterDefault(0).WithPayload(modelsError(err)) + } + + return operations.NewPatchClusterOK() +} diff --git a/pkg/api/rest/configure_kubernikus.go b/pkg/api/rest/configure_kubernikus.go index 2e78f40f1f..bb32cc3e89 100644 --- a/pkg/api/rest/configure_kubernikus.go +++ b/pkg/api/rest/configure_kubernikus.go @@ -53,6 +53,7 @@ func configureAPI(api *operations.KubernikusAPI) http.Handler { api.CreateClusterHandler = handlers.NewCreateCluster(rt) api.ShowClusterHandler = handlers.NewShowCluster(rt) api.TerminateClusterHandler = handlers.NewTerminateCluster(rt) + api.PatchClusterHandler = handlers.NewPatchCluster(rt) api.ServerShutdown = func() {} diff --git a/pkg/api/rest/embedded_spec.go b/pkg/api/rest/embedded_spec.go index 29b210a64c..72aed99353 100644 --- a/pkg/api/rest/embedded_spec.go +++ b/pkg/api/rest/embedded_spec.go @@ -139,6 +139,26 @@ func init() { } } }, + "patch": { + "summary": "Patch the specified cluser", + "operationId": "PatchCluster", + "security": [ + { + "keystone": [] + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Cluster" + } + }, + "default": { + "$ref": "#/responses/errorResponse" + } + } + }, "parameters": [ { "uniqueItems": true, @@ -146,6 +166,14 @@ func init() { "name": "name", "in": "path", "required": true + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/Cluster" + } } ] } diff --git a/pkg/api/rest/operations/kubernikus_api.go b/pkg/api/rest/operations/kubernikus_api.go index ae8eee71d1..d1ef15ccff 100644 --- a/pkg/api/rest/operations/kubernikus_api.go +++ b/pkg/api/rest/operations/kubernikus_api.go @@ -46,6 +46,9 @@ func NewKubernikusAPI(spec *loads.Document) *KubernikusAPI { ListClustersHandler: ListClustersHandlerFunc(func(params ListClustersParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation ListClusters has not yet been implemented") }), + PatchClusterHandler: PatchClusterHandlerFunc(func(params PatchClusterParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation PatchCluster has not yet been implemented") + }), ShowClusterHandler: ShowClusterHandlerFunc(func(params ShowClusterParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation ShowCluster has not yet been implemented") }), @@ -96,6 +99,8 @@ type KubernikusAPI struct { ListAPIVersionsHandler ListAPIVersionsHandler // ListClustersHandler sets the operation handler for the list clusters operation ListClustersHandler ListClustersHandler + // PatchClusterHandler sets the operation handler for the patch cluster operation + PatchClusterHandler PatchClusterHandler // ShowClusterHandler sets the operation handler for the show cluster operation ShowClusterHandler ShowClusterHandler // TerminateClusterHandler sets the operation handler for the terminate cluster operation @@ -179,6 +184,10 @@ func (o *KubernikusAPI) Validate() error { unregistered = append(unregistered, "ListClustersHandler") } + if o.PatchClusterHandler == nil { + unregistered = append(unregistered, "PatchClusterHandler") + } + if o.ShowClusterHandler == nil { unregistered = append(unregistered, "ShowClusterHandler") } @@ -297,6 +306,11 @@ func (o *KubernikusAPI) initHandlerCache() { } o.handlers["GET"]["/api/v1/clusters"] = NewListClusters(o.context, o.ListClustersHandler) + if o.handlers["PATCH"] == nil { + o.handlers["PATCH"] = make(map[string]http.Handler) + } + o.handlers["PATCH"]["/api/v1/clusters/{name}"] = NewPatchCluster(o.context, o.PatchClusterHandler) + if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } diff --git a/pkg/api/rest/operations/patch_cluster.go b/pkg/api/rest/operations/patch_cluster.go new file mode 100644 index 0000000000..17fed25606 --- /dev/null +++ b/pkg/api/rest/operations/patch_cluster.go @@ -0,0 +1,73 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + middleware "github.com/go-openapi/runtime/middleware" + + "github.com/sapcc/kubernikus/pkg/api/models" +) + +// PatchClusterHandlerFunc turns a function with the right signature into a patch cluster handler +type PatchClusterHandlerFunc func(PatchClusterParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn PatchClusterHandlerFunc) Handle(params PatchClusterParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// PatchClusterHandler interface for that can handle valid patch cluster params +type PatchClusterHandler interface { + Handle(PatchClusterParams, *models.Principal) middleware.Responder +} + +// NewPatchCluster creates a new http.Handler for the patch cluster operation +func NewPatchCluster(ctx *middleware.Context, handler PatchClusterHandler) *PatchCluster { + return &PatchCluster{Context: ctx, Handler: handler} +} + +/*PatchCluster swagger:route PATCH /api/v1/clusters/{name} patchCluster + +Patch the specified cluser + +*/ +type PatchCluster struct { + Context *middleware.Context + Handler PatchClusterHandler +} + +func (o *PatchCluster) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + r = rCtx + } + var Params = NewPatchClusterParams() + + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + r = aCtx + } + var principal *models.Principal + if uprinc != nil { + principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/pkg/api/rest/operations/patch_cluster_parameters.go b/pkg/api/rest/operations/patch_cluster_parameters.go new file mode 100644 index 0000000000..1ffef805ae --- /dev/null +++ b/pkg/api/rest/operations/patch_cluster_parameters.go @@ -0,0 +1,109 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "io" + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + + strfmt "github.com/go-openapi/strfmt" + + "github.com/sapcc/kubernikus/pkg/api/models" +) + +// NewPatchClusterParams creates a new PatchClusterParams object +// with the default values initialized. +func NewPatchClusterParams() PatchClusterParams { + var () + return PatchClusterParams{} +} + +// PatchClusterParams contains all the bound params for the patch cluster operation +// typically these are obtained from a http.Request +// +// swagger:parameters PatchCluster +type PatchClusterParams struct { + + // HTTP Request Object + HTTPRequest *http.Request + + /* + Required: true + In: body + */ + Body *models.Cluster + /* + Required: true + Unique: true + In: path + */ + Name string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls +func (o *PatchClusterParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.Cluster + if err := route.Consumer.Consume(r.Body, &body); err != nil { + if err == io.EOF { + res = append(res, errors.Required("body", "body")) + } else { + res = append(res, errors.NewParseError("body", "body", "", err)) + } + + } else { + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Body = &body + } + } + + } else { + res = append(res, errors.Required("body", "body")) + } + + rName, rhkName, _ := route.Params.GetOK("name") + if err := o.bindName(rName, rhkName, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *PatchClusterParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + o.Name = raw + + if err := o.validateName(formats); err != nil { + return err + } + + return nil +} + +func (o *PatchClusterParams) validateName(formats strfmt.Registry) error { + + return nil +} diff --git a/pkg/api/rest/operations/patch_cluster_responses.go b/pkg/api/rest/operations/patch_cluster_responses.go new file mode 100644 index 0000000000..01b85652b4 --- /dev/null +++ b/pkg/api/rest/operations/patch_cluster_responses.go @@ -0,0 +1,115 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/sapcc/kubernikus/pkg/api/models" +) + +// PatchClusterOKCode is the HTTP code returned for type PatchClusterOK +const PatchClusterOKCode int = 200 + +/*PatchClusterOK OK + +swagger:response patchClusterOK +*/ +type PatchClusterOK struct { + + /* + In: Body + */ + Payload *models.Cluster `json:"body,omitempty"` +} + +// NewPatchClusterOK creates PatchClusterOK with default headers values +func NewPatchClusterOK() *PatchClusterOK { + return &PatchClusterOK{} +} + +// WithPayload adds the payload to the patch cluster o k response +func (o *PatchClusterOK) WithPayload(payload *models.Cluster) *PatchClusterOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the patch cluster o k response +func (o *PatchClusterOK) SetPayload(payload *models.Cluster) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *PatchClusterOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +/*PatchClusterDefault Error + +swagger:response patchClusterDefault +*/ +type PatchClusterDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewPatchClusterDefault creates PatchClusterDefault with default headers values +func NewPatchClusterDefault(code int) *PatchClusterDefault { + if code <= 0 { + code = 500 + } + + return &PatchClusterDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the patch cluster default response +func (o *PatchClusterDefault) WithStatusCode(code int) *PatchClusterDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the patch cluster default response +func (o *PatchClusterDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the patch cluster default response +func (o *PatchClusterDefault) WithPayload(payload *models.Error) *PatchClusterDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the patch cluster default response +func (o *PatchClusterDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *PatchClusterDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/pkg/api/rest/operations/patch_cluster_urlbuilder.go b/pkg/api/rest/operations/patch_cluster_urlbuilder.go new file mode 100644 index 0000000000..36ef67172e --- /dev/null +++ b/pkg/api/rest/operations/patch_cluster_urlbuilder.go @@ -0,0 +1,95 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// PatchClusterURL generates an URL for the patch cluster operation +type PatchClusterURL struct { + Name string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *PatchClusterURL) WithBasePath(bp string) *PatchClusterURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *PatchClusterURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *PatchClusterURL) Build() (*url.URL, error) { + var result url.URL + + var _path = "/api/v1/clusters/{name}" + + name := o.Name + if name != "" { + _path = strings.Replace(_path, "{name}", name, -1) + } else { + return nil, errors.New("Name is required on PatchClusterURL") + } + _basePath := o._basePath + result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *PatchClusterURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *PatchClusterURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *PatchClusterURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on PatchClusterURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on PatchClusterURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *PatchClusterURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/pkg/api/rest/operations/terminate_cluster_parameters.go b/pkg/api/rest/operations/terminate_cluster_parameters.go index 82c3e5994e..90b9db6978 100644 --- a/pkg/api/rest/operations/terminate_cluster_parameters.go +++ b/pkg/api/rest/operations/terminate_cluster_parameters.go @@ -6,12 +6,16 @@ package operations // Editing this file might prove futile when you re-run the swagger generate command import ( + "io" "net/http" "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" strfmt "github.com/go-openapi/strfmt" + + "github.com/sapcc/kubernikus/pkg/api/models" ) // NewTerminateClusterParams creates a new TerminateClusterParams object @@ -30,6 +34,11 @@ type TerminateClusterParams struct { // HTTP Request Object HTTPRequest *http.Request + /* + Required: true + In: body + */ + Body *models.Cluster /* Required: true Unique: true @@ -44,6 +53,30 @@ func (o *TerminateClusterParams) BindRequest(r *http.Request, route *middleware. var res []error o.HTTPRequest = r + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.Cluster + if err := route.Consumer.Consume(r.Body, &body); err != nil { + if err == io.EOF { + res = append(res, errors.Required("body", "body")) + } else { + res = append(res, errors.NewParseError("body", "body", "", err)) + } + + } else { + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Body = &body + } + } + + } else { + res = append(res, errors.Required("body", "body")) + } + rName, rhkName, _ := route.Params.GetOK("name") if err := o.bindName(rName, rhkName, route.Formats); err != nil { res = append(res, err) diff --git a/swagger.yml b/swagger.yml index 00d91bfbec..94b7d4e54e 100644 --- a/swagger.yml +++ b/swagger.yml @@ -103,6 +103,29 @@ paths: name: name required: true in: path + patch: + operationId: PatchCluster + summary: Patch the specified cluser + responses: + 200: + description: OK + schema: + $ref: '#/definitions/Cluster' + default: + $ref: "#/responses/errorResponse" + security: + - keystone: [] + parameters: + - uniqueItems: true + type: string + name: name + required: true + in: path + - name: body + in: body + required: true + schema: + $ref: '#/definitions/Cluster' definitions: ApiVersions: From 2119647a4fead515a809292945ab42bcb610cb53 Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Thu, 3 Aug 2017 17:23:58 +0200 Subject: [PATCH 08/13] fix indent --- pkg/api/rest/embedded_spec.go | 25 +++++++++----- .../terminate_cluster_parameters.go | 33 ------------------- swagger.yml | 22 ++++++------- 3 files changed, 28 insertions(+), 52 deletions(-) diff --git a/pkg/api/rest/embedded_spec.go b/pkg/api/rest/embedded_spec.go index 72aed99353..dc0ab4acce 100644 --- a/pkg/api/rest/embedded_spec.go +++ b/pkg/api/rest/embedded_spec.go @@ -147,6 +147,23 @@ func init() { "keystone": [] } ], + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "name": "name", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/Cluster" + } + } + ], "responses": { "200": { "description": "OK", @@ -166,14 +183,6 @@ func init() { "name": "name", "in": "path", "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/Cluster" - } } ] } diff --git a/pkg/api/rest/operations/terminate_cluster_parameters.go b/pkg/api/rest/operations/terminate_cluster_parameters.go index 90b9db6978..82c3e5994e 100644 --- a/pkg/api/rest/operations/terminate_cluster_parameters.go +++ b/pkg/api/rest/operations/terminate_cluster_parameters.go @@ -6,16 +6,12 @@ package operations // Editing this file might prove futile when you re-run the swagger generate command import ( - "io" "net/http" "github.com/go-openapi/errors" - "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" strfmt "github.com/go-openapi/strfmt" - - "github.com/sapcc/kubernikus/pkg/api/models" ) // NewTerminateClusterParams creates a new TerminateClusterParams object @@ -34,11 +30,6 @@ type TerminateClusterParams struct { // HTTP Request Object HTTPRequest *http.Request - /* - Required: true - In: body - */ - Body *models.Cluster /* Required: true Unique: true @@ -53,30 +44,6 @@ func (o *TerminateClusterParams) BindRequest(r *http.Request, route *middleware. var res []error o.HTTPRequest = r - if runtime.HasBody(r) { - defer r.Body.Close() - var body models.Cluster - if err := route.Consumer.Consume(r.Body, &body); err != nil { - if err == io.EOF { - res = append(res, errors.Required("body", "body")) - } else { - res = append(res, errors.NewParseError("body", "body", "", err)) - } - - } else { - if err := body.Validate(route.Formats); err != nil { - res = append(res, err) - } - - if len(res) == 0 { - o.Body = &body - } - } - - } else { - res = append(res, errors.Required("body", "body")) - } - rName, rhkName, _ := route.Params.GetOK("name") if err := o.bindName(rName, rhkName, route.Formats); err != nil { res = append(res, err) diff --git a/swagger.yml b/swagger.yml index 94b7d4e54e..ac6f2dc7cd 100644 --- a/swagger.yml +++ b/swagger.yml @@ -115,17 +115,17 @@ paths: $ref: "#/responses/errorResponse" security: - keystone: [] - parameters: - - uniqueItems: true - type: string - name: name - required: true - in: path - - name: body - in: body - required: true - schema: - $ref: '#/definitions/Cluster' + parameters: + - uniqueItems: true + type: string + name: name + required: true + in: path + - name: body + in: body + required: true + schema: + $ref: '#/definitions/Cluster' definitions: ApiVersions: From 434bd3bd2001e7f7d40a674bb6406970a8b4749e Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Thu, 3 Aug 2017 17:39:07 +0200 Subject: [PATCH 09/13] fix patch call --- pkg/api/handlers/terminate_cluster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/api/handlers/terminate_cluster.go b/pkg/api/handlers/terminate_cluster.go index 2c3a236a44..0e5b59b220 100644 --- a/pkg/api/handlers/terminate_cluster.go +++ b/pkg/api/handlers/terminate_cluster.go @@ -40,7 +40,7 @@ func (d *terminateCluster) Handle(params operations.TerminateClusterParams, prin return operations.NewTerminateClusterDefault(0).WithPayload(modelsError(err)) } - if err := d.rt.Clients.TPRClient().Patch(patchType).Body(patchBytes).Do().Error(); err != nil { + if err := d.rt.Clients.TPRClient().Patch(patchType).Body(patchBytes).Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(qualifiedName(params.Name,principal.Account)).Do().Error(); err != nil { glog.Errorf("Failed to patch %s/%s: %s",oldKluster.GetNamespace(),oldKluster.GetName(),err) return operations.NewTerminateClusterDefault(0).WithPayload(modelsError(err)) } From 9fd815cec9508266a9edbfcca6b7034e31d6aa01 Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Thu, 3 Aug 2017 17:42:27 +0200 Subject: [PATCH 10/13] spaces instead of tabs in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b76ea09e1a..312cc2bfbe 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,6 @@ ifndef HAS_GLIDE brew install glide endif ifndef HAS_SWAGGER - brew tap go-swagger/go-swagger + brew tap go-swagger/go-swagger brew install go-swagger endif From e0cc85586fd9ea7b987567c5449e9dbafd3387d8 Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Fri, 4 Aug 2017 11:13:51 +0200 Subject: [PATCH 11/13] api: terminate and update via put --- pkg/api/handlers/patch_cluster.go | 59 --------- pkg/api/handlers/terminate_cluster.go | 29 ++--- pkg/api/handlers/update_cluster.go | 33 +++++ pkg/api/handlers/util.go | 33 ++--- pkg/api/rest/configure_kubernikus.go | 2 +- pkg/api/rest/embedded_spec.go | 46 +++---- pkg/api/rest/operations/kubernikus_api.go | 28 ++--- .../operations/patch_cluster_responses.go | 115 ------------------ .../{patch_cluster.go => update_cluster.go} | 30 ++--- ...meters.go => update_cluster_parameters.go} | 18 +-- .../operations/update_cluster_responses.go | 115 ++++++++++++++++++ ...uilder.go => update_cluster_urlbuilder.go} | 24 ++-- swagger.yml | 6 +- 13 files changed, 244 insertions(+), 294 deletions(-) delete mode 100644 pkg/api/handlers/patch_cluster.go create mode 100644 pkg/api/handlers/update_cluster.go delete mode 100644 pkg/api/rest/operations/patch_cluster_responses.go rename pkg/api/rest/operations/{patch_cluster.go => update_cluster.go} (53%) rename pkg/api/rest/operations/{patch_cluster_parameters.go => update_cluster_parameters.go} (76%) create mode 100644 pkg/api/rest/operations/update_cluster_responses.go rename pkg/api/rest/operations/{patch_cluster_urlbuilder.go => update_cluster_urlbuilder.go} (69%) diff --git a/pkg/api/handlers/patch_cluster.go b/pkg/api/handlers/patch_cluster.go deleted file mode 100644 index 77e0241082..0000000000 --- a/pkg/api/handlers/patch_cluster.go +++ /dev/null @@ -1,59 +0,0 @@ -package handlers - -import ( - "github.com/go-openapi/runtime/middleware" - "github.com/sapcc/kubernikus/pkg/api" - "github.com/sapcc/kubernikus/pkg/api/models" - "github.com/sapcc/kubernikus/pkg/api/rest/operations" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/golang/glog" - "fmt" -) - -func NewPatchCluster(rt *api.Runtime) operations.PatchClusterHandler { - return &patchCluster{rt: rt} -} - -type patchCluster struct { - rt *api.Runtime -} - -func (d *patchCluster) Handle(params operations.PatchClusterParams, principal *models.Principal) middleware.Responder { - var oldKluster tprv1.Kluster - if err := d.rt.Clients.TPRClient().Get().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(qualifiedName(params.Name,principal.Account)).Do().Into(&oldKluster); err != nil { - if apierrors.IsNotFound(err) { - return operations.NewPatchClusterDefault(404).WithPayload(modelsError(err)) - } - return operations.NewPatchClusterDefault(0).WithPayload(modelsError(err)) - } - - kluster := &tprv1.Kluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s", params.Body.Name, principal.Account), - Labels: map[string]string{"account": principal.Account}, - Annotations: map[string]string{"creator": principal.Name}, - }, - Spec: tprv1.KlusterSpec{ - Name: params.Body.Name, - Account: principal.Account, - }, - Status: tprv1.KlusterStatus{ - State: oldKluster.Status.State, - }, - } - - patchBytes, patchType, err := createPatch(&oldKluster,kluster) - if err != nil { - return operations.NewPatchClusterDefault(0).WithPayload(modelsError(err)) - } - - if err := d.rt.Clients.TPRClient().Patch(patchType).Body(patchBytes).Do().Error(); err != nil { - glog.Errorf("Failed to patch %s/%s: %s",oldKluster.GetNamespace(),oldKluster.GetName(),err) - return operations.NewPatchClusterDefault(0).WithPayload(modelsError(err)) - } - - return operations.NewPatchClusterOK() -} diff --git a/pkg/api/handlers/terminate_cluster.go b/pkg/api/handlers/terminate_cluster.go index 0e5b59b220..c323ce789d 100644 --- a/pkg/api/handlers/terminate_cluster.go +++ b/pkg/api/handlers/terminate_cluster.go @@ -6,9 +6,8 @@ import ( "github.com/sapcc/kubernikus/pkg/api/models" "github.com/sapcc/kubernikus/pkg/api/rest/operations" - apierrors "k8s.io/apimachinery/pkg/api/errors" tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" - "github.com/golang/glog" + apierrors "k8s.io/apimachinery/pkg/api/errors" ) func NewTerminateCluster(rt *api.Runtime) operations.TerminateClusterHandler { @@ -20,30 +19,16 @@ type terminateCluster struct { } func (d *terminateCluster) Handle(params operations.TerminateClusterParams, principal *models.Principal) middleware.Responder { - var oldKluster tprv1.Kluster - if err := d.rt.Clients.TPRClient().Get().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(qualifiedName(params.Name,principal.Account)).Do().Into(&oldKluster); err != nil { + + _,err := editCluster(d.rt.Clients.TPRClient(),principal,params.Name,func(kluster *tprv1.Kluster){ + kluster.Status.State = tprv1.KlusterTerminating + kluster.Status.Message = "Cluster terminating" + }) + if err != nil { if apierrors.IsNotFound(err) { return operations.NewTerminateClusterDefault(404).WithPayload(modelsError(err)) } return operations.NewTerminateClusterDefault(0).WithPayload(modelsError(err)) } - - copy, err := d.rt.Clients.TPRScheme().Copy(&oldKluster) - if err != nil { - - } - cpKluster := copy.(*tprv1.Kluster) - cpKluster.Status.State = tprv1.KlusterTerminating - - patchBytes, patchType, err := createPatch(&oldKluster,cpKluster) - if err != nil { - return operations.NewTerminateClusterDefault(0).WithPayload(modelsError(err)) - } - - if err := d.rt.Clients.TPRClient().Patch(patchType).Body(patchBytes).Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(qualifiedName(params.Name,principal.Account)).Do().Error(); err != nil { - glog.Errorf("Failed to patch %s/%s: %s",oldKluster.GetNamespace(),oldKluster.GetName(),err) - return operations.NewTerminateClusterDefault(0).WithPayload(modelsError(err)) - } - return operations.NewTerminateClusterOK() } diff --git a/pkg/api/handlers/update_cluster.go b/pkg/api/handlers/update_cluster.go new file mode 100644 index 0000000000..93fe3a879f --- /dev/null +++ b/pkg/api/handlers/update_cluster.go @@ -0,0 +1,33 @@ +package handlers + +import ( + "github.com/go-openapi/runtime/middleware" + "github.com/sapcc/kubernikus/pkg/api" + "github.com/sapcc/kubernikus/pkg/api/models" + "github.com/sapcc/kubernikus/pkg/api/rest/operations" + + tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" +) + +func NewUpdateCluster(rt *api.Runtime) operations.UpdateClusterHandler { + return &updateCluster{rt: rt} +} + +type updateCluster struct { + rt *api.Runtime +} + +func (d *updateCluster) Handle(params operations.UpdateClusterParams, principal *models.Principal) middleware.Responder { + + _, err := editCluster(d.rt.Clients.TPRClient(), principal, params.Name, func(kluster *tprv1.Kluster) { + //TODO: currently no field to update + }) + if err != nil { + if apierrors.IsNotFound(err) { + return operations.NewUpdateClusterDefault(404).WithPayload(modelsError(err)) + } + return operations.NewUpdateClusterDefault(0).WithPayload(modelsError(err)) + } + return operations.NewUpdateClusterOK() +} diff --git a/pkg/api/handlers/util.go b/pkg/api/handlers/util.go index c31c56356f..cbdddd6a11 100644 --- a/pkg/api/handlers/util.go +++ b/pkg/api/handlers/util.go @@ -1,16 +1,13 @@ package handlers import ( - "encoding/json" - "github.com/go-openapi/swag" "github.com/sapcc/kubernikus/pkg/api/models" "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/client-go/rest" - tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" "fmt" + tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" "strings" ) @@ -26,29 +23,23 @@ func accountSelector(principal *models.Principal) labels.Selector { // qualifiedName returns - func qualifiedName(name string, accountId string) string { - if strings.Contains(name,accountId) { + if strings.Contains(name, accountId) { return name } - return fmt.Sprintf("%s-%s",name,accountId) + return fmt.Sprintf("%s-%s", name, accountId) } -func createPatch(old, new *tprv1.Kluster) (patchBytes []byte, patchType types.PatchType, err error) { - - oldData, err := json.Marshal(old) - if err != nil { - return nil, types.StrategicMergePatchType, err +func editCluster(tprClient *rest.RESTClient, principal *models.Principal, name string, updateFunc func(k *tprv1.Kluster)) (*tprv1.Kluster, error) { + var kluster, updatedCluster tprv1.Kluster + if err := tprClient.Get().Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(qualifiedName(name, principal.Account)).Do().Into(&kluster); err != nil { + return nil, err } - newData, err := json.Marshal(new) - if err != nil { - return nil, types.StrategicMergePatchType, err - } + updateFunc(&kluster) - patchBytes, err = strategicpatch.CreateTwoWayMergePatch(oldData, newData, tprv1.Kluster{}) - if err != nil { - return nil, types.StrategicMergePatchType, err + if err := tprClient.Put().Body(&kluster).Namespace("kubernikus").Resource(tprv1.KlusterResourcePlural).LabelsSelectorParam(accountSelector(principal)).Name(qualifiedName(name, principal.Account)).Do().Into(&updatedCluster); err != nil { + return nil, err } - - return patchBytes, types.StrategicMergePatchType, nil + return &updatedCluster, nil } diff --git a/pkg/api/rest/configure_kubernikus.go b/pkg/api/rest/configure_kubernikus.go index bb32cc3e89..54334c8e05 100644 --- a/pkg/api/rest/configure_kubernikus.go +++ b/pkg/api/rest/configure_kubernikus.go @@ -53,7 +53,7 @@ func configureAPI(api *operations.KubernikusAPI) http.Handler { api.CreateClusterHandler = handlers.NewCreateCluster(rt) api.ShowClusterHandler = handlers.NewShowCluster(rt) api.TerminateClusterHandler = handlers.NewTerminateCluster(rt) - api.PatchClusterHandler = handlers.NewPatchCluster(rt) + api.UpdateClusterHandler = handlers.NewUpdateCluster(rt) api.ServerShutdown = func() {} diff --git a/pkg/api/rest/embedded_spec.go b/pkg/api/rest/embedded_spec.go index dc0ab4acce..ca47659beb 100644 --- a/pkg/api/rest/embedded_spec.go +++ b/pkg/api/rest/embedded_spec.go @@ -119,29 +119,9 @@ func init() { } } }, - "delete": { - "summary": "Terminate the specified cluser", - "operationId": "TerminateCluster", - "security": [ - { - "keystone": [] - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Cluster" - } - }, - "default": { - "$ref": "#/responses/errorResponse" - } - } - }, - "patch": { - "summary": "Patch the specified cluser", - "operationId": "PatchCluster", + "put": { + "summary": "Update the specified cluser", + "operationId": "UpdateCluster", "security": [ { "keystone": [] @@ -176,6 +156,26 @@ func init() { } } }, + "delete": { + "summary": "Terminate the specified cluser", + "operationId": "TerminateCluster", + "security": [ + { + "keystone": [] + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Cluster" + } + }, + "default": { + "$ref": "#/responses/errorResponse" + } + } + }, "parameters": [ { "uniqueItems": true, diff --git a/pkg/api/rest/operations/kubernikus_api.go b/pkg/api/rest/operations/kubernikus_api.go index d1ef15ccff..ac45b1b4a2 100644 --- a/pkg/api/rest/operations/kubernikus_api.go +++ b/pkg/api/rest/operations/kubernikus_api.go @@ -46,15 +46,15 @@ func NewKubernikusAPI(spec *loads.Document) *KubernikusAPI { ListClustersHandler: ListClustersHandlerFunc(func(params ListClustersParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation ListClusters has not yet been implemented") }), - PatchClusterHandler: PatchClusterHandlerFunc(func(params PatchClusterParams, principal *models.Principal) middleware.Responder { - return middleware.NotImplemented("operation PatchCluster has not yet been implemented") - }), ShowClusterHandler: ShowClusterHandlerFunc(func(params ShowClusterParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation ShowCluster has not yet been implemented") }), TerminateClusterHandler: TerminateClusterHandlerFunc(func(params TerminateClusterParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation TerminateCluster has not yet been implemented") }), + UpdateClusterHandler: UpdateClusterHandlerFunc(func(params UpdateClusterParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation UpdateCluster has not yet been implemented") + }), // Applies when the "x-auth-token" header is set KeystoneAuth: func(token string) (*models.Principal, error) { @@ -99,12 +99,12 @@ type KubernikusAPI struct { ListAPIVersionsHandler ListAPIVersionsHandler // ListClustersHandler sets the operation handler for the list clusters operation ListClustersHandler ListClustersHandler - // PatchClusterHandler sets the operation handler for the patch cluster operation - PatchClusterHandler PatchClusterHandler // ShowClusterHandler sets the operation handler for the show cluster operation ShowClusterHandler ShowClusterHandler // TerminateClusterHandler sets the operation handler for the terminate cluster operation TerminateClusterHandler TerminateClusterHandler + // UpdateClusterHandler sets the operation handler for the update cluster operation + UpdateClusterHandler UpdateClusterHandler // ServeError is called when an error is received, there is a default handler // but you can set your own with this @@ -184,10 +184,6 @@ func (o *KubernikusAPI) Validate() error { unregistered = append(unregistered, "ListClustersHandler") } - if o.PatchClusterHandler == nil { - unregistered = append(unregistered, "PatchClusterHandler") - } - if o.ShowClusterHandler == nil { unregistered = append(unregistered, "ShowClusterHandler") } @@ -196,6 +192,10 @@ func (o *KubernikusAPI) Validate() error { unregistered = append(unregistered, "TerminateClusterHandler") } + if o.UpdateClusterHandler == nil { + unregistered = append(unregistered, "UpdateClusterHandler") + } + if len(unregistered) > 0 { return fmt.Errorf("missing registration: %s", strings.Join(unregistered, ", ")) } @@ -306,11 +306,6 @@ func (o *KubernikusAPI) initHandlerCache() { } o.handlers["GET"]["/api/v1/clusters"] = NewListClusters(o.context, o.ListClustersHandler) - if o.handlers["PATCH"] == nil { - o.handlers["PATCH"] = make(map[string]http.Handler) - } - o.handlers["PATCH"]["/api/v1/clusters/{name}"] = NewPatchCluster(o.context, o.PatchClusterHandler) - if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } @@ -321,6 +316,11 @@ func (o *KubernikusAPI) initHandlerCache() { } o.handlers["DELETE"]["/api/v1/clusters/{name}"] = NewTerminateCluster(o.context, o.TerminateClusterHandler) + if o.handlers["PUT"] == nil { + o.handlers["PUT"] = make(map[string]http.Handler) + } + o.handlers["PUT"]["/api/v1/clusters/{name}"] = NewUpdateCluster(o.context, o.UpdateClusterHandler) + } // Serve creates a http handler to serve the API over HTTP diff --git a/pkg/api/rest/operations/patch_cluster_responses.go b/pkg/api/rest/operations/patch_cluster_responses.go deleted file mode 100644 index 01b85652b4..0000000000 --- a/pkg/api/rest/operations/patch_cluster_responses.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package operations - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - "net/http" - - "github.com/go-openapi/runtime" - - "github.com/sapcc/kubernikus/pkg/api/models" -) - -// PatchClusterOKCode is the HTTP code returned for type PatchClusterOK -const PatchClusterOKCode int = 200 - -/*PatchClusterOK OK - -swagger:response patchClusterOK -*/ -type PatchClusterOK struct { - - /* - In: Body - */ - Payload *models.Cluster `json:"body,omitempty"` -} - -// NewPatchClusterOK creates PatchClusterOK with default headers values -func NewPatchClusterOK() *PatchClusterOK { - return &PatchClusterOK{} -} - -// WithPayload adds the payload to the patch cluster o k response -func (o *PatchClusterOK) WithPayload(payload *models.Cluster) *PatchClusterOK { - o.Payload = payload - return o -} - -// SetPayload sets the payload to the patch cluster o k response -func (o *PatchClusterOK) SetPayload(payload *models.Cluster) { - o.Payload = payload -} - -// WriteResponse to the client -func (o *PatchClusterOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { - - rw.WriteHeader(200) - if o.Payload != nil { - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this - } - } -} - -/*PatchClusterDefault Error - -swagger:response patchClusterDefault -*/ -type PatchClusterDefault struct { - _statusCode int - - /* - In: Body - */ - Payload *models.Error `json:"body,omitempty"` -} - -// NewPatchClusterDefault creates PatchClusterDefault with default headers values -func NewPatchClusterDefault(code int) *PatchClusterDefault { - if code <= 0 { - code = 500 - } - - return &PatchClusterDefault{ - _statusCode: code, - } -} - -// WithStatusCode adds the status to the patch cluster default response -func (o *PatchClusterDefault) WithStatusCode(code int) *PatchClusterDefault { - o._statusCode = code - return o -} - -// SetStatusCode sets the status to the patch cluster default response -func (o *PatchClusterDefault) SetStatusCode(code int) { - o._statusCode = code -} - -// WithPayload adds the payload to the patch cluster default response -func (o *PatchClusterDefault) WithPayload(payload *models.Error) *PatchClusterDefault { - o.Payload = payload - return o -} - -// SetPayload sets the payload to the patch cluster default response -func (o *PatchClusterDefault) SetPayload(payload *models.Error) { - o.Payload = payload -} - -// WriteResponse to the client -func (o *PatchClusterDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { - - rw.WriteHeader(o._statusCode) - if o.Payload != nil { - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this - } - } -} diff --git a/pkg/api/rest/operations/patch_cluster.go b/pkg/api/rest/operations/update_cluster.go similarity index 53% rename from pkg/api/rest/operations/patch_cluster.go rename to pkg/api/rest/operations/update_cluster.go index 17fed25606..c058aba710 100644 --- a/pkg/api/rest/operations/patch_cluster.go +++ b/pkg/api/rest/operations/update_cluster.go @@ -13,40 +13,40 @@ import ( "github.com/sapcc/kubernikus/pkg/api/models" ) -// PatchClusterHandlerFunc turns a function with the right signature into a patch cluster handler -type PatchClusterHandlerFunc func(PatchClusterParams, *models.Principal) middleware.Responder +// UpdateClusterHandlerFunc turns a function with the right signature into a update cluster handler +type UpdateClusterHandlerFunc func(UpdateClusterParams, *models.Principal) middleware.Responder // Handle executing the request and returning a response -func (fn PatchClusterHandlerFunc) Handle(params PatchClusterParams, principal *models.Principal) middleware.Responder { +func (fn UpdateClusterHandlerFunc) Handle(params UpdateClusterParams, principal *models.Principal) middleware.Responder { return fn(params, principal) } -// PatchClusterHandler interface for that can handle valid patch cluster params -type PatchClusterHandler interface { - Handle(PatchClusterParams, *models.Principal) middleware.Responder +// UpdateClusterHandler interface for that can handle valid update cluster params +type UpdateClusterHandler interface { + Handle(UpdateClusterParams, *models.Principal) middleware.Responder } -// NewPatchCluster creates a new http.Handler for the patch cluster operation -func NewPatchCluster(ctx *middleware.Context, handler PatchClusterHandler) *PatchCluster { - return &PatchCluster{Context: ctx, Handler: handler} +// NewUpdateCluster creates a new http.Handler for the update cluster operation +func NewUpdateCluster(ctx *middleware.Context, handler UpdateClusterHandler) *UpdateCluster { + return &UpdateCluster{Context: ctx, Handler: handler} } -/*PatchCluster swagger:route PATCH /api/v1/clusters/{name} patchCluster +/*UpdateCluster swagger:route PUT /api/v1/clusters/{name} updateCluster -Patch the specified cluser +Update the specified cluser */ -type PatchCluster struct { +type UpdateCluster struct { Context *middleware.Context - Handler PatchClusterHandler + Handler UpdateClusterHandler } -func (o *PatchCluster) ServeHTTP(rw http.ResponseWriter, r *http.Request) { +func (o *UpdateCluster) ServeHTTP(rw http.ResponseWriter, r *http.Request) { route, rCtx, _ := o.Context.RouteInfo(r) if rCtx != nil { r = rCtx } - var Params = NewPatchClusterParams() + var Params = NewUpdateClusterParams() uprinc, aCtx, err := o.Context.Authorize(r, route) if err != nil { diff --git a/pkg/api/rest/operations/patch_cluster_parameters.go b/pkg/api/rest/operations/update_cluster_parameters.go similarity index 76% rename from pkg/api/rest/operations/patch_cluster_parameters.go rename to pkg/api/rest/operations/update_cluster_parameters.go index 1ffef805ae..1d26214578 100644 --- a/pkg/api/rest/operations/patch_cluster_parameters.go +++ b/pkg/api/rest/operations/update_cluster_parameters.go @@ -18,18 +18,18 @@ import ( "github.com/sapcc/kubernikus/pkg/api/models" ) -// NewPatchClusterParams creates a new PatchClusterParams object +// NewUpdateClusterParams creates a new UpdateClusterParams object // with the default values initialized. -func NewPatchClusterParams() PatchClusterParams { +func NewUpdateClusterParams() UpdateClusterParams { var () - return PatchClusterParams{} + return UpdateClusterParams{} } -// PatchClusterParams contains all the bound params for the patch cluster operation +// UpdateClusterParams contains all the bound params for the update cluster operation // typically these are obtained from a http.Request // -// swagger:parameters PatchCluster -type PatchClusterParams struct { +// swagger:parameters UpdateCluster +type UpdateClusterParams struct { // HTTP Request Object HTTPRequest *http.Request @@ -49,7 +49,7 @@ type PatchClusterParams struct { // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls -func (o *PatchClusterParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { +func (o *UpdateClusterParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error o.HTTPRequest = r @@ -88,7 +88,7 @@ func (o *PatchClusterParams) BindRequest(r *http.Request, route *middleware.Matc return nil } -func (o *PatchClusterParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { +func (o *UpdateClusterParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string if len(rawData) > 0 { raw = rawData[len(rawData)-1] @@ -103,7 +103,7 @@ func (o *PatchClusterParams) bindName(rawData []string, hasKey bool, formats str return nil } -func (o *PatchClusterParams) validateName(formats strfmt.Registry) error { +func (o *UpdateClusterParams) validateName(formats strfmt.Registry) error { return nil } diff --git a/pkg/api/rest/operations/update_cluster_responses.go b/pkg/api/rest/operations/update_cluster_responses.go new file mode 100644 index 0000000000..75ffe86039 --- /dev/null +++ b/pkg/api/rest/operations/update_cluster_responses.go @@ -0,0 +1,115 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/sapcc/kubernikus/pkg/api/models" +) + +// UpdateClusterOKCode is the HTTP code returned for type UpdateClusterOK +const UpdateClusterOKCode int = 200 + +/*UpdateClusterOK OK + +swagger:response updateClusterOK +*/ +type UpdateClusterOK struct { + + /* + In: Body + */ + Payload *models.Cluster `json:"body,omitempty"` +} + +// NewUpdateClusterOK creates UpdateClusterOK with default headers values +func NewUpdateClusterOK() *UpdateClusterOK { + return &UpdateClusterOK{} +} + +// WithPayload adds the payload to the update cluster o k response +func (o *UpdateClusterOK) WithPayload(payload *models.Cluster) *UpdateClusterOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the update cluster o k response +func (o *UpdateClusterOK) SetPayload(payload *models.Cluster) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *UpdateClusterOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +/*UpdateClusterDefault Error + +swagger:response updateClusterDefault +*/ +type UpdateClusterDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewUpdateClusterDefault creates UpdateClusterDefault with default headers values +func NewUpdateClusterDefault(code int) *UpdateClusterDefault { + if code <= 0 { + code = 500 + } + + return &UpdateClusterDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the update cluster default response +func (o *UpdateClusterDefault) WithStatusCode(code int) *UpdateClusterDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the update cluster default response +func (o *UpdateClusterDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the update cluster default response +func (o *UpdateClusterDefault) WithPayload(payload *models.Error) *UpdateClusterDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the update cluster default response +func (o *UpdateClusterDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *UpdateClusterDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/pkg/api/rest/operations/patch_cluster_urlbuilder.go b/pkg/api/rest/operations/update_cluster_urlbuilder.go similarity index 69% rename from pkg/api/rest/operations/patch_cluster_urlbuilder.go rename to pkg/api/rest/operations/update_cluster_urlbuilder.go index 36ef67172e..4ea3d56ac2 100644 --- a/pkg/api/rest/operations/patch_cluster_urlbuilder.go +++ b/pkg/api/rest/operations/update_cluster_urlbuilder.go @@ -12,8 +12,8 @@ import ( "strings" ) -// PatchClusterURL generates an URL for the patch cluster operation -type PatchClusterURL struct { +// UpdateClusterURL generates an URL for the update cluster operation +type UpdateClusterURL struct { Name string _basePath string @@ -24,7 +24,7 @@ type PatchClusterURL struct { // WithBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string -func (o *PatchClusterURL) WithBasePath(bp string) *PatchClusterURL { +func (o *UpdateClusterURL) WithBasePath(bp string) *UpdateClusterURL { o.SetBasePath(bp) return o } @@ -32,12 +32,12 @@ func (o *PatchClusterURL) WithBasePath(bp string) *PatchClusterURL { // SetBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string -func (o *PatchClusterURL) SetBasePath(bp string) { +func (o *UpdateClusterURL) SetBasePath(bp string) { o._basePath = bp } // Build a url path and query string -func (o *PatchClusterURL) Build() (*url.URL, error) { +func (o *UpdateClusterURL) Build() (*url.URL, error) { var result url.URL var _path = "/api/v1/clusters/{name}" @@ -46,7 +46,7 @@ func (o *PatchClusterURL) Build() (*url.URL, error) { if name != "" { _path = strings.Replace(_path, "{name}", name, -1) } else { - return nil, errors.New("Name is required on PatchClusterURL") + return nil, errors.New("Name is required on UpdateClusterURL") } _basePath := o._basePath result.Path = golangswaggerpaths.Join(_basePath, _path) @@ -55,7 +55,7 @@ func (o *PatchClusterURL) Build() (*url.URL, error) { } // Must is a helper function to panic when the url builder returns an error -func (o *PatchClusterURL) Must(u *url.URL, err error) *url.URL { +func (o *UpdateClusterURL) Must(u *url.URL, err error) *url.URL { if err != nil { panic(err) } @@ -66,17 +66,17 @@ func (o *PatchClusterURL) Must(u *url.URL, err error) *url.URL { } // String returns the string representation of the path with query string -func (o *PatchClusterURL) String() string { +func (o *UpdateClusterURL) String() string { return o.Must(o.Build()).String() } // BuildFull builds a full url with scheme, host, path and query string -func (o *PatchClusterURL) BuildFull(scheme, host string) (*url.URL, error) { +func (o *UpdateClusterURL) BuildFull(scheme, host string) (*url.URL, error) { if scheme == "" { - return nil, errors.New("scheme is required for a full url on PatchClusterURL") + return nil, errors.New("scheme is required for a full url on UpdateClusterURL") } if host == "" { - return nil, errors.New("host is required for a full url on PatchClusterURL") + return nil, errors.New("host is required for a full url on UpdateClusterURL") } base, err := o.Build() @@ -90,6 +90,6 @@ func (o *PatchClusterURL) BuildFull(scheme, host string) (*url.URL, error) { } // StringFull returns the string representation of a complete url -func (o *PatchClusterURL) StringFull(scheme, host string) string { +func (o *UpdateClusterURL) StringFull(scheme, host string) string { return o.Must(o.BuildFull(scheme, host)).String() } diff --git a/swagger.yml b/swagger.yml index ac6f2dc7cd..c13f05fd91 100644 --- a/swagger.yml +++ b/swagger.yml @@ -103,9 +103,9 @@ paths: name: name required: true in: path - patch: - operationId: PatchCluster - summary: Patch the specified cluser + put: + operationId: UpdateCluster + summary: Update the specified cluser responses: 200: description: OK From 26a30984086ec0f8ca54a7b174e904478cab4969 Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Fri, 4 Aug 2017 11:55:41 +0200 Subject: [PATCH 12/13] add terminating to groundctl SM --- pkg/controller/ground/controller.go | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/pkg/controller/ground/controller.go b/pkg/controller/ground/controller.go index 4e0f378008..239fc15afe 100644 --- a/pkg/controller/ground/controller.go +++ b/pkg/controller/ground/controller.go @@ -158,7 +158,8 @@ func (op *Operator) handler(key string) error { glog.Infof("Deleting kluster %s (not really, maybe in the future)", key) } else { tpr := obj.(*tprv1.Kluster) - if tpr.Status.State == tprv1.KlusterPending { + switch state := tpr.Status.State; state { + case tprv1.KlusterPending: { glog.Infof("Creating Kluster %s", tpr.GetName()) if err := op.updateStatus(tpr, tprv1.KlusterCreating, "Creating Cluster"); err != nil { glog.Errorf("Failed to update status of kluster %s:%s", tpr.GetName(), err) @@ -173,6 +174,14 @@ func (op *Operator) handler(key string) error { } glog.Infof("Kluster %s created", tpr.GetName()) } + case tprv1.KlusterTerminating: { + glog.Infof("Terminating Kluster %s", tpr.GetName()) + if err := op.terminateKluster(tpr); err != nil { + glog.Errorf("Failed to terminate kluster %s: %s",tpr.Name,err) + } + return nil + } + } } return nil } @@ -283,3 +292,19 @@ func (op *Operator) createKluster(tpr *tprv1.Kluster) error { _, err = helmClient.InstallRelease(path.Join(op.ChartDirectory, "kube-master"), tpr.Namespace, helm.ValueOverrides(rawValues), helm.ReleaseName(tpr.GetName())) return err } + +func (op *Operator) terminateKluster(tpr *tprv1.Kluster) error { + helmClient, err := helmutil.NewClient(op.clients.Clientset(), op.clients.Config()) + if err != nil { + return fmt.Errorf("Failed to create helm client: %s", err) + } + + _, err = helmClient.ReleaseContent(tpr.GetName()) + if err != nil { + _, err = helmClient.DeleteRelease(tpr.GetName(),helm.DeletePurge(true)) + if err != nil { + return err + } + } + return op.clients.TPRClient().Delete().Namespace(tpr.GetNamespace()).Resource(tprv1.KlusterResourcePlural).Name(tpr.GetName()).Do().Error() +} From 218a61721c1116a64b031eabc6f8d435448720d7 Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Fri, 4 Aug 2017 13:35:46 +0200 Subject: [PATCH 13/13] terminating kluster --- pkg/controller/ground/controller.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pkg/controller/ground/controller.go b/pkg/controller/ground/controller.go index 239fc15afe..495bd151fe 100644 --- a/pkg/controller/ground/controller.go +++ b/pkg/controller/ground/controller.go @@ -23,6 +23,8 @@ import ( "github.com/sapcc/kubernikus/pkg/openstack" tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1" "github.com/sapcc/kubernikus/pkg/version" + "google.golang.org/grpc" + "strings" ) const ( @@ -155,7 +157,7 @@ func (op *Operator) handler(key string) error { return fmt.Errorf("Failed to fetch key %s from cache: %s", key, err) } if !exists { - glog.Infof("Deleting kluster %s (not really, maybe in the future)", key) + glog.Infof("TPR of kluster %s deleted",key) } else { tpr := obj.(*tprv1.Kluster) switch state := tpr.Status.State; state { @@ -178,7 +180,9 @@ func (op *Operator) handler(key string) error { glog.Infof("Terminating Kluster %s", tpr.GetName()) if err := op.terminateKluster(tpr); err != nil { glog.Errorf("Failed to terminate kluster %s: %s",tpr.Name,err) + return err } + glog.Infof("Terminated kluster %s",tpr.GetName()) return nil } } @@ -298,13 +302,10 @@ func (op *Operator) terminateKluster(tpr *tprv1.Kluster) error { if err != nil { return fmt.Errorf("Failed to create helm client: %s", err) } - - _, err = helmClient.ReleaseContent(tpr.GetName()) - if err != nil { - _, err = helmClient.DeleteRelease(tpr.GetName(),helm.DeletePurge(true)) - if err != nil { - return err - } + glog.Infof("Deleting helm release %s",tpr.GetName()) + _, err = helmClient.DeleteRelease(tpr.GetName(),helm.DeletePurge(true)) + if err != nil && !strings.Contains(grpc.ErrorDesc(err),"release not found") { + return err } return op.clients.TPRClient().Delete().Namespace(tpr.GetNamespace()).Resource(tprv1.KlusterResourcePlural).Name(tpr.GetName()).Do().Error() }