| /* |
| * 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, |
| }) |
| }) |
| |
| }) |