2019-03-11 16:54:05 +03:00
package integration
import (
2019-06-11 16:12:04 +03:00
"bytes"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
2024-01-09 19:00:07 +03:00
"net"
2019-03-11 16:54:05 +03:00
"net/http"
"os"
"path/filepath"
2019-06-11 16:12:04 +03:00
"regexp"
2024-01-09 19:00:07 +03:00
"strings"
"testing"
2019-03-11 16:54:05 +03:00
"time"
2019-06-11 16:12:04 +03:00
"github.com/pmezard/go-difflib/difflib"
2022-11-21 20:36:05 +03:00
"github.com/rs/zerolog/log"
2024-01-09 19:00:07 +03:00
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
2023-02-03 17:24:05 +03:00
"github.com/traefik/traefik/v3/integration/try"
"github.com/traefik/traefik/v3/pkg/api"
2019-03-11 16:54:05 +03:00
)
2019-06-11 16:12:04 +03:00
var updateExpected = flag . Bool ( "update_expected" , false , "Update expected files in testdata" )
2020-05-11 13:06:07 +03:00
// K8sSuite tests suite.
2019-03-11 16:54:05 +03:00
type K8sSuite struct { BaseSuite }
2024-01-09 19:00:07 +03:00
func TestK8sSuite ( t * testing . T ) {
suite . Run ( t , new ( K8sSuite ) )
}
func ( s * K8sSuite ) SetupSuite ( ) {
s . BaseSuite . SetupSuite ( )
s . createComposeProject ( "k8s" )
s . composeUp ( )
2019-03-11 16:54:05 +03:00
2019-08-11 13:22:14 +03:00
abs , err := filepath . Abs ( "./fixtures/k8s/config.skip/kubeconfig.yaml" )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-03-11 16:54:05 +03:00
2020-02-24 18:06:05 +03:00
err = try . Do ( 60 * time . Second , func ( ) error {
2019-03-11 16:54:05 +03:00
_ , err := os . Stat ( abs )
return err
2020-02-24 18:06:05 +03:00
} )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
data , err := os . ReadFile ( abs )
require . NoError ( s . T ( ) , err )
content := strings . ReplaceAll ( string ( data ) , "https://server:6443" , fmt . Sprintf ( "https://%s" , net . JoinHostPort ( s . getComposeServiceIP ( "server" ) , "6443" ) ) )
err = os . WriteFile ( abs , [ ] byte ( content ) , 0 o644 )
require . NoError ( s . T ( ) , err )
2019-03-11 16:54:05 +03:00
err = os . Setenv ( "KUBECONFIG" , abs )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-03-14 17:56:06 +03:00
}
2024-01-09 19:00:07 +03:00
func ( s * K8sSuite ) TearDownSuite ( ) {
s . BaseSuite . TearDownSuite ( )
2019-03-14 17:56:06 +03:00
2019-08-11 13:22:14 +03: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 16:58:04 +03:00
"./fixtures/k8s/ccm.yaml" ,
2019-03-14 17:56:06 +03:00
}
2019-08-11 13:22:14 +03:00
for _ , filename := range generatedFiles {
2021-11-25 13:10:06 +03:00
if err := os . Remove ( filename ) ; err != nil {
2022-11-21 20:36:05 +03:00
log . Warn ( ) . Err ( err ) . Send ( )
2019-08-11 13:22:14 +03:00
}
2019-03-14 17:56:06 +03:00
}
}
2019-03-11 16:54:05 +03:00
2024-01-09 19:00:07 +03:00
func ( s * K8sSuite ) TestIngressConfiguration ( ) {
s . traefikCmd ( withConfigFile ( "fixtures/k8s_default.toml" ) )
2019-03-11 16:54:05 +03:00
2024-01-09 19:00:07 +03:00
s . testConfiguration ( "testdata/rawdata-ingress.json" , "8080" )
2019-03-14 17:56:06 +03:00
}
2024-01-09 19:00:07 +03:00
func ( s * K8sSuite ) TestIngressLabelSelector ( ) {
s . traefikCmd ( withConfigFile ( "fixtures/k8s_ingress_label_selector.toml" ) )
2020-11-20 02:18:04 +03:00
2024-01-09 19:00:07 +03:00
s . testConfiguration ( "testdata/rawdata-ingress-label-selector.json" , "8080" )
2020-11-20 02:18:04 +03:00
}
2024-01-09 19:00:07 +03:00
func ( s * K8sSuite ) TestCRDConfiguration ( ) {
s . traefikCmd ( withConfigFile ( "fixtures/k8s_crd.toml" ) )
2019-03-11 16:54:05 +03:00
2024-01-09 19:00:07 +03:00
s . testConfiguration ( "testdata/rawdata-crd.json" , "8000" )
2019-06-11 16:12:04 +03:00
}
2024-01-09 19:00:07 +03:00
func ( s * K8sSuite ) TestCRDLabelSelector ( ) {
s . traefikCmd ( withConfigFile ( "fixtures/k8s_crd_label_selector.toml" ) )
2020-11-20 02:18:04 +03:00
2024-01-09 19:00:07 +03:00
s . testConfiguration ( "testdata/rawdata-crd-label-selector.json" , "8000" )
2020-11-20 02:18:04 +03:00
}
2024-01-09 19:00:07 +03:00
func ( s * K8sSuite ) TestGatewayConfiguration ( ) {
s . traefikCmd ( withConfigFile ( "fixtures/k8s_gateway.toml" ) )
2020-12-15 18:40:05 +03:00
2024-01-09 19:00:07 +03:00
s . testConfiguration ( "testdata/rawdata-gateway.json" , "8080" )
2020-12-15 18:40:05 +03:00
}
2024-01-09 19:00:07 +03:00
func ( s * K8sSuite ) TestIngressclass ( ) {
s . traefikCmd ( withConfigFile ( "fixtures/k8s_ingressclass.toml" ) )
2021-03-02 23:34:03 +03:00
2024-01-09 19:00:07 +03:00
s . testConfiguration ( "testdata/rawdata-ingressclass.json" , "8080" )
2021-03-02 23:34:03 +03:00
}
2024-01-10 12:47:44 +03:00
func ( s * K8sSuite ) TestDisableIngressclassLookup ( ) {
s . traefikCmd ( withConfigFile ( "fixtures/k8s_ingressclass_disabled.toml" ) )
2022-12-22 18:30:05 +03:00
2024-01-10 12:47:44 +03:00
s . testConfiguration ( "testdata/rawdata-ingressclass-disabled.json" , "8080" )
2022-12-22 18:30:05 +03:00
}
2024-01-09 19:00:07 +03:00
func ( s * K8sSuite ) testConfiguration ( path , apiPort string ) {
2019-11-14 21:28:04 +03:00
err := try . GetRequest ( "http://127.0.0.1:" + apiPort + "/api/entrypoints" , 20 * time . Second , try . BodyContains ( ` "name":"web" ` ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-08-11 13:22:14 +03:00
2019-06-11 16:12:04 +03:00
expectedJSON := filepath . FromSlash ( path )
if * updateExpected {
fi , err := os . Create ( expectedJSON )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-11 16:12:04 +03:00
err = fi . Close ( )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-11 16:12:04 +03:00
}
var buf bytes . Buffer
2020-02-24 18:06:05 +03: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 16:54:05 +03:00
2019-06-11 16:12:04 +03:00
if ! * updateExpected {
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-11 16:12:04 +03:00
return
}
if err != nil {
2024-01-10 12:47:44 +03:00
log . Info ( ) . Msgf ( "In file update mode, got expected error: %v" , err )
2019-06-11 16:12:04 +03:00
}
var rtRepr api . RunTimeRepresentation
err = json . Unmarshal ( buf . Bytes ( ) , & rtRepr )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-03-11 16:54:05 +03:00
2019-06-11 16:12:04 +03:00
newJSON , err := json . MarshalIndent ( rtRepr , "" , "\t" )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-03-11 16:54:05 +03:00
2021-03-04 22:08:03 +03:00
err = os . WriteFile ( expectedJSON , newJSON , 0 o644 )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
s . T ( ) . Fatal ( "We do not want a passing test in file update mode" )
2019-06-11 16:12:04 +03:00
}
func matchesConfig ( wantConfig string , buf * bytes . Buffer ) try . ResponseCondition {
return func ( res * http . Response ) error {
2021-03-04 22:08:03 +03:00
body , err := io . ReadAll ( res . Body )
2019-06-11 16:12:04 +03:00
if err != nil {
2020-05-11 13:06:07 +03:00
return fmt . Errorf ( "failed to read response body: %w" , err )
2019-06-11 16:12:04 +03: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 22:08:03 +03:00
expected , err := os . ReadFile ( wantConfig )
2019-06-11 16:12:04 +03: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+(".*") ` )
2023-03-21 14:00:46 +03:00
sanitizedExpected := rxURL . ReplaceAll ( bytes . TrimSpace ( expected ) , [ ] byte ( ` "$1": "XXXX" ` ) )
2019-06-11 16:12:04 +03:00
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 16:54:05 +03:00
}