2019-03-11 13:54:05 +00:00
|
|
|
package integration
|
|
|
|
|
|
|
|
import (
|
2019-06-11 13:12:04 +00:00
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2019-03-11 13:54:05 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2019-06-11 13:12:04 +00:00
|
|
|
"regexp"
|
2019-03-11 13:54:05 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-check/check"
|
2019-06-11 13:12:04 +00:00
|
|
|
"github.com/pmezard/go-difflib/difflib"
|
2022-11-21 17:36:05 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2023-02-03 14:24:05 +00:00
|
|
|
"github.com/traefik/traefik/v3/integration/try"
|
|
|
|
"github.com/traefik/traefik/v3/pkg/api"
|
2019-03-11 13:54:05 +00:00
|
|
|
checker "github.com/vdemeester/shakers"
|
|
|
|
)
|
|
|
|
|
2019-06-11 13:12:04 +00:00
|
|
|
var updateExpected = flag.Bool("update_expected", false, "Update expected files in testdata")
|
|
|
|
|
2020-05-11 10:06:07 +00:00
|
|
|
// K8sSuite tests suite.
|
2019-03-11 13:54:05 +00:00
|
|
|
type K8sSuite struct{ BaseSuite }
|
|
|
|
|
|
|
|
func (s *K8sSuite) SetUpSuite(c *check.C) {
|
|
|
|
s.createComposeProject(c, "k8s")
|
2021-11-25 10:10:06 +00:00
|
|
|
s.composeUp(c)
|
2019-03-11 13:54:05 +00:00
|
|
|
|
2019-08-11 10:22:14 +00:00
|
|
|
abs, err := filepath.Abs("./fixtures/k8s/config.skip/kubeconfig.yaml")
|
2019-03-11 13:54:05 +00:00
|
|
|
c.Assert(err, checker.IsNil)
|
|
|
|
|
2020-02-24 15:06:05 +00:00
|
|
|
err = try.Do(60*time.Second, func() error {
|
2019-03-11 13:54:05 +00:00
|
|
|
_, err := os.Stat(abs)
|
|
|
|
return err
|
2020-02-24 15:06:05 +00:00
|
|
|
})
|
2019-03-11 13:54:05 +00:00
|
|
|
c.Assert(err, checker.IsNil)
|
|
|
|
|
|
|
|
err = os.Setenv("KUBECONFIG", abs)
|
|
|
|
c.Assert(err, checker.IsNil)
|
2019-03-14 14:56:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *K8sSuite) TearDownSuite(c *check.C) {
|
2021-11-25 10:10:06 +00:00
|
|
|
s.composeDown(c)
|
2019-03-14 14:56:06 +00:00
|
|
|
|
2019-08-11 10:22:14 +00:00
|
|
|
generatedFiles := []string{
|
|
|
|
"./fixtures/k8s/config.skip/kubeconfig.yaml",
|
|
|
|
"./fixtures/k8s/config.skip/k3s.log",
|
|
|
|
"./fixtures/k8s/coredns.yaml",
|
|
|
|
"./fixtures/k8s/rolebindings.yaml",
|
|
|
|
"./fixtures/k8s/traefik.yaml",
|
2020-12-10 13:58:04 +00:00
|
|
|
"./fixtures/k8s/ccm.yaml",
|
2019-03-14 14:56:06 +00:00
|
|
|
}
|
2019-08-11 10:22:14 +00:00
|
|
|
|
|
|
|
for _, filename := range generatedFiles {
|
2021-11-25 10:10:06 +00:00
|
|
|
if err := os.Remove(filename); err != nil {
|
2022-11-21 17:36:05 +00:00
|
|
|
log.Warn().Err(err).Send()
|
2019-08-11 10:22:14 +00:00
|
|
|
}
|
2019-03-14 14:56:06 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-11 13:54:05 +00:00
|
|
|
|
2019-06-11 13:12:04 +00:00
|
|
|
func (s *K8sSuite) TestIngressConfiguration(c *check.C) {
|
2019-03-11 13:54:05 +00:00
|
|
|
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_default.toml"))
|
|
|
|
defer display(c)
|
|
|
|
|
2019-03-14 14:56:06 +00:00
|
|
|
err := cmd.Start()
|
2019-03-11 13:54:05 +00:00
|
|
|
c.Assert(err, checker.IsNil)
|
2020-10-09 07:32:03 +00:00
|
|
|
defer s.killCmd(cmd)
|
2019-03-11 13:54:05 +00:00
|
|
|
|
2019-11-14 18:28:04 +00:00
|
|
|
testConfiguration(c, "testdata/rawdata-ingress.json", "8080")
|
2019-03-14 14:56:06 +00:00
|
|
|
}
|
|
|
|
|
2020-11-19 23:18:04 +00:00
|
|
|
func (s *K8sSuite) TestIngressLabelSelector(c *check.C) {
|
|
|
|
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingress_label_selector.toml"))
|
|
|
|
defer display(c)
|
|
|
|
|
|
|
|
err := cmd.Start()
|
|
|
|
c.Assert(err, checker.IsNil)
|
|
|
|
defer s.killCmd(cmd)
|
|
|
|
|
|
|
|
testConfiguration(c, "testdata/rawdata-ingress-label-selector.json", "8080")
|
|
|
|
}
|
|
|
|
|
2019-06-11 13:12:04 +00:00
|
|
|
func (s *K8sSuite) TestCRDConfiguration(c *check.C) {
|
2019-03-14 14:56:06 +00:00
|
|
|
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd.toml"))
|
|
|
|
defer display(c)
|
2019-03-11 13:54:05 +00:00
|
|
|
|
2019-03-14 14:56:06 +00:00
|
|
|
err := cmd.Start()
|
2019-03-11 13:54:05 +00:00
|
|
|
c.Assert(err, checker.IsNil)
|
2020-10-09 07:32:03 +00:00
|
|
|
defer s.killCmd(cmd)
|
2019-03-11 13:54:05 +00:00
|
|
|
|
2019-11-14 18:28:04 +00:00
|
|
|
testConfiguration(c, "testdata/rawdata-crd.json", "8000")
|
2019-06-11 13:12:04 +00:00
|
|
|
}
|
|
|
|
|
2020-11-19 23:18:04 +00:00
|
|
|
func (s *K8sSuite) TestCRDLabelSelector(c *check.C) {
|
|
|
|
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd_label_selector.toml"))
|
|
|
|
defer display(c)
|
|
|
|
|
|
|
|
err := cmd.Start()
|
|
|
|
c.Assert(err, checker.IsNil)
|
|
|
|
defer s.killCmd(cmd)
|
|
|
|
|
|
|
|
testConfiguration(c, "testdata/rawdata-crd-label-selector.json", "8000")
|
|
|
|
}
|
|
|
|
|
2020-12-15 15:40:05 +00:00
|
|
|
func (s *K8sSuite) TestGatewayConfiguration(c *check.C) {
|
|
|
|
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_gateway.toml"))
|
|
|
|
defer display(c)
|
|
|
|
|
|
|
|
err := cmd.Start()
|
|
|
|
c.Assert(err, checker.IsNil)
|
|
|
|
defer s.killCmd(cmd)
|
|
|
|
|
|
|
|
testConfiguration(c, "testdata/rawdata-gateway.json", "8080")
|
|
|
|
}
|
|
|
|
|
2021-03-02 20:34:03 +00:00
|
|
|
func (s *K8sSuite) TestIngressclass(c *check.C) {
|
|
|
|
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingressclass.toml"))
|
|
|
|
defer display(c)
|
|
|
|
|
|
|
|
err := cmd.Start()
|
|
|
|
c.Assert(err, checker.IsNil)
|
|
|
|
defer s.killCmd(cmd)
|
|
|
|
|
|
|
|
testConfiguration(c, "testdata/rawdata-ingressclass.json", "8080")
|
|
|
|
}
|
|
|
|
|
2022-12-22 15:30:05 +00:00
|
|
|
func (s *K8sSuite) TestDisableIngressclassLookup(c *check.C) {
|
|
|
|
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingressclass_disabled.toml"))
|
|
|
|
defer display(c)
|
|
|
|
|
|
|
|
err := cmd.Start()
|
|
|
|
c.Assert(err, checker.IsNil)
|
|
|
|
defer s.killCmd(cmd)
|
|
|
|
|
|
|
|
testConfiguration(c, "testdata/rawdata-ingressclass-disabled.json", "8080")
|
|
|
|
}
|
|
|
|
|
2019-11-14 18:28:04 +00:00
|
|
|
func testConfiguration(c *check.C, path, apiPort string) {
|
|
|
|
err := try.GetRequest("http://127.0.0.1:"+apiPort+"/api/entrypoints", 20*time.Second, try.BodyContains(`"name":"web"`))
|
2019-08-11 10:22:14 +00:00
|
|
|
c.Assert(err, checker.IsNil)
|
|
|
|
|
2019-06-11 13:12:04 +00:00
|
|
|
expectedJSON := filepath.FromSlash(path)
|
|
|
|
|
|
|
|
if *updateExpected {
|
|
|
|
fi, err := os.Create(expectedJSON)
|
|
|
|
c.Assert(err, checker.IsNil)
|
|
|
|
err = fi.Close()
|
|
|
|
c.Assert(err, checker.IsNil)
|
|
|
|
}
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
2020-02-24 15:06:05 +00:00
|
|
|
err = try.GetRequest("http://127.0.0.1:"+apiPort+"/api/rawdata", 1*time.Minute, try.StatusCodeIs(http.StatusOK), matchesConfig(expectedJSON, &buf))
|
2019-03-11 13:54:05 +00:00
|
|
|
|
2019-06-11 13:12:04 +00:00
|
|
|
if !*updateExpected {
|
|
|
|
if err != nil {
|
|
|
|
c.Error(err)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
c.Logf("In file update mode, got expected error: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var rtRepr api.RunTimeRepresentation
|
|
|
|
err = json.Unmarshal(buf.Bytes(), &rtRepr)
|
2019-03-14 14:56:06 +00:00
|
|
|
c.Assert(err, checker.IsNil)
|
2019-03-11 13:54:05 +00:00
|
|
|
|
2019-06-11 13:12:04 +00:00
|
|
|
newJSON, err := json.MarshalIndent(rtRepr, "", "\t")
|
2019-03-14 14:56:06 +00:00
|
|
|
c.Assert(err, checker.IsNil)
|
2019-03-11 13:54:05 +00:00
|
|
|
|
2021-03-04 19:08:03 +00:00
|
|
|
err = os.WriteFile(expectedJSON, newJSON, 0o644)
|
2019-03-11 13:54:05 +00:00
|
|
|
c.Assert(err, checker.IsNil)
|
2019-06-11 13:12:04 +00:00
|
|
|
c.Errorf("We do not want a passing test in file update mode")
|
|
|
|
}
|
|
|
|
|
|
|
|
func matchesConfig(wantConfig string, buf *bytes.Buffer) try.ResponseCondition {
|
|
|
|
return func(res *http.Response) error {
|
2021-03-04 19:08:03 +00:00
|
|
|
body, err := io.ReadAll(res.Body)
|
2019-06-11 13:12:04 +00:00
|
|
|
if err != nil {
|
2020-05-11 10:06:07 +00:00
|
|
|
return fmt.Errorf("failed to read response body: %w", err)
|
2019-06-11 13:12:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := res.Body.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var obtained api.RunTimeRepresentation
|
|
|
|
err = json.Unmarshal(body, &obtained)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if buf != nil {
|
|
|
|
buf.Reset()
|
|
|
|
if _, err := io.Copy(buf, bytes.NewReader(body)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
got, err := json.MarshalIndent(obtained, "", "\t")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-03-04 19:08:03 +00:00
|
|
|
expected, err := os.ReadFile(wantConfig)
|
2019-06-11 13:12:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// The pods IPs are dynamic, so we cannot predict them,
|
|
|
|
// which is why we have to ignore them in the comparison.
|
|
|
|
rxURL := regexp.MustCompile(`"(url|address)":\s+(".*")`)
|
|
|
|
sanitizedExpected := rxURL.ReplaceAll(expected, []byte(`"$1": "XXXX"`))
|
|
|
|
sanitizedGot := rxURL.ReplaceAll(got, []byte(`"$1": "XXXX"`))
|
|
|
|
|
|
|
|
rxServerStatus := regexp.MustCompile(`"http://.*?":\s+(".*")`)
|
|
|
|
sanitizedExpected = rxServerStatus.ReplaceAll(sanitizedExpected, []byte(`"http://XXXX": $1`))
|
|
|
|
sanitizedGot = rxServerStatus.ReplaceAll(sanitizedGot, []byte(`"http://XXXX": $1`))
|
|
|
|
|
|
|
|
if bytes.Equal(sanitizedExpected, sanitizedGot) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
diff := difflib.UnifiedDiff{
|
|
|
|
FromFile: "Expected",
|
|
|
|
A: difflib.SplitLines(string(sanitizedExpected)),
|
|
|
|
ToFile: "Got",
|
|
|
|
B: difflib.SplitLines(string(sanitizedGot)),
|
|
|
|
Context: 3,
|
|
|
|
}
|
|
|
|
|
|
|
|
text, err := difflib.GetUnifiedDiffString(diff)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return errors.New(text)
|
|
|
|
}
|
2019-03-11 13:54:05 +00:00
|
|
|
}
|