diff --git a/Makefile b/Makefile
index 76f0e6b93e..312cc2bfbe 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
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
new file mode 100644
index 0000000000..c323ce789d
--- /dev/null
+++ b/pkg/api/handlers/terminate_cluster.go
@@ -0,0 +1,34 @@
+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 NewTerminateCluster(rt *api.Runtime) operations.TerminateClusterHandler {
+	return &terminateCluster{rt: rt}
+}
+
+type terminateCluster struct {
+	rt *api.Runtime
+}
+
+func (d *terminateCluster) Handle(params operations.TerminateClusterParams, principal *models.Principal) middleware.Responder {
+
+	_,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))
+	}
+	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 2467a698ad..cbdddd6a11 100644
--- a/pkg/api/handlers/util.go
+++ b/pkg/api/handlers/util.go
@@ -4,6 +4,11 @@ import (
 	"github.com/go-openapi/swag"
 	"github.com/sapcc/kubernikus/pkg/api/models"
 	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/client-go/rest"
+
+	"fmt"
+	tprv1 "github.com/sapcc/kubernikus/pkg/tpr/v1"
+	"strings"
 )
 
 func modelsError(err error) *models.Error {
@@ -15,3 +20,26 @@ func modelsError(err error) *models.Error {
 func accountSelector(principal *models.Principal) labels.Selector {
 	return labels.SelectorFromSet(map[string]string{"account": principal.Account})
 }
+
+// qualifiedName returns <cluster_name>-<account_id>
+func qualifiedName(name string, accountId string) string {
+	if strings.Contains(name, accountId) {
+		return name
+	}
+	return fmt.Sprintf("%s-%s", name, accountId)
+}
+
+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
+	}
+
+	updateFunc(&kluster)
+
+	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 &updatedCluster, nil
+
+}
diff --git a/pkg/api/rest/configure_kubernikus.go b/pkg/api/rest/configure_kubernikus.go
index 35aeecd077..54334c8e05 100644
--- a/pkg/api/rest/configure_kubernikus.go
+++ b/pkg/api/rest/configure_kubernikus.go
@@ -52,6 +52,8 @@ func configureAPI(api *operations.KubernikusAPI) http.Handler {
 	api.ListClustersHandler = handlers.NewListClusters(rt)
 	api.CreateClusterHandler = handlers.NewCreateCluster(rt)
 	api.ShowClusterHandler = handlers.NewShowCluster(rt)
+	api.TerminateClusterHandler = handlers.NewTerminateCluster(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 eeb5144eb2..ca47659beb 100644
--- a/pkg/api/rest/embedded_spec.go
+++ b/pkg/api/rest/embedded_spec.go
@@ -119,6 +119,63 @@ func init() {
           }
         }
       },
+      "put": {
+        "summary": "Update the specified cluser",
+        "operationId": "UpdateCluster",
+        "security": [
+          {
+            "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",
+            "schema": {
+              "$ref": "#/definitions/Cluster"
+            }
+          },
+          "default": {
+            "$ref": "#/responses/errorResponse"
+          }
+        }
+      },
+      "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 d0fe940117..ac45b1b4a2 100644
--- a/pkg/api/rest/operations/kubernikus_api.go
+++ b/pkg/api/rest/operations/kubernikus_api.go
@@ -49,6 +49,12 @@ 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")
+		}),
+		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) {
@@ -95,6 +101,10 @@ type KubernikusAPI struct {
 	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
+	// 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
@@ -178,6 +188,14 @@ func (o *KubernikusAPI) Validate() error {
 		unregistered = append(unregistered, "ShowClusterHandler")
 	}
 
+	if o.TerminateClusterHandler == nil {
+		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, ", "))
 	}
@@ -293,6 +311,16 @@ 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)
+
+	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/terminate_cluster.go b/pkg/api/rest/operations/terminate_cluster.go
new file mode 100644
index 0000000000..866da2f19f
--- /dev/null
+++ b/pkg/api/rest/operations/terminate_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"
+)
+
+// 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 TerminateClusterHandlerFunc) Handle(params TerminateClusterParams, principal *models.Principal) middleware.Responder {
+	return fn(params, principal)
+}
+
+// TerminateClusterHandler interface for that can handle valid terminate cluster params
+type TerminateClusterHandler interface {
+	Handle(TerminateClusterParams, *models.Principal) middleware.Responder
+}
+
+// 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}
+}
+
+/*TerminateCluster swagger:route DELETE /api/v1/clusters/{name} terminateCluster
+
+Terminate the specified cluser
+
+*/
+type TerminateCluster struct {
+	Context *middleware.Context
+	Handler TerminateClusterHandler
+}
+
+func (o *TerminateCluster) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+	route, rCtx, _ := o.Context.RouteInfo(r)
+	if rCtx != nil {
+		r = rCtx
+	}
+	var Params = NewTerminateClusterParams()
+
+	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/terminate_cluster_parameters.go b/pkg/api/rest/operations/terminate_cluster_parameters.go
new file mode 100644
index 0000000000..82c3e5994e
--- /dev/null
+++ b/pkg/api/rest/operations/terminate_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"
+)
+
+// NewTerminateClusterParams creates a new TerminateClusterParams object
+// with the default values initialized.
+func NewTerminateClusterParams() TerminateClusterParams {
+	var ()
+	return TerminateClusterParams{}
+}
+
+// TerminateClusterParams contains all the bound params for the terminate cluster operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters TerminateCluster
+type TerminateClusterParams 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 *TerminateClusterParams) 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 *TerminateClusterParams) 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 *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/terminate_cluster_urlbuilder.go b/pkg/api/rest/operations/terminate_cluster_urlbuilder.go
new file mode 100644
index 0000000000..4789d5ecc4
--- /dev/null
+++ b/pkg/api/rest/operations/terminate_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"
+)
+
+// TerminateClusterURL generates an URL for the terminate cluster operation
+type TerminateClusterURL 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 *TerminateClusterURL) WithBasePath(bp string) *TerminateClusterURL {
+	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 *TerminateClusterURL) SetBasePath(bp string) {
+	o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *TerminateClusterURL) 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 TerminateClusterURL")
+	}
+	_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 *TerminateClusterURL) 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 *TerminateClusterURL) String() string {
+	return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *TerminateClusterURL) BuildFull(scheme, host string) (*url.URL, error) {
+	if scheme == "" {
+		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 TerminateClusterURL")
+	}
+
+	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 *TerminateClusterURL) StringFull(scheme, host string) string {
+	return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/pkg/api/rest/operations/update_cluster.go b/pkg/api/rest/operations/update_cluster.go
new file mode 100644
index 0000000000..c058aba710
--- /dev/null
+++ b/pkg/api/rest/operations/update_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"
+)
+
+// 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 UpdateClusterHandlerFunc) Handle(params UpdateClusterParams, principal *models.Principal) middleware.Responder {
+	return fn(params, principal)
+}
+
+// UpdateClusterHandler interface for that can handle valid update cluster params
+type UpdateClusterHandler interface {
+	Handle(UpdateClusterParams, *models.Principal) middleware.Responder
+}
+
+// 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}
+}
+
+/*UpdateCluster swagger:route PUT /api/v1/clusters/{name} updateCluster
+
+Update the specified cluser
+
+*/
+type UpdateCluster struct {
+	Context *middleware.Context
+	Handler UpdateClusterHandler
+}
+
+func (o *UpdateCluster) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+	route, rCtx, _ := o.Context.RouteInfo(r)
+	if rCtx != nil {
+		r = rCtx
+	}
+	var Params = NewUpdateClusterParams()
+
+	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/update_cluster_parameters.go b/pkg/api/rest/operations/update_cluster_parameters.go
new file mode 100644
index 0000000000..1d26214578
--- /dev/null
+++ b/pkg/api/rest/operations/update_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"
+)
+
+// NewUpdateClusterParams creates a new UpdateClusterParams object
+// with the default values initialized.
+func NewUpdateClusterParams() UpdateClusterParams {
+	var ()
+	return UpdateClusterParams{}
+}
+
+// UpdateClusterParams contains all the bound params for the update cluster operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters UpdateCluster
+type UpdateClusterParams 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 *UpdateClusterParams) 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 *UpdateClusterParams) 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 *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/update_cluster_urlbuilder.go b/pkg/api/rest/operations/update_cluster_urlbuilder.go
new file mode 100644
index 0000000000..4ea3d56ac2
--- /dev/null
+++ b/pkg/api/rest/operations/update_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"
+)
+
+// UpdateClusterURL generates an URL for the update cluster operation
+type UpdateClusterURL 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 *UpdateClusterURL) WithBasePath(bp string) *UpdateClusterURL {
+	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 *UpdateClusterURL) SetBasePath(bp string) {
+	o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *UpdateClusterURL) 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 UpdateClusterURL")
+	}
+	_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 *UpdateClusterURL) 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 *UpdateClusterURL) String() string {
+	return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *UpdateClusterURL) BuildFull(scheme, host string) (*url.URL, error) {
+	if scheme == "" {
+		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 UpdateClusterURL")
+	}
+
+	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 *UpdateClusterURL) 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..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 (
@@ -85,7 +87,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
 
@@ -155,10 +157,11 @@ 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)
-		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 +176,16 @@ 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 err
+			}
+			glog.Infof("Terminated kluster %s",tpr.GetName())
+			return nil
+		}
+		}
 	}
 	return nil
 }
@@ -187,7 +200,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 {
@@ -283,3 +296,16 @@ 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)
+	}
+	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()
+}
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 35e4a274d2..c13f05fd91 100644
--- a/swagger.yml
+++ b/swagger.yml
@@ -85,6 +85,47 @@ paths:
         name: name
         required: true
         in: path
+    delete:
+      operationId: TerminateCluster
+      summary: Terminate 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
+    put:
+      operationId: UpdateCluster
+      summary: Update 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: