2019-11-28 20:56:04 +00:00
|
|
|
package integration
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2022-08-11 13:42:07 +00:00
|
|
|
"context"
|
2019-11-28 20:56:04 +00:00
|
|
|
"encoding/json"
|
2023-09-25 14:38:07 +00:00
|
|
|
"errors"
|
2021-11-25 10:10:06 +00:00
|
|
|
"fmt"
|
|
|
|
"net"
|
2019-11-28 20:56:04 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2024-01-09 16:00:07 +00:00
|
|
|
"testing"
|
2019-11-28 20:56:04 +00:00
|
|
|
"time"
|
|
|
|
|
2022-09-12 15:40:09 +00:00
|
|
|
"github.com/kvtools/consul"
|
2022-01-12 13:42:21 +00:00
|
|
|
"github.com/kvtools/valkeyrie"
|
|
|
|
"github.com/kvtools/valkeyrie/store"
|
2019-11-28 20:56:04 +00:00
|
|
|
"github.com/pmezard/go-difflib/difflib"
|
2024-01-09 16:00:07 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/stretchr/testify/suite"
|
2023-02-03 14:24:05 +00:00
|
|
|
"github.com/traefik/traefik/v3/integration/try"
|
|
|
|
"github.com/traefik/traefik/v3/pkg/api"
|
2019-11-28 20:56:04 +00:00
|
|
|
)
|
|
|
|
|
2021-11-25 10:10:06 +00:00
|
|
|
// Consul test suites.
|
2019-11-28 20:56:04 +00:00
|
|
|
type ConsulSuite struct {
|
|
|
|
BaseSuite
|
2021-11-25 10:10:06 +00:00
|
|
|
kvClient store.Store
|
|
|
|
consulURL string
|
2019-11-28 20:56:04 +00:00
|
|
|
}
|
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
func TestConsulSuite(t *testing.T) {
|
|
|
|
suite.Run(t, new(ConsulSuite))
|
2023-09-25 14:38:07 +00:00
|
|
|
}
|
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
func (s *ConsulSuite) SetupSuite() {
|
|
|
|
s.BaseSuite.SetupSuite()
|
|
|
|
s.createComposeProject("consul")
|
|
|
|
s.composeUp()
|
2021-11-25 10:10:06 +00:00
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
consulAddr := net.JoinHostPort(s.getComposeServiceIP("consul"), "8500")
|
2021-11-25 10:10:06 +00:00
|
|
|
s.consulURL = fmt.Sprintf("http://%s", consulAddr)
|
2019-11-28 20:56:04 +00:00
|
|
|
|
|
|
|
kv, err := valkeyrie.NewStore(
|
2022-08-11 13:42:07 +00:00
|
|
|
context.Background(),
|
2022-09-12 15:40:09 +00:00
|
|
|
consul.StoreName,
|
2021-11-25 10:10:06 +00:00
|
|
|
[]string{consulAddr},
|
2022-09-12 15:40:09 +00:00
|
|
|
&consul.Config{
|
2019-11-28 20:56:04 +00:00
|
|
|
ConnectionTimeout: 10 * time.Second,
|
|
|
|
},
|
|
|
|
)
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err, "Cannot create store consul")
|
2019-11-28 20:56:04 +00:00
|
|
|
s.kvClient = kv
|
|
|
|
|
|
|
|
// wait for consul
|
|
|
|
err = try.Do(60*time.Second, try.KVExists(kv, "test"))
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2019-11-28 20:56:04 +00:00
|
|
|
}
|
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
func (s *ConsulSuite) TearDownSuite() {
|
|
|
|
s.BaseSuite.TearDownSuite()
|
|
|
|
}
|
2019-11-28 20:56:04 +00:00
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
func (s *ConsulSuite) TearDownTest() {
|
|
|
|
err := s.kvClient.DeleteTree(context.Background(), "traefik")
|
|
|
|
if err != nil && !errors.Is(err, store.ErrKeyNotFound) {
|
|
|
|
require.ErrorIs(s.T(), err, store.ErrKeyNotFound)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *ConsulSuite) TestSimpleConfiguration() {
|
|
|
|
file := s.adaptFile("fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL})
|
2019-11-28 20:56:04 +00:00
|
|
|
|
|
|
|
data := map[string]string{
|
|
|
|
"traefik/http/routers/Router0/entryPoints/0": "web",
|
|
|
|
"traefik/http/routers/Router0/middlewares/0": "compressor",
|
|
|
|
"traefik/http/routers/Router0/middlewares/1": "striper",
|
|
|
|
"traefik/http/routers/Router0/service": "simplesvc",
|
|
|
|
"traefik/http/routers/Router0/rule": "Host(`kv1.localhost`)",
|
|
|
|
"traefik/http/routers/Router0/priority": "42",
|
|
|
|
"traefik/http/routers/Router0/tls": "",
|
|
|
|
|
|
|
|
"traefik/http/routers/Router1/rule": "Host(`kv2.localhost`)",
|
|
|
|
"traefik/http/routers/Router1/priority": "42",
|
|
|
|
"traefik/http/routers/Router1/tls/domains/0/main": "aaa.localhost",
|
|
|
|
"traefik/http/routers/Router1/tls/domains/0/sans/0": "aaa.aaa.localhost",
|
|
|
|
"traefik/http/routers/Router1/tls/domains/0/sans/1": "bbb.aaa.localhost",
|
|
|
|
"traefik/http/routers/Router1/tls/domains/1/main": "bbb.localhost",
|
|
|
|
"traefik/http/routers/Router1/tls/domains/1/sans/0": "aaa.bbb.localhost",
|
|
|
|
"traefik/http/routers/Router1/tls/domains/1/sans/1": "bbb.bbb.localhost",
|
|
|
|
"traefik/http/routers/Router1/entryPoints/0": "web",
|
|
|
|
"traefik/http/routers/Router1/service": "simplesvc",
|
|
|
|
|
|
|
|
"traefik/http/services/simplesvc/loadBalancer/servers/0/url": "http://10.0.1.1:8888",
|
|
|
|
"traefik/http/services/simplesvc/loadBalancer/servers/1/url": "http://10.0.1.1:8889",
|
|
|
|
|
|
|
|
"traefik/http/services/srvcA/loadBalancer/servers/0/url": "http://10.0.1.2:8888",
|
|
|
|
"traefik/http/services/srvcA/loadBalancer/servers/1/url": "http://10.0.1.2:8889",
|
|
|
|
|
|
|
|
"traefik/http/services/srvcB/loadBalancer/servers/0/url": "http://10.0.1.3:8888",
|
|
|
|
"traefik/http/services/srvcB/loadBalancer/servers/1/url": "http://10.0.1.3:8889",
|
|
|
|
|
|
|
|
"traefik/http/services/mirror/mirroring/service": "simplesvc",
|
|
|
|
"traefik/http/services/mirror/mirroring/mirrors/0/name": "srvcA",
|
|
|
|
"traefik/http/services/mirror/mirroring/mirrors/0/percent": "42",
|
|
|
|
"traefik/http/services/mirror/mirroring/mirrors/1/name": "srvcB",
|
|
|
|
"traefik/http/services/mirror/mirroring/mirrors/1/percent": "42",
|
|
|
|
|
|
|
|
"traefik/http/services/Service03/weighted/services/0/name": "srvcA",
|
|
|
|
"traefik/http/services/Service03/weighted/services/0/weight": "42",
|
|
|
|
"traefik/http/services/Service03/weighted/services/1/name": "srvcB",
|
|
|
|
"traefik/http/services/Service03/weighted/services/1/weight": "42",
|
|
|
|
|
|
|
|
"traefik/http/middlewares/compressor/compress": "",
|
|
|
|
"traefik/http/middlewares/striper/stripPrefix/prefixes/0": "foo",
|
|
|
|
"traefik/http/middlewares/striper/stripPrefix/prefixes/1": "bar",
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range data {
|
2022-08-11 13:42:07 +00:00
|
|
|
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2019-11-28 20:56:04 +00:00
|
|
|
}
|
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
s.traefikCmd(withConfigFile(file))
|
2020-02-10 14:48:06 +00:00
|
|
|
|
2019-11-28 20:56:04 +00:00
|
|
|
// wait for traefik
|
2024-01-09 16:00:07 +00:00
|
|
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
2020-02-10 14:48:06 +00:00
|
|
|
try.BodyContains(`"striper@consul":`, `"compressor@consul":`, `"srvcA@consul":`, `"srvcB@consul":`),
|
|
|
|
)
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2019-11-28 20:56:04 +00:00
|
|
|
|
|
|
|
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2019-11-28 20:56:04 +00:00
|
|
|
|
|
|
|
var obtained api.RunTimeRepresentation
|
|
|
|
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2019-11-28 20:56:04 +00:00
|
|
|
got, err := json.MarshalIndent(obtained, "", " ")
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2019-11-28 20:56:04 +00:00
|
|
|
|
|
|
|
expectedJSON := filepath.FromSlash("testdata/rawdata-consul.json")
|
|
|
|
|
|
|
|
if *updateExpected {
|
2021-03-04 19:08:03 +00:00
|
|
|
err = os.WriteFile(expectedJSON, got, 0o666)
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2019-11-28 20:56:04 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 19:08:03 +00:00
|
|
|
expected, err := os.ReadFile(expectedJSON)
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2019-11-28 20:56:04 +00:00
|
|
|
|
|
|
|
if !bytes.Equal(expected, got) {
|
|
|
|
diff := difflib.UnifiedDiff{
|
|
|
|
FromFile: "Expected",
|
|
|
|
A: difflib.SplitLines(string(expected)),
|
|
|
|
ToFile: "Got",
|
|
|
|
B: difflib.SplitLines(string(got)),
|
|
|
|
Context: 3,
|
|
|
|
}
|
|
|
|
|
|
|
|
text, err := difflib.GetUnifiedDiffString(diff)
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err, text)
|
2019-11-28 20:56:04 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-25 14:38:07 +00:00
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
func (s *ConsulSuite) assertWhoami(host string, expectedStatusCode int) {
|
2023-09-25 14:38:07 +00:00
|
|
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2023-09-25 14:38:07 +00:00
|
|
|
req.Host = host
|
|
|
|
|
|
|
|
resp, err := try.ResponseUntilStatusCode(req, 15*time.Second, expectedStatusCode)
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2023-09-25 14:38:07 +00:00
|
|
|
resp.Body.Close()
|
|
|
|
}
|
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
func (s *ConsulSuite) TestDeleteRootKey() {
|
2023-09-25 14:38:07 +00:00
|
|
|
// This test case reproduce the issue: https://github.com/traefik/traefik/issues/8092
|
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
file := s.adaptFile("fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL})
|
2023-09-25 14:38:07 +00:00
|
|
|
|
|
|
|
ctx := context.Background()
|
2024-01-09 16:00:07 +00:00
|
|
|
svcaddr := net.JoinHostPort(s.getComposeServiceIP("whoami"), "80")
|
2023-09-25 14:38:07 +00:00
|
|
|
|
|
|
|
data := map[string]string{
|
|
|
|
"traefik/http/routers/Router0/entryPoints/0": "web",
|
|
|
|
"traefik/http/routers/Router0/rule": "Host(`kv1.localhost`)",
|
|
|
|
"traefik/http/routers/Router0/service": "simplesvc0",
|
|
|
|
|
|
|
|
"traefik/http/routers/Router1/entryPoints/0": "web",
|
|
|
|
"traefik/http/routers/Router1/rule": "Host(`kv2.localhost`)",
|
|
|
|
"traefik/http/routers/Router1/service": "simplesvc1",
|
|
|
|
|
|
|
|
"traefik/http/services/simplesvc0/loadBalancer/servers/0/url": "http://" + svcaddr,
|
|
|
|
"traefik/http/services/simplesvc1/loadBalancer/servers/0/url": "http://" + svcaddr,
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range data {
|
|
|
|
err := s.kvClient.Put(ctx, k, []byte(v), nil)
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2023-09-25 14:38:07 +00:00
|
|
|
}
|
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
s.traefikCmd(withConfigFile(file))
|
2023-09-25 14:38:07 +00:00
|
|
|
|
|
|
|
// wait for traefik
|
2024-01-09 16:00:07 +00:00
|
|
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
2023-09-25 14:38:07 +00:00
|
|
|
try.BodyContains(`"Router0@consul":`, `"Router1@consul":`, `"simplesvc0@consul":`, `"simplesvc1@consul":`),
|
|
|
|
)
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
|
|
|
s.assertWhoami("kv1.localhost", http.StatusOK)
|
|
|
|
s.assertWhoami("kv2.localhost", http.StatusOK)
|
2023-09-25 14:38:07 +00:00
|
|
|
|
|
|
|
// delete router1
|
|
|
|
err = s.kvClient.DeleteTree(ctx, "traefik/http/routers/Router1")
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
|
|
|
s.assertWhoami("kv1.localhost", http.StatusOK)
|
|
|
|
s.assertWhoami("kv2.localhost", http.StatusNotFound)
|
2023-09-25 14:38:07 +00:00
|
|
|
|
|
|
|
// delete simple services and router0
|
|
|
|
err = s.kvClient.DeleteTree(ctx, "traefik")
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
|
|
|
s.assertWhoami("kv1.localhost", http.StatusNotFound)
|
|
|
|
s.assertWhoami("kv2.localhost", http.StatusNotFound)
|
2023-09-25 14:38:07 +00:00
|
|
|
}
|