Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Refactor publish offline be #1081

Merged
merged 13 commits into from
Dec 21, 2020
34 changes: 34 additions & 0 deletions api/filter/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (

"github.com/gin-gonic/gin"

"github.com/apisix/manager-api/internal/core/entity"
"github.com/apisix/manager-api/internal/core/store"
"github.com/apisix/manager-api/internal/utils/consts"
"github.com/apisix/manager-api/log"
Expand All @@ -43,6 +44,11 @@ var resources = map[string]string{
"ssl": "ssl",
}

const (
StatusDisable entity.Status = iota
StatusEnable
)

func parseCert(crt, key string) ([]string, error) {
if crt == "" || key == "" {
return nil, errors.New("invalid certificate")
Expand Down Expand Up @@ -136,6 +142,25 @@ func handleSpecialField(resource string, reqBody []byte) ([]byte, error) {
return reqBody, nil
}

func handleDefaultValue(resource string, reqBody []byte) ([]byte, error) {
// go jsonschema lib doesn't support setting default values, so we need to set for some fields necessary
if resource == "routes" {
var route map[string]interface{}
err := json.Unmarshal(reqBody, &route)
if err != nil {
return reqBody, fmt.Errorf("read request body failed: %s", err)
}
if _, ok := route["status"]; !ok {
juzhiyuan marked this conversation as resolved.
Show resolved Hide resolved
route["status"] = StatusEnable
reqBody, err = json.Marshal(route)
if err != nil {
return nil, fmt.Errorf("read request body failed: %s", err)
}
}
}
return reqBody, nil
}

func SchemaCheck() gin.HandlerFunc {
return func(c *gin.Context) {
pathPrefix := "/apisix/admin/"
Expand Down Expand Up @@ -164,6 +189,15 @@ func SchemaCheck() gin.HandlerFunc {
return
}

// set default value
reqBody, err = handleDefaultValue(resource, reqBody)
if err != nil {
errMsg := err.Error()
c.AbortWithStatusJSON(http.StatusBadRequest, consts.InvalidParam(errMsg))
log.Error(errMsg)
return
}

// other filter need it
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody))

Expand Down
3 changes: 3 additions & 0 deletions api/internal/core/entity/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ type BaseInfoGetter interface {
GetBaseInfo() *BaseInfo
}

type Status uint8

// swagger:model Route
type Route struct {
BaseInfo
Expand All @@ -83,6 +85,7 @@ type Route struct {
ServiceProtocol string `json:"service_protocol,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
EnableWebsocket bool `json:"enable_websocket,omitempty"`
Status Status `json:"status"`
}

// --- structures for upstream start ---
Expand Down
32 changes: 32 additions & 0 deletions api/internal/handler/route/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,41 @@ func (h *Handler) ApplyRoute(r *gin.Engine) {
r.DELETE("/apisix/admin/routes/:ids", wgin.Wraps(h.BatchDelete,
wrapper.InputType(reflect.TypeOf(BatchDelete{}))))

r.PATCH("/apisix/admin/routes/:id", consts.ErrorWrapper(Patch))
r.PATCH("/apisix/admin/routes/:id/*path", consts.ErrorWrapper(Patch))

r.GET("/apisix/admin/notexist/routes", consts.ErrorWrapper(Exist))
}

func Patch(c *gin.Context) (interface{}, error) {
reqBody, _ := c.GetRawData()
ID := c.Param("id")
subPath := c.Param("path")

routeStore := store.GetStore(store.HubKeyRoute)
stored, err := routeStore.Get(ID)
if err != nil {
return handler.SpecCodeResponse(err), err
}

res, err := utils.MergePatch(stored, subPath, reqBody)
if err != nil {
return handler.SpecCodeResponse(err), err
}

var route entity.Route
err = json.Unmarshal(res, &route)
if err != nil {
return handler.SpecCodeResponse(err), err
}

if err := routeStore.Update(c, &route, false); err != nil {
return handler.SpecCodeResponse(err), err
}

return nil, nil
}

type GetInput struct {
ID string `auto_read:"id,path" validate:"required"`
}
Expand Down
5 changes: 2 additions & 3 deletions api/test/e2e/route_online_debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,22 +477,21 @@ func TestRoute_Online_Debug_Route_With_Jwt_Auth(t *testing.T) {
}`,
Headers: map[string]string{"Authorization": token},
ExpectStatus: http.StatusOK,
Sleep: sleepTime,
},
}

for _, tc := range tests {
testCaseCheck(tc)
}

time.Sleep(sleepTime)

// sign jwt token
body, status, err := httpGet("http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key")
assert.Nil(t, err)
assert.Equal(t, http.StatusOK, status)
jwtToken := string(body)

time.Sleep(sleepTime)

tests = []HttpTestCase{
{
caseDesc: "online debug route with jwt token",
Expand Down
166 changes: 150 additions & 16 deletions api/test/e2e/route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,24 @@ func TestRoute_Create_With_Hosts(t *testing.T) {
ExpectStatus: http.StatusNotFound,
ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n",
},
{
caseDesc: "delete the route just created",
Object: ManagerApiExpect(t),
Method: http.MethodDelete,
Path: "/apisix/admin/routes/r1",
Headers: map[string]string{"Authorization": token},
ExpectStatus: http.StatusOK,
},
{
caseDesc: "hit the route just deleted",
Object: APISIXExpect(t),
Method: http.MethodGet,
Path: "/hello_",
Headers: map[string]string{"Host": "bar.com"},
ExpectStatus: http.StatusNotFound,
ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n",
Sleep: sleepTime,
},
}

for _, tc := range tests {
Expand All @@ -181,12 +199,51 @@ func TestRoute_Create_With_Hosts(t *testing.T) {
func TestRoute_Update_Routes_With_Hosts(t *testing.T) {
tests := []HttpTestCase{
{
caseDesc: "update route",
caseDesc: "hit route that not exist",
Object: APISIXExpect(t),
Method: http.MethodGet,
Path: "/hello",
Headers: map[string]string{"Host": "foo.com"},
ExpectStatus: http.StatusNotFound,
ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n",
},
{
caseDesc: "create route with host foo.com",
Object: ManagerApiExpect(t),
Method: http.MethodPut,
Path: "/apisix/admin/routes/r1",
Body: `{
"uri": "/hello1",
"uri": "/hello",
"methods": ["GET"],
"hosts": ["foo.com"],
"upstream": {
"type": "roundrobin",
"nodes": [{
"host": "172.16.238.20",
"port": 1980,
"weight": 1
}]
}
}`,
Headers: map[string]string{"Authorization": token},
ExpectStatus: http.StatusOK,
},
{
caseDesc: "hit the route just create",
Object: APISIXExpect(t),
Method: http.MethodGet,
Path: "/hello",
Headers: map[string]string{"Host": "foo.com"},
ExpectStatus: http.StatusOK,
Sleep: sleepTime,
},
{
caseDesc: "update route with host bar.com",
Object: ManagerApiExpect(t),
Method: http.MethodPut,
Path: "/apisix/admin/routes/r1",
Body: `{
"uri": "/hello",
"hosts": ["bar.com"],
"upstream": {
"nodes": {
Expand All @@ -199,23 +256,40 @@ func TestRoute_Update_Routes_With_Hosts(t *testing.T) {
ExpectStatus: http.StatusOK,
},
{
caseDesc: "hit the route just updated",
caseDesc: "hit the route with host foo.com",
Object: APISIXExpect(t),
Method: http.MethodGet,
Path: "/hello_",
Path: "/hello",
Headers: map[string]string{"Host": "foo.com"},
ExpectStatus: http.StatusNotFound,
ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n",
Sleep: sleepTime,
},
{
caseDesc: "hit the route just updated",
Object: APISIXExpect(t),
Method: http.MethodGet,
Path: "/hello1",
Path: "/hello",
Headers: map[string]string{"Host": "bar.com"},
ExpectStatus: http.StatusOK,
ExpectBody: "hello1 world\n",
ExpectBody: "hello world\n",
},
{
caseDesc: "delete route",
Object: ManagerApiExpect(t),
Method: http.MethodDelete,
Path: "/apisix/admin/routes/r1",
Headers: map[string]string{"Authorization": token},
ExpectStatus: http.StatusOK,
},
{
caseDesc: "hit the route just deleted",
Object: APISIXExpect(t),
Method: http.MethodGet,
Path: "/hello",
Headers: map[string]string{"Host": "bar.com"},
ExpectStatus: http.StatusNotFound,
ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n",
Sleep: sleepTime,
},
}

Expand All @@ -224,36 +298,96 @@ func TestRoute_Update_Routes_With_Hosts(t *testing.T) {
}
}

func TestRoute_Delete_Routes_With_Hosts(t *testing.T) {
func TestRoute_Patch(t *testing.T) {
tests := []HttpTestCase{
{
caseDesc: "delete route",
caseDesc: "make sure the route not exists",
Object: APISIXExpect(t),
Method: http.MethodGet,
Path: "/hello",
ExpectStatus: http.StatusNotFound,
ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n",
},
{
caseDesc: "create route",
Object: ManagerApiExpect(t),
Method: http.MethodPut,
Path: "/apisix/admin/routes/r1",
Body: `{
"uri": "/hello",
"upstream": {
"nodes": {
"172.16.238.20:1980": 1
},
"type": "roundrobin"
}
}`,
Headers: map[string]string{"Authorization": token},
ExpectStatus: http.StatusOK,
},
{
caseDesc: "hit the route just created ",
Object: APISIXExpect(t),
Method: http.MethodGet,
Path: "/hello",
ExpectStatus: http.StatusOK,
ExpectBody: "hello world",
Sleep: sleepTime,
},
{
caseDesc: "route patch for update status(route offline)",
Object: ManagerApiExpect(t),
Method: http.MethodDelete,
Method: http.MethodPatch,
Path: "/apisix/admin/routes/r1",
Body: `{"status":0}`,
Headers: map[string]string{"Authorization": token},
ExpectStatus: http.StatusOK,
},
{
caseDesc: "delete not exist route",
caseDesc: "make sure the route has been offline",
Object: APISIXExpect(t),
Method: http.MethodGet,
Path: "/hello",
ExpectStatus: http.StatusNotFound,
ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n",
Sleep: sleepTime,
},
{
caseDesc: "route patch for update status (route online)",
Object: ManagerApiExpect(t),
Method: http.MethodPatch,
Path: "/apisix/admin/routes/r1/status",
Body: "1",
Headers: map[string]string{"Authorization": token},
ExpectStatus: http.StatusOK,
},
{
caseDesc: "make sure the route has been online",
Object: APISIXExpect(t),
Method: http.MethodGet,
Path: "/hello",
ExpectStatus: http.StatusOK,
ExpectBody: "hello world",
Sleep: sleepTime,
},
{
caseDesc: "delete route",
Object: ManagerApiExpect(t),
Method: http.MethodDelete,
Path: "/apisix/admin/routes/not-exist",
Path: "/apisix/admin/routes/r1",
Headers: map[string]string{"Authorization": token},
ExpectStatus: http.StatusNotFound,
ExpectStatus: http.StatusOK,
},
{
caseDesc: "hit the route just deleted",
Object: APISIXExpect(t),
Method: http.MethodGet,
Path: "/hello1",
Headers: map[string]string{"Host": "bar.com"},
Path: "/hello",
ExpectStatus: http.StatusNotFound,
ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n",
Sleep: sleepTime,
},
}

for _, tc := range tests {
testCaseCheck(tc)
}
Expand Down