mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
api: add support for deleting apps (#327)
* api: add support for deleting apps Fixes #274 * functions: improve error name and description * functions: fix test regression
This commit is contained in:
@@ -8,16 +8,17 @@ import (
|
|||||||
type Apps []*App
|
type Apps []*App
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrAppsCreate = errors.New("Could not create app")
|
ErrAppsAlreadyExists = errors.New("App already exists")
|
||||||
ErrAppsUpdate = errors.New("Could not update app")
|
ErrAppsCreate = errors.New("Could not create app")
|
||||||
ErrAppsRemoving = errors.New("Could not remove app from datastore")
|
ErrAppsGet = errors.New("Could not get app from datastore")
|
||||||
ErrAppsGet = errors.New("Could not get app from datastore")
|
ErrAppsList = errors.New("Could not list apps from datastore")
|
||||||
ErrAppsList = errors.New("Could not list apps from datastore")
|
ErrAppsMissingNew = errors.New("Missing new application")
|
||||||
ErrAppsAlreadyExists = errors.New("App already exists")
|
ErrAppsNotFound = errors.New("App not found")
|
||||||
ErrAppsNotFound = errors.New("App not found")
|
ErrAppsNothingToUpdate = errors.New("Nothing to update")
|
||||||
ErrAppsNothingToUpdate = errors.New("Nothing to update")
|
ErrAppsRemoving = errors.New("Could not remove app from datastore")
|
||||||
ErrAppsMissingNew = errors.New("Missing new application")
|
ErrDeleteAppsWithRoutes = errors.New("Cannot remove apps with routes")
|
||||||
ErrUsableImage = errors.New("Image not found")
|
ErrAppsUpdate = errors.New("Could not update app")
|
||||||
|
ErrUsableImage = errors.New("Image not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
|
|||||||
@@ -14,13 +14,25 @@ func handleAppDelete(c *gin.Context) {
|
|||||||
log := common.Logger(ctx)
|
log := common.Logger(ctx)
|
||||||
|
|
||||||
appName := c.Param("app")
|
appName := c.Param("app")
|
||||||
err := Api.Datastore.RemoveApp(appName)
|
|
||||||
|
|
||||||
|
routes, err := Api.Datastore.GetRoutesByApp(appName, &models.RouteFilter{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Debug(models.ErrAppsRemoving)
|
log.WithError(err).Debug(models.ErrAppsRemoving)
|
||||||
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsRemoving))
|
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsRemoving))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(routes) > 0 {
|
||||||
|
log.WithError(err).Debug(models.ErrDeleteAppsWithRoutes)
|
||||||
|
c.JSON(http.StatusBadRequest, simpleError(models.ErrDeleteAppsWithRoutes))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Api.Datastore.RemoveApp(appName); err != nil {
|
||||||
|
log.WithError(err).Debug(models.ErrAppsRemoving)
|
||||||
|
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsRemoving))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "App deleted"})
|
c.JSON(http.StatusOK, gin.H{"message": "App deleted"})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,33 +97,36 @@ func TestFullStack(t *testing.T) {
|
|||||||
|
|
||||||
router := testRouter(ds, &mqs.Mock{}, testRunner(t), tasks)
|
router := testRouter(ds, &mqs.Mock{}, testRunner(t), tasks)
|
||||||
|
|
||||||
for i, test := range []struct {
|
for _, test := range []struct {
|
||||||
|
name string
|
||||||
method string
|
method string
|
||||||
path string
|
path string
|
||||||
body string
|
body string
|
||||||
expectedCode int
|
expectedCode int
|
||||||
}{
|
}{
|
||||||
{"POST", "/v1/apps", `{ "app": { "name": "myapp" } }`, http.StatusCreated},
|
{"create my app", "POST", "/v1/apps", `{ "app": { "name": "myapp" } }`, http.StatusCreated},
|
||||||
{"GET", "/v1/apps", ``, http.StatusOK},
|
{"list apps", "GET", "/v1/apps", ``, http.StatusOK},
|
||||||
{"GET", "/v1/apps/myapp", ``, http.StatusOK},
|
{"get app", "GET", "/v1/apps/myapp", ``, http.StatusOK},
|
||||||
{"POST", "/v1/apps/myapp/routes", `{ "route": { "name": "myroute", "path": "/myroute", "image": "iron/hello" } }`, http.StatusCreated},
|
{"add myroute", "POST", "/v1/apps/myapp/routes", `{ "route": { "name": "myroute", "path": "/myroute", "image": "iron/hello" } }`, http.StatusCreated},
|
||||||
{"POST", "/v1/apps/myapp/routes", `{ "route": { "name": "myroute2", "path": "/myroute2", "image": "iron/error" } }`, http.StatusCreated},
|
{"add myroute2", "POST", "/v1/apps/myapp/routes", `{ "route": { "name": "myroute2", "path": "/myroute2", "image": "iron/error" } }`, http.StatusCreated},
|
||||||
{"GET", "/v1/apps/myapp/routes/myroute", ``, http.StatusOK},
|
{"get myroute", "GET", "/v1/apps/myapp/routes/myroute", ``, http.StatusOK},
|
||||||
{"GET", "/v1/apps/myapp/routes/myroute2", ``, http.StatusOK},
|
{"get myroute2", "GET", "/v1/apps/myapp/routes/myroute2", ``, http.StatusOK},
|
||||||
{"GET", "/v1/apps/myapp/routes", ``, http.StatusOK},
|
{"get all routes", "GET", "/v1/apps/myapp/routes", ``, http.StatusOK},
|
||||||
{"POST", "/r/myapp/myroute", `{ "name": "Teste" }`, http.StatusOK},
|
{"execute myroute", "POST", "/r/myapp/myroute", `{ "name": "Teste" }`, http.StatusOK},
|
||||||
{"POST", "/r/myapp/myroute2", `{ "name": "Teste" }`, http.StatusInternalServerError},
|
{"execute myroute2", "POST", "/r/myapp/myroute2", `{ "name": "Teste" }`, http.StatusInternalServerError},
|
||||||
{"DELETE", "/v1/apps/myapp/routes/myroute", ``, http.StatusOK},
|
{"delete myroute", "DELETE", "/v1/apps/myapp/routes/myroute", ``, http.StatusOK},
|
||||||
{"DELETE", "/v1/apps/myapp", ``, http.StatusOK},
|
{"delete app (fail)", "DELETE", "/v1/apps/myapp", ``, http.StatusBadRequest},
|
||||||
{"GET", "/v1/apps/myapp", ``, http.StatusNotFound},
|
{"delete myroute2", "DELETE", "/v1/apps/myapp/routes/myroute2", ``, http.StatusOK},
|
||||||
{"GET", "/v1/apps/myapp/routes/myroute", ``, http.StatusInternalServerError},
|
{"delete app (success)", "DELETE", "/v1/apps/myapp", ``, http.StatusOK},
|
||||||
|
{"get deleted app", "GET", "/v1/apps/myapp", ``, http.StatusNotFound},
|
||||||
|
{"get delete route on deleted app", "GET", "/v1/apps/myapp/routes/myroute", ``, http.StatusInternalServerError},
|
||||||
} {
|
} {
|
||||||
_, rec := routerRequest(t, router, test.method, test.path, bytes.NewBuffer([]byte(test.body)))
|
_, rec := routerRequest(t, router, test.method, test.path, bytes.NewBuffer([]byte(test.body)))
|
||||||
|
|
||||||
if rec.Code != test.expectedCode {
|
if rec.Code != test.expectedCode {
|
||||||
t.Log(buf.String())
|
t.Log(buf.String())
|
||||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
t.Errorf("Test \"%s\": Expected status code to be %d but was %d",
|
||||||
i, test.expectedCode, rec.Code)
|
test.name, test.expectedCode, rec.Code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ swagger: '2.0'
|
|||||||
info:
|
info:
|
||||||
title: IronFunctions
|
title: IronFunctions
|
||||||
description: The open source serverless platform.
|
description: The open source serverless platform.
|
||||||
version: "0.1.16"
|
version: "0.1.17"
|
||||||
# the domain of the service
|
# the domain of the service
|
||||||
host: "127.0.0.1:8080"
|
host: "127.0.0.1:8080"
|
||||||
# array of all schemes that your API supports
|
# array of all schemes that your API supports
|
||||||
@@ -67,6 +67,20 @@ paths:
|
|||||||
|
|
||||||
|
|
||||||
/apps/{app}:
|
/apps/{app}:
|
||||||
|
delete:
|
||||||
|
summary: "Delete an app."
|
||||||
|
description: "Delete an app."
|
||||||
|
tags:
|
||||||
|
- Apps
|
||||||
|
parameters:
|
||||||
|
- name: app
|
||||||
|
in: path
|
||||||
|
description: Name of the app.
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Apps successfully deleted.
|
||||||
get:
|
get:
|
||||||
summary: "Get information for a app."
|
summary: "Get information for a app."
|
||||||
description: "This gives more details about a app, such as statistics."
|
description: "This gives more details about a app, such as statistics."
|
||||||
@@ -271,7 +285,7 @@ paths:
|
|||||||
type: string
|
type: string
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Route successfully deleted. Deletion succeeds even on routes that do not exist.
|
description: Route successfully deleted.
|
||||||
|
|
||||||
/tasks:
|
/tasks:
|
||||||
get:
|
get:
|
||||||
|
|||||||
29
fn/apps.go
29
fn/apps.go
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
@@ -79,6 +80,11 @@ func apps() cli.Command {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "delete",
|
||||||
|
Usage: "delete an app",
|
||||||
|
Action: a.delete,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -249,3 +255,26 @@ func (a *appsCmd) storeApp(appName string, config map[string]string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *appsCmd) delete(c *cli.Context) error {
|
||||||
|
appName := c.Args().First()
|
||||||
|
if appName == "" {
|
||||||
|
return errors.New("error: deleting an app takes one argument, an app name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := resetBasePath(a.Configuration); err != nil {
|
||||||
|
return fmt.Errorf("error setting endpoint: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := a.AppsAppDelete(appName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error deleting app: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusBadRequest {
|
||||||
|
return errors.New("could not delete this application - pending routes")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(appName, "deleted")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user