blob: 5908537244153110102eaaa63700609c00ad210a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package migrate_test
import (
"bytes"
"encoding/binary"
"encoding/json"
"hash/crc32"
"net/http"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/apisix/manager-api/test/e2e/base"
)
const (
checksumLength = 4 // 4 bytes (uint32)
)
type AllData struct {
Consumers []interface{}
Routes []interface{}
Services []interface{}
SSLs []interface{}
Upstreams []interface{}
Scripts []interface{}
GlobalPlugins []interface{}
PluginConfigs []interface{}
}
type response struct {
Code int `json:"Code"`
Message string `json:"Message"`
Data struct {
ConflictItems AllData `json:"ConflictItems"`
} `json:"Data"`
}
var _ = Describe("Migrate", func() {
var exportData []byte
DescribeTable("Prepare config data",
func(tc base.HttpTestCase) {
base.RunTestCase(tc)
},
Entry("create test route", base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodPut,
Path: "/apisix/admin/routes/r1",
Body: `{
"name": "route",
"uri": "/hello_",
"upstream": {
"nodes": {
"` + base.UpstreamIp + `:1980": 1
},
"type": "roundrobin"
}
}`,
Headers: map[string]string{"Authorization": base.GetToken()},
ExpectStatus: http.StatusOK,
}),
Entry("create test upstream", base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodPut,
Path: "/apisix/admin/upstreams/u1",
Body: `{
"name": "upstream",
"nodes": [
{
"host": "` + base.UpstreamIp + `",
"port": 1980,
"weight": 1
}
],
"type": "roundrobin"
}`,
Headers: map[string]string{"Authorization": base.GetToken()},
ExpectStatus: http.StatusOK,
}),
Entry("create test service", base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodPut,
Path: "/apisix/admin/services/s1",
Body: `{
"name": "service",
"upstream": {
"nodes": [
{
"host": "` + base.UpstreamIp + `",
"port": 1980,
"weight": 1
},
{
"host": "` + base.UpstreamIp + `",
"port": 1981,
"weight": 2
},
{
"host": "` + base.UpstreamIp + `",
"port": 1982,
"weight": 3
}
],
"type": "roundrobin"
}
}`,
Headers: map[string]string{"Authorization": base.GetToken()},
ExpectStatus: http.StatusOK,
Sleep: time.Second * 1,
}),
Entry("migrate export auth test", base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodPost,
Path: "/apisix/admin/migrate/export",
ExpectStatus: http.StatusUnauthorized,
ExpectBody: "request unauthorized",
Sleep: base.SleepTime,
}),
Entry("migrate import auth test", base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodPost,
Path: "/apisix/admin/migrate/import",
ExpectStatus: http.StatusUnauthorized,
ExpectBody: "request unauthorized",
Sleep: base.SleepTime,
}),
)
It("export config success", func() {
req := base.ManagerApiExpect().GET("/apisix/admin/migrate/export")
req.WithHeader("Authorization", base.GetToken())
resp := req.Expect()
resp.Status(http.StatusOK)
exportData = []byte(resp.Body().Raw())
data := exportData[:len(exportData)-checksumLength]
checksum := binary.BigEndian.Uint32(exportData[len(exportData)-checksumLength:])
Expect(checksum).Should(Equal(crc32.ChecksumIEEE(data)))
})
It("import config conflict and return", func() {
req := base.ManagerApiExpect().POST("/apisix/admin/migrate/import")
buffer := bytes.NewBuffer(exportData)
req.WithMultipart().WithForm(map[string]string{"mode": "return"})
req.WithMultipart().WithFile("file", "apisix-config.bak", buffer)
req.WithHeader("Authorization", base.GetToken())
resp := req.Expect()
resp.Status(http.StatusOK)
rsp := &response{}
err := json.Unmarshal([]byte(resp.Body().Raw()), rsp)
Expect(err).Should(BeNil())
Expect(rsp.Code).Should(Equal(20001))
Expect(len(rsp.Data.ConflictItems.Routes)).Should(Equal(1))
Expect(len(rsp.Data.ConflictItems.Upstreams)).Should(Equal(1))
Expect(len(rsp.Data.ConflictItems.Services)).Should(Equal(1))
})
It("import config conflict and skip", func() {
req := base.ManagerApiExpect().POST("/apisix/admin/migrate/import")
buffer := bytes.NewBuffer(exportData)
req.WithMultipart().WithForm(map[string]string{"mode": "skip"})
req.WithMultipart().WithFile("file", "apisix-config.bak", buffer)
req.WithHeader("Authorization", base.GetToken())
resp := req.Expect()
resp.Status(http.StatusOK)
rsp := &response{}
err := json.Unmarshal([]byte(resp.Body().Raw()), rsp)
Expect(err).Should(BeNil())
Expect(rsp.Code).Should(Equal(0))
})
It("import config conflict and overwrite", func() {
req := base.ManagerApiExpect().POST("/apisix/admin/migrate/import")
buffer := bytes.NewBuffer(exportData)
req.WithMultipart().WithForm(map[string]string{"mode": "overwrite"})
req.WithMultipart().WithFile("file", "apisix-config.bak", buffer)
req.WithHeader("Authorization", base.GetToken())
resp := req.Expect()
resp.Status(http.StatusOK)
rsp := &response{}
err := json.Unmarshal([]byte(resp.Body().Raw()), rsp)
Expect(err).Should(BeNil())
Expect(rsp.Code).Should(Equal(0))
})
It("request hit route r1", func() {
base.RunTestCase(base.HttpTestCase{
Object: base.APISIXExpect(),
Method: http.MethodGet,
Path: "/hello_",
ExpectStatus: http.StatusOK,
ExpectBody: "hello world",
Sleep: base.SleepTime,
})
})
DescribeTable("Cleanup config data",
func(tc base.HttpTestCase) {
base.RunTestCase(tc)
},
Entry("remove test route", base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodDelete,
Path: "/apisix/admin/routes/r1",
Headers: map[string]string{"Authorization": base.GetToken()},
ExpectStatus: http.StatusOK,
}),
Entry("remove test upstream", base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodDelete,
Path: "/apisix/admin/upstreams/u1",
Headers: map[string]string{"Authorization": base.GetToken()},
ExpectStatus: http.StatusOK,
}),
Entry("remove test service", base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodDelete,
Path: "/apisix/admin/services/s1",
Headers: map[string]string{"Authorization": base.GetToken()},
ExpectStatus: http.StatusOK,
}),
)
It("delete imported route failed", func() {
base.RunTestCase(base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodDelete,
Path: "/apisix/admin/routes/r1",
Headers: map[string]string{"Authorization": base.GetToken()},
ExpectStatus: http.StatusNotFound,
})
})
It("request route r1 not found", func() {
base.RunTestCase(base.HttpTestCase{
Object: base.APISIXExpect(),
Method: http.MethodGet,
Path: "/hello_",
ExpectStatus: http.StatusNotFound,
Sleep: base.SleepTime,
})
})
It("import config success", func() {
req := base.ManagerApiExpect().POST("/apisix/admin/migrate/import")
buffer := bytes.NewBuffer(exportData)
req.WithMultipart().WithForm(map[string]string{"mode": "return"})
req.WithMultipart().WithFile("file", "apisix-config.bak", buffer)
req.WithHeader("Authorization", base.GetToken())
resp := req.Expect()
resp.Status(http.StatusOK)
rsp := &response{}
err := json.Unmarshal([]byte(resp.Body().Raw()), rsp)
Expect(err).Should(BeNil())
Expect(rsp.Code).Should(Equal(0))
})
It("request hit route r1", func() {
base.RunTestCase(base.HttpTestCase{
Object: base.APISIXExpect(),
Method: http.MethodGet,
Path: "/hello_",
ExpectStatus: http.StatusOK,
ExpectBody: "hello world",
Sleep: base.SleepTime,
})
})
It("delete imported route success", func() {
base.RunTestCase(base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodDelete,
Path: "/apisix/admin/routes/r1",
Headers: map[string]string{"Authorization": base.GetToken()},
ExpectStatus: http.StatusOK,
})
})
It("delete imported upstream success", func() {
base.RunTestCase(base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodDelete,
Path: "/apisix/admin/upstreams/u1",
Headers: map[string]string{"Authorization": base.GetToken()},
ExpectStatus: http.StatusOK,
})
})
It("delete imported service success", func() {
base.RunTestCase(base.HttpTestCase{
Object: base.ManagerApiExpect(),
Method: http.MethodDelete,
Path: "/apisix/admin/services/s1",
Headers: map[string]string{"Authorization": base.GetToken()},
ExpectStatus: http.StatusOK,
})
})
})