From 2e5c006b9a063969e4cf53e1d623adcf8832d32c Mon Sep 17 00:00:00 2001 From: Mojtaba Date: Mon, 13 May 2024 12:53:50 +0200 Subject: [PATCH 1/9] chore: prepare for merging main into this branch --- e2e/basic/bittwister_test.go | 2 +- e2e/basic/reverse_proxy_test.go | 72 +++++++++++ e2e/celestia_app/main_test.go | 2 +- go.mod | 20 +-- go.sum | 23 ++++ pkg/k8s/errors.go | 143 +++++++++++---------- pkg/k8s/k8s.go | 4 + pkg/k8s/k8s_pod.go | 26 +++- pkg/k8s/k8s_replicaset.go | 7 +- pkg/k8s/k8s_role.go | 27 ++++ pkg/k8s/k8s_rolebinding.go | 39 ++++++ pkg/k8s/k8s_service.go | 91 ++++++++++++++ pkg/knuu/bittwister.go | 8 +- pkg/knuu/errors.go | 2 + pkg/knuu/instance_helper.go | 38 +++++- pkg/knuu/knuu.go | 23 +++- pkg/traefik/errors.go | 46 +++++++ pkg/traefik/traefik.go | 213 ++++++++++++++++++++++++++++++++ 18 files changed, 689 insertions(+), 97 deletions(-) create mode 100644 e2e/basic/reverse_proxy_test.go create mode 100644 pkg/traefik/errors.go create mode 100644 pkg/traefik/traefik.go diff --git a/e2e/basic/bittwister_test.go b/e2e/basic/bittwister_test.go index 956a392..9b8d10f 100644 --- a/e2e/basic/bittwister_test.go +++ b/e2e/basic/bittwister_test.go @@ -600,6 +600,6 @@ func forwardBitTwisterPort(t *testing.T, i *knuu.Instance) { fwdBtPort, err := i.PortForwardTCP(i.BitTwister.Port()) require.NoError(t, err, "Error port forwarding") i.BitTwister.SetPort(fwdBtPort) - i.BitTwister.SetNewClientByIPAddr("http://localhost") + i.BitTwister.SetNewClientByURL("http://localhost") t.Logf("BitTwister listening on http://localhost:%d", fwdBtPort) } diff --git a/e2e/basic/reverse_proxy_test.go b/e2e/basic/reverse_proxy_test.go new file mode 100644 index 0000000..312c3dd --- /dev/null +++ b/e2e/basic/reverse_proxy_test.go @@ -0,0 +1,72 @@ +package basic + +import ( + "context" + "fmt" + "os" + "testing" + "time" + + "github.com/celestiaorg/knuu/pkg/knuu" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestReverseProxy(t *testing.T) { + t.Parallel() + // Setup + + main, err := knuu.NewInstance("main") + require.NoError(t, err, "Error creating instance") + + err = main.SetImage("alpine:latest") + require.NoError(t, err, "Error setting image") + + err = main.SetCommand("sleep", "infinite") + require.NoError(t, err, "Error executing command") + + require.NoError(t, main.Commit(), "Error committing instance") + + t.Cleanup(func() { + if os.Getenv("KNUU_SKIP_CLEANUP") == "true" { + t.Log("Skipping cleanup") + return + } + + require.NoError(t, main.Destroy(), "Error destroying instance") + }) + + // Prepare iperf client & server + + require.NoError(t, main.EnableBitTwister(), "Error enabling BitTwister") + require.NoError(t, main.Start(), "Error starting main instance") + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + + { + out, err := main.BitTwister.Client().AllServicesStatus() + fmt.Printf("err: %v\n", err) + time.Sleep(10 * time.Minute) + require.NoError(t, err, "Error getting all services status") + assert.NotEmpty(t, out, "No services found") + } + require.NoError(t, main.BitTwister.WaitForStart(ctx), "Error waiting for BitTwister to start") + + // test if BitTwister running in a sidecar is accessible + + err = main.SetBandwidthLimit(1000) + assert.NoError(t, err, "Error setting bandwidth limit") + + // Check if the BitTwister service is set + out, err := main.BitTwister.Client().AllServicesStatus() + require.NoError(t, err, "Error getting all services status") + assert.NotEmpty(t, out, "No services found") + + for _, svc := range out { + fmt.Printf("\nsvc: %#v\n", svc) + } +} + +// reset && LOG_LEVEL=debug go test -v ./e2e/basic/ --run TestReverseProxy diff --git a/e2e/celestia_app/main_test.go b/e2e/celestia_app/main_test.go index 8f2b092..203f5ff 100644 --- a/e2e/celestia_app/main_test.go +++ b/e2e/celestia_app/main_test.go @@ -104,6 +104,6 @@ func forwardBitTwisterPort(t *testing.T, i *knuu.Instance) { fwdBtPort, err := i.PortForwardTCP(i.BitTwister.Port()) require.NoError(t, err, "Error port forwarding") i.BitTwister.SetPort(fwdBtPort) - i.BitTwister.SetNewClientByIPAddr("http://localhost") + i.BitTwister.SetNewClientByURL("http://localhost") t.Logf("BitTwister listening on http://localhost:%d", fwdBtPort) } diff --git a/go.mod b/go.mod index b9d388e..a9fe951 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/Workiva/go-datastructures v1.0.53 // indirect + github.com/acroca/go-symbols v0.1.1 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/aws/aws-sdk-go v1.44.122 // indirect github.com/benbjohnson/clock v1.3.5 // indirect @@ -260,6 +261,7 @@ require ( github.com/multiformats/go-varint v0.0.7 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/nsf/gocode v0.0.0-20230322162601-b672b49f3818 // indirect github.com/onsi/ginkgo/v2 v2.15.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect @@ -280,6 +282,7 @@ require ( github.com/quic-go/quic-go v0.42.0 // indirect github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/rakyll/statik v0.1.7 // indirect + github.com/ramya-rao-a/go-outline v0.0.0-20210608161538-9736a4bde949 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/regen-network/cosmos-proto v0.3.1 // indirect @@ -325,17 +328,18 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.21.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect - golang.org/x/mod v0.15.0 // indirect - golang.org/x/net v0.23.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.18.0 // indirect + golang.org/x/tools v0.21.0 // indirect + golang.org/x/tools/cmd/guru v0.1.1-deprecated // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect gonum.org/v1/gonum v0.14.0 // indirect google.golang.org/api v0.155.0 // indirect diff --git a/go.sum b/go.sum index 1e05fe1..32afa68 100644 --- a/go.sum +++ b/go.sum @@ -253,6 +253,8 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/ github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig= github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6/go.mod h1:eSYp2T6f0apnuW8TzhV3f6Aff2SE8Dwio++U4ha4yEM= +github.com/acroca/go-symbols v0.1.1 h1:q3IzaMNYocw/Bnc2a8jkXf0hM3+POfLoq30x8HYuaPE= +github.com/acroca/go-symbols v0.1.1/go.mod h1:RKAIDWtcELAw6/wjNJGWRYZ7QEinSWoJeJ2H5cfK6AM= github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= @@ -1938,6 +1940,8 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/neilotoole/errgroup v0.1.6/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nsf/gocode v0.0.0-20230322162601-b672b49f3818 h1:btvxUuer0DCdhu/N5fvMxW759ASqzIsm6cF8D23TNYs= +github.com/nsf/gocode v0.0.0-20230322162601-b672b49f3818/go.mod h1:6Q8/OMaaKAgTX7/jt2bOXVDrm1eJhoNd+iwzghR7jvs= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -2112,6 +2116,8 @@ github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFD github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= +github.com/ramya-rao-a/go-outline v0.0.0-20210608161538-9736a4bde949 h1:iaD+iVf9xGfajsJp+zYrg9Lrk6gMJ6/hZHO4cYq5D5o= +github.com/ramya-rao-a/go-outline v0.0.0-20210608161538-9736a4bde949/go.mod h1:9V3eNbj9Z53yO7cKB6cSX9f0O7rYdIiuGBhjA1YsQuw= github.com/raulk/clock v1.1.0/go.mod h1:3MpVxdZ/ODBQDxbN+kzshf5OSZwPjtMDx6BBXBmOeY0= github.com/raulk/go-watchdog v1.2.0/go.mod h1:lzSbAl5sh4rtI8tYHU01BWIDzgzqaQLj6RcA1i4mlqI= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= @@ -2530,6 +2536,8 @@ golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2591,6 +2599,8 @@ golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2682,6 +2692,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2729,6 +2741,7 @@ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2878,6 +2891,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2888,6 +2903,8 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2904,6 +2921,8 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2990,6 +3009,10 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools/cmd/guru v0.1.1-deprecated h1:WiL3pQGXG71u4N45C0eRkE2IcEMAiQdDZ2H5lGspNjM= +golang.org/x/tools/cmd/guru v0.1.1-deprecated/go.mod h1:yFb7vixnH8+ByFZ63niwlvUUxyTE/6ULZ6AiEHZwlTk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/k8s/errors.go b/pkg/k8s/errors.go index e08e282..6a1cf9d 100644 --- a/pkg/k8s/errors.go +++ b/pkg/k8s/errors.go @@ -30,70 +30,81 @@ func (e *Error) WithParams(params ...interface{}) *Error { } var ( - ErrKnuuNotInitialized = &Error{Code: "KnuuNotInitialized", Message: "knuu is not initialized"} - ErrGettingConfigmap = &Error{Code: "ErrorGettingConfigmap", Message: "error getting configmap %s"} - ErrConfigmapAlreadyExists = &Error{Code: "ConfigmapAlreadyExists", Message: "configmap %s already exists"} - ErrCreatingConfigmap = &Error{Code: "ErrorCreatingConfigmap", Message: "error creating configmap %s"} - ErrConfigmapDoesNotExist = &Error{Code: "ConfigmapDoesNotExist", Message: "configmap %s does not exist"} - ErrDeletingConfigmap = &Error{Code: "ErrorDeletingConfigmap", Message: "error deleting configmap %s"} - ErrGettingDaemonset = &Error{Code: "ErrorGettingDaemonset", Message: "error getting daemonset %s"} - ErrCreatingDaemonset = &Error{Code: "ErrorCreatingDaemonset", Message: "error creating daemonset %s"} - ErrUpdatingDaemonset = &Error{Code: "ErrorUpdatingDaemonset", Message: "error updating daemonset %s"} - ErrDeletingDaemonset = &Error{Code: "ErrorDeletingDaemonset", Message: "error deleting daemonset %s"} - ErrCreatingNamespace = &Error{Code: "ErrorCreatingNamespace", Message: "error creating namespace %s"} - ErrDeletingNamespace = &Error{Code: "ErrorDeletingNamespace", Message: "error deleting namespace %s"} - ErrGettingNamespace = &Error{Code: "ErrorGettingNamespace", Message: "error getting namespace %s"} - ErrCreatingNetworkPolicy = &Error{Code: "ErrorCreatingNetworkPolicy", Message: "error creating network policy %s"} - ErrDeletingNetworkPolicy = &Error{Code: "ErrorDeletingNetworkPolicy", Message: "error deleting network policy %s"} - ErrGettingNetworkPolicy = &Error{Code: "ErrorGettingNetworkPolicy", Message: "error getting network policy %s"} - ErrGettingPod = &Error{Code: "ErrorGettingPod", Message: "failed to get pod %s"} - ErrPreparingPod = &Error{Code: "ErrorPreparingPod", Message: "error preparing pod"} - ErrCreatingPod = &Error{Code: "ErrorCreatingPod", Message: "failed to create pod"} - ErrDeletingPod = &Error{Code: "ErrorDeletingPod", Message: "failed to delete pod"} - ErrDeployingPod = &Error{Code: "ErrorDeployingPod", Message: "failed to deploy pod"} - ErrGettingK8sConfig = &Error{Code: "ErrorGettingK8sConfig", Message: "failed to get k8s config"} - ErrCreatingExecutor = &Error{Code: "ErrorCreatingExecutor", Message: "failed to create Executor"} - ErrExecutingCommand = &Error{Code: "ErrorExecutingCommand", Message: "failed to execute command"} - ErrCommandExecution = &Error{Code: "ErrorCommandExecution", Message: "error while executing command"} - ErrDeletingPodFailed = &Error{Code: "ErrorDeletingPodFailed", Message: "failed to delete pod %s"} - ErrParsingMemoryRequest = &Error{Code: "ErrorParsingMemoryRequest", Message: "failed to parse memory request quantity '%s'"} - ErrParsingMemoryLimit = &Error{Code: "ErrorParsingMemoryLimit", Message: "failed to parse memory limit quantity '%s'"} - ErrParsingCPURequest = &Error{Code: "ErrorParsingCPURequest", Message: "failed to parse CPU request quantity '%s'"} - ErrBuildingContainerVolumes = &Error{Code: "ErrorBuildingContainerVolumes", Message: "failed to build container volumes"} - ErrBuildingResources = &Error{Code: "ErrorBuildingResources", Message: "failed to build resources"} - ErrBuildingInitContainerVolumes = &Error{Code: "ErrorBuildingInitContainerVolumes", Message: "failed to build init container volumes"} - ErrBuildingInitContainerCommand = &Error{Code: "ErrorBuildingInitContainerCommand", Message: "failed to build init container command"} - ErrBuildingPodVolumes = &Error{Code: "ErrorBuildingPodVolumes", Message: "failed to build pod volumes"} - ErrPreparingMainContainer = &Error{Code: "ErrorPreparingMainContainer", Message: "failed to prepare main container"} - ErrPreparingInitContainer = &Error{Code: "ErrorPreparingInitContainer", Message: "failed to prepare init container"} - ErrPreparingPodVolumes = &Error{Code: "ErrorPreparingPodVolumes", Message: "failed to prepare pod volumes"} - ErrPreparingSidecarContainer = &Error{Code: "ErrorPreparingSidecarContainer", Message: "failed to prepare sidecar container"} - ErrPreparingSidecarVolumes = &Error{Code: "ErrorPreparingSidecarVolumes", Message: "failed to prepare sidecar volumes"} - ErrCreatingPodSpec = &Error{Code: "ErrorCreatingPodSpec", Message: "failed to create pod spec"} - ErrGettingClusterConfig = &Error{Code: "ErrorGettingClusterConfig", Message: "failed to get cluster config"} - ErrCreatingRoundTripper = &Error{Code: "ErrorCreatingRoundTripper", Message: "failed to create round tripper"} - ErrCreatingPortForwarder = &Error{Code: "ErrorCreatingPortForwarder", Message: "failed to create port forwarder"} - ErrPortForwarding = &Error{Code: "ErrorPortForwarding", Message: "failed to port forward: %v"} - ErrForwardingPorts = &Error{Code: "ErrorForwardingPorts", Message: "error forwarding ports"} - ErrPortForwardingTimeout = &Error{Code: "ErrorPortForwardingTimeout", Message: "timed out waiting for port forwarding to be ready"} - ErrDeletingPersistentVolumeClaim = &Error{Code: "ErrorDeletingPersistentVolumeClaim", Message: "error deleting PersistentVolumeClaim %s"} - ErrCreatingPersistentVolumeClaim = &Error{Code: "ErrorCreatingPersistentVolumeClaim", Message: "error creating PersistentVolumeClaim"} - ErrGettingReplicaSet = &Error{Code: "ErrorGettingReplicaSet", Message: "failed to get ReplicaSet %s"} - ErrCreatingReplicaSet = &Error{Code: "ErrorCreatingReplicaSet", Message: "failed to create ReplicaSet"} - ErrDeletingReplicaSet = &Error{Code: "ErrorDeletingReplicaSet", Message: "failed to delete ReplicaSet %s"} - ErrWaitingForReplicaSet = &Error{Code: "ErrorWaitingForReplicaSet", Message: "error waiting for ReplicaSet to delete"} - ErrDeployingReplicaSet = &Error{Code: "ErrorDeployingReplicaSet", Message: "failed to deploy ReplicaSet"} - ErrPreparingPodSpec = &Error{Code: "ErrorPreparingPodSpec", Message: "failed to prepare pod spec"} - ErrListingPodsForReplicaSet = &Error{Code: "ErrorListingPodsForReplicaSet", Message: "failed to list pods for ReplicaSet %s"} - ErrNoPodsForReplicaSet = &Error{Code: "NoPodsForReplicaSet", Message: "no pods found for ReplicaSet %s"} - ErrGettingService = &Error{Code: "ErrorGettingService", Message: "error getting service %s"} - ErrPreparingService = &Error{Code: "ErrorPreparingService", Message: "error preparing service %s"} - ErrCreatingService = &Error{Code: "ErrorCreatingService", Message: "error creating service %s"} - ErrPatchingService = &Error{Code: "ErrorPatchingService", Message: "error patching service %s"} - ErrDeletingService = &Error{Code: "ErrorDeletingService", Message: "error deleting service %s"} - ErrNamespaceRequired = &Error{Code: "NamespaceRequired", Message: "namespace is required"} - ErrServiceNameRequired = &Error{Code: "ServiceNameRequired", Message: "service name is required"} - ErrNoPortsSpecified = &Error{Code: "NoPortsSpecified", Message: "no ports specified for service %s"} - ErrRetrievingKubernetesConfig = &Error{Code: "RetrievingKubernetesConfig", Message: "retrieving the Kubernetes config"} - ErrCreatingClientset = &Error{Code: "CreatingClientset", Message: "creating clientset for Kubernetes"} + ErrBuildingContainerVolumes = &Error{Code: "ErrorBuildingContainerVolumes", Message: "failed to build container volumes"} + ErrBuildingInitContainerCommand = &Error{Code: "ErrorBuildingInitContainerCommand", Message: "failed to build init container command"} + ErrBuildingInitContainerVolumes = &Error{Code: "ErrorBuildingInitContainerVolumes", Message: "failed to build init container volumes"} + ErrBuildingPodVolumes = &Error{Code: "ErrorBuildingPodVolumes", Message: "failed to build pod volumes"} + ErrBuildingResources = &Error{Code: "ErrorBuildingResources", Message: "failed to build resources"} + ErrCommandExecution = &Error{Code: "ErrorCommandExecution", Message: "error while executing command"} + ErrConfigmapAlreadyExists = &Error{Code: "ConfigmapAlreadyExists", Message: "configmap %s already exists"} + ErrConfigmapDoesNotExist = &Error{Code: "ConfigmapDoesNotExist", Message: "configmap %s does not exist"} + ErrCreatingClientset = &Error{Code: "CreatingClientset", Message: "creating clientset for Kubernetes"} + ErrCreatingConfigmap = &Error{Code: "ErrorCreatingConfigmap", Message: "error creating configmap %s"} + ErrCreatingDaemonset = &Error{Code: "ErrorCreatingDaemonset", Message: "error creating daemonset %s"} + ErrCreatingExecutor = &Error{Code: "ErrorCreatingExecutor", Message: "failed to create Executor"} + ErrCreatingNamespace = &Error{Code: "ErrorCreatingNamespace", Message: "error creating namespace %s"} + ErrCreatingNetworkPolicy = &Error{Code: "ErrorCreatingNetworkPolicy", Message: "error creating network policy %s"} + ErrCreatingPersistentVolumeClaim = &Error{Code: "ErrorCreatingPersistentVolumeClaim", Message: "error creating PersistentVolumeClaim"} + ErrCreatingPod = &Error{Code: "ErrorCreatingPod", Message: "failed to create pod"} + ErrCreatingPodSpec = &Error{Code: "ErrorCreatingPodSpec", Message: "failed to create pod spec"} + ErrCreatingPortForwarder = &Error{Code: "ErrorCreatingPortForwarder", Message: "failed to create port forwarder"} + ErrCreatingReplicaSet = &Error{Code: "ErrorCreatingReplicaSet", Message: "failed to create ReplicaSet"} + ErrCreatingRoundTripper = &Error{Code: "ErrorCreatingRoundTripper", Message: "failed to create round tripper"} + ErrCreatingService = &Error{Code: "ErrorCreatingService", Message: "error creating service %s"} + ErrDeletingConfigmap = &Error{Code: "ErrorDeletingConfigmap", Message: "error deleting configmap %s"} + ErrDeletingDaemonset = &Error{Code: "ErrorDeletingDaemonset", Message: "error deleting daemonset %s"} + ErrDeletingNamespace = &Error{Code: "ErrorDeletingNamespace", Message: "error deleting namespace %s"} + ErrDeletingNetworkPolicy = &Error{Code: "ErrorDeletingNetworkPolicy", Message: "error deleting network policy %s"} + ErrDeletingPersistentVolumeClaim = &Error{Code: "ErrorDeletingPersistentVolumeClaim", Message: "error deleting PersistentVolumeClaim %s"} + ErrDeletingPod = &Error{Code: "ErrorDeletingPod", Message: "failed to delete pod"} + ErrDeletingPodFailed = &Error{Code: "ErrorDeletingPodFailed", Message: "failed to delete pod %s"} + ErrDeletingReplicaSet = &Error{Code: "ErrorDeletingReplicaSet", Message: "failed to delete ReplicaSet %s"} + ErrDeletingService = &Error{Code: "ErrorDeletingService", Message: "error deleting service %s"} + ErrDeployingPod = &Error{Code: "ErrorDeployingPod", Message: "failed to deploy pod"} + ErrDeployingReplicaSet = &Error{Code: "ErrorDeployingReplicaSet", Message: "failed to deploy ReplicaSet"} + ErrExecutingCommand = &Error{Code: "ErrorExecutingCommand", Message: "failed to execute command"} + ErrExternalIPsNotSet = &Error{Code: "ExternalIPsNotSet", Message: "external IPs not set for service %s"} + ErrFailedToConnect = &Error{Code: "FailedToConnect", Message: "failed to connect to %s"} + ErrForwardingPorts = &Error{Code: "ErrorForwardingPorts", Message: "error forwarding ports"} + ErrGettingClusterConfig = &Error{Code: "ErrorGettingClusterConfig", Message: "failed to get cluster config"} + ErrGettingConfigmap = &Error{Code: "ErrorGettingConfigmap", Message: "error getting configmap %s"} + ErrGettingDaemonset = &Error{Code: "ErrorGettingDaemonset", Message: "error getting daemonset %s"} + ErrGettingK8sConfig = &Error{Code: "ErrorGettingK8sConfig", Message: "failed to get k8s config"} + ErrGettingNamespace = &Error{Code: "ErrorGettingNamespace", Message: "error getting namespace %s"} + ErrGettingNetworkPolicy = &Error{Code: "ErrorGettingNetworkPolicy", Message: "error getting network policy %s"} + ErrGettingNodes = &Error{Code: "ErrorGettingNodes", Message: "error getting nodes"} + ErrGettingPod = &Error{Code: "ErrorGettingPod", Message: "failed to get pod %s"} + ErrGettingReplicaSet = &Error{Code: "ErrorGettingReplicaSet", Message: "failed to get ReplicaSet %s"} + ErrGettingService = &Error{Code: "ErrorGettingService", Message: "error getting service %s"} + ErrGettingServiceEndpoint = &Error{Code: "ErrorGettingServiceEndpoint", Message: "error getting service endpoint %s"} + ErrKnuuNotInitialized = &Error{Code: "KnuuNotInitialized", Message: "knuu is not initialized"} + ErrLoadBalancerIPNotAvailable = &Error{Code: "LoadBalancerIPNotAvailable", Message: "load balancer IP not available for service %s"} + ErrListingPodsForReplicaSet = &Error{Code: "ErrorListingPodsForReplicaSet", Message: "failed to list pods for ReplicaSet %s"} + ErrNamespaceRequired = &Error{Code: "NamespaceRequired", Message: "namespace is required"} + ErrNoNodesFound = &Error{Code: "NoNodesFound", Message: "no nodes found"} + ErrNoPodsForReplicaSet = &Error{Code: "NoPodsForReplicaSet", Message: "no pods found for ReplicaSet %s"} + ErrNoPortsSpecified = &Error{Code: "NoPortsSpecified", Message: "no ports specified for service %s"} + ErrNodePortNotSet = &Error{Code: "NodePortNotSet", Message: "node port not set for service %s"} + ErrParsingCPURequest = &Error{Code: "ErrorParsingCPURequest", Message: "failed to parse CPU request quantity '%s'"} + ErrParsingMemoryLimit = &Error{Code: "ErrorParsingMemoryLimit", Message: "failed to parse memory limit quantity '%s'"} + ErrParsingMemoryRequest = &Error{Code: "ErrorParsingMemoryRequest", Message: "failed to parse memory request quantity '%s'"} + ErrPatchingService = &Error{Code: "ErrorPatchingService", Message: "error patching service %s"} + ErrPortForwarding = &Error{Code: "ErrorPortForwarding", Message: "failed to port forward: %v"} + ErrPortForwardingTimeout = &Error{Code: "ErrorPortForwardingTimeout", Message: "timed out waiting for port forwarding to be ready"} + ErrPreparingInitContainer = &Error{Code: "ErrorPreparingInitContainer", Message: "failed to prepare init container"} + ErrPreparingMainContainer = &Error{Code: "ErrorPreparingMainContainer", Message: "failed to prepare main container"} + ErrPreparingPod = &Error{Code: "ErrorPreparingPod", Message: "error preparing pod"} + ErrPreparingPodSpec = &Error{Code: "ErrorPreparingPodSpec", Message: "failed to prepare pod spec"} + ErrPreparingPodVolumes = &Error{Code: "ErrorPreparingPodVolumes", Message: "failed to prepare pod volumes"} + ErrPreparingService = &Error{Code: "ErrorPreparingService", Message: "error preparing service %s"} + ErrPreparingSidecarContainer = &Error{Code: "ErrorPreparingSidecarContainer", Message: "failed to prepare sidecar container"} + ErrPreparingSidecarVolumes = &Error{Code: "ErrorPreparingSidecarVolumes", Message: "failed to prepare sidecar volumes"} + ErrRetrievingKubernetesConfig = &Error{Code: "RetrievingKubernetesConfig", Message: "retrieving the Kubernetes config"} + ErrServiceNameRequired = &Error{Code: "ServiceNameRequired", Message: "service name is required"} + ErrTimeoutWaitingForServiceReady = &Error{Code: "TimeoutWaitingForServiceReady", Message: "timed out waiting for service %s to be ready"} + ErrUpdatingDaemonset = &Error{Code: "ErrorUpdatingDaemonset", Message: "error updating daemonset %s"} + ErrWaitingForDeployment = &Error{Code: "ErrorWaitingForDeployment", Message: "error waiting for Deployment %s to be ready"} + ErrWaitingForReplicaSet = &Error{Code: "ErrorWaitingForReplicaSet", Message: "error waiting for ReplicaSet to delete"} + ErrClusterRoleAlreadyExists = &Error{Code: "ClusterRoleAlreadyExists", Message: "ClusterRole %s already exists"} + ErrClusterRoleBindingAlreadyExists = &Error{Code: "ClusterRoleBindingAlreadyExists", Message: "ClusterRoleBinding %s already exists"} ) diff --git a/pkg/k8s/k8s.go b/pkg/k8s/k8s.go index 172234b..5693c65 100644 --- a/pkg/k8s/k8s.go +++ b/pkg/k8s/k8s.go @@ -7,6 +7,7 @@ import ( "path/filepath" "regexp" "strings" + "time" "github.com/sirupsen/logrus" "k8s.io/client-go/kubernetes" @@ -20,6 +21,9 @@ const ( // certPath path in the filesystem to the ca.crt certPath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" + + // waitRetry is the time to wait between retries for a readiness checking + waitRetry = 2 * time.Second ) type Client struct { diff --git a/pkg/k8s/k8s_pod.go b/pkg/k8s/k8s_pod.go index 076d5fe..467de54 100644 --- a/pkg/k8s/k8s_pod.go +++ b/pkg/k8s/k8s_pod.go @@ -47,6 +47,7 @@ type PodConfig struct { FsGroup int64 // FSGroup to apply to the Pod ContainerConfig ContainerConfig // ContainerConfig for the Pod SidecarConfigs []ContainerConfig // SideCarConfigs for the Pod + Annotations map[string]string // Annotations to apply to the Pod } type Volume struct { @@ -308,6 +309,24 @@ func (c *Client) getPod(ctx context.Context, name string) (*v1.Pod, error) { return pod, nil } +func (c *Client) WaitForDeployment(ctx context.Context, name string) error { + for { + deployment, err := c.clientset.AppsV1().Deployments(c.namespace).Get(ctx, name, metav1.GetOptions{}) + if err == nil && deployment.Status.ReadyReplicas > 0 { + break + } + + select { + case <-ctx.Done(): + return ErrWaitingForDeployment.WithParams(name).Wrap(err) + case <-time.After(waitRetry): + // Retry after some seconds + } + } + + return nil +} + // buildEnv builds an environment variable configuration for a Pod based on the given map of key-value pairs. func buildEnv(envMap map[string]string) []v1.EnvVar { envVars := make([]v1.EnvVar, 0, len(envMap)) @@ -606,9 +625,10 @@ func preparePod(spec PodConfig, init bool) (*v1.Pod, error) { // Construct the Pod object using the above data pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - Labels: labels, + Namespace: namespace, + Name: name, + Labels: labels, + Annotations: spec.Annotations, }, Spec: podSpec, } diff --git a/pkg/k8s/k8s_replicaset.go b/pkg/k8s/k8s_replicaset.go index f52c230..6dfeef3 100644 --- a/pkg/k8s/k8s_replicaset.go +++ b/pkg/k8s/k8s_replicaset.go @@ -157,9 +157,10 @@ func prepareReplicaSet(rsConf ReplicaSetConfig, init bool) (*appv1.ReplicaSet, e Selector: &metav1.LabelSelector{MatchLabels: rsConf.Labels}, Template: v1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Namespace: rsConf.Namespace, - Name: rsConf.Name, - Labels: rsConf.Labels, + Namespace: rsConf.Namespace, + Name: rsConf.Name, + Labels: rsConf.Labels, + Annotations: rsConf.PodConfig.Annotations, }, Spec: podSpec, }, diff --git a/pkg/k8s/k8s_role.go b/pkg/k8s/k8s_role.go index a632952..17dcc23 100644 --- a/pkg/k8s/k8s_role.go +++ b/pkg/k8s/k8s_role.go @@ -4,6 +4,7 @@ import ( "context" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -29,3 +30,29 @@ func (c *Client) CreateRole( func (c *Client) DeleteRole(ctx context.Context, name string) error { return c.clientset.RbacV1().Roles(c.namespace).Delete(ctx, name, metav1.DeleteOptions{}) } + +func (c *Client) CreateClusterRole( + ctx context.Context, + name string, + labels map[string]string, + policyRules []rbacv1.PolicyRule, +) error { + _, err := c.clientset.RbacV1().ClusterRoles().Get(ctx, name, metav1.GetOptions{}) + if err == nil || !errors.IsNotFound(err) { + return ErrClusterRoleAlreadyExists.WithParams(name) + } + + role := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labels, + }, + Rules: policyRules, + } + _, err = c.clientset.RbacV1().ClusterRoles().Create(ctx, role, metav1.CreateOptions{}) + return err +} + +func (c *Client) DeleteClusterRole(ctx context.Context, name string) error { + return c.clientset.RbacV1().ClusterRoles().Delete(ctx, name, metav1.DeleteOptions{}) +} diff --git a/pkg/k8s/k8s_rolebinding.go b/pkg/k8s/k8s_rolebinding.go index a8b3fea..0f386c5 100644 --- a/pkg/k8s/k8s_rolebinding.go +++ b/pkg/k8s/k8s_rolebinding.go @@ -4,6 +4,7 @@ import ( "context" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -39,3 +40,41 @@ func (c *Client) CreateRoleBinding( func (c *Client) DeleteRoleBinding(ctx context.Context, name string) error { return c.clientset.RbacV1().RoleBindings(c.namespace).Delete(ctx, name, metav1.DeleteOptions{}) } + +func (c *Client) CreateClusterRoleBinding( + ctx context.Context, + name string, + labels map[string]string, + clusterRole, serviceAccount string, +) error { + _, err := c.clientset.RbacV1().ClusterRoleBindings().Get(ctx, name, metav1.GetOptions{}) + if err == nil || !errors.IsNotFound(err) { + return ErrClusterRoleBindingAlreadyExists.WithParams(name) + } + + role := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labels, + }, + RoleRef: rbacv1.RoleRef{ + Kind: "ClusterRole", + Name: clusterRole, + APIGroup: rbacv1.GroupName, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: serviceAccount, + Namespace: c.namespace, + }, + }, + } + + _, err = c.clientset.RbacV1().ClusterRoleBindings().Create(ctx, role, metav1.CreateOptions{}) + return err +} + +func (c *Client) DeleteClusterRoleBinding(ctx context.Context, name string) error { + return c.clientset.RbacV1().ClusterRoleBindings().Delete(ctx, name, metav1.DeleteOptions{}) +} diff --git a/pkg/k8s/k8s_service.go b/pkg/k8s/k8s_service.go index e7ccd99..d516652 100644 --- a/pkg/k8s/k8s_service.go +++ b/pkg/k8s/k8s_service.go @@ -3,6 +3,8 @@ package k8s import ( "context" "fmt" + "net" + "time" "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" @@ -142,3 +144,92 @@ func prepareService( } return svc, nil } + +func (c *Client) WaitForService(ctx context.Context, name string) error { + for { + service, err := c.clientset.CoreV1().Services(c.namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return ErrGettingService.WithParams(name).Wrap(err) + } + + if service.Spec.Type == v1.ServiceTypeLoadBalancer { + if len(service.Status.LoadBalancer.Ingress) == 0 { + time.Sleep(waitRetry) + continue // Wait until the LoadBalancer IP is available + } + } else if service.Spec.Type == v1.ServiceTypeNodePort { + if service.Spec.Ports[0].NodePort == 0 { + return ErrNodePortNotSet + } + } else if len(service.Spec.ExternalIPs) == 0 { + return ErrExternalIPsNotSet + } + + // Check if service is reachable + endpoint, err := c.GetServiceEndpoint(ctx, name) + if err != nil { + return ErrGettingServiceEndpoint.WithParams(name).Wrap(err) + } + + if err := checkServiceConnectivity(endpoint); err != nil { + time.Sleep(waitRetry) // Retry after some seconds if Minio is not reachable + continue + } + + break // Service is reachable, exit the loop + } + + select { + case <-ctx.Done(): + return ErrTimeoutWaitingForServiceReady + default: + return nil + } +} + +func (c *Client) GetServiceEndpoint(ctx context.Context, name string) (string, error) { + minioService, err := c.clientset.CoreV1().Services(c.namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return "", ErrGettingService.WithParams(name).Wrap(err) + } + + if minioService.Spec.Type == v1.ServiceTypeLoadBalancer { + // Use the LoadBalancer's external IP + if len(minioService.Status.LoadBalancer.Ingress) > 0 { + return fmt.Sprintf("%s:%d", minioService.Status.LoadBalancer.Ingress[0].IP, minioService.Spec.Ports[0].Port), nil + } + return "", ErrLoadBalancerIPNotAvailable + } + + if minioService.Spec.Type == v1.ServiceTypeNodePort { + // Use the Node IP and NodePort + nodes, err := c.clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) + if err != nil { + return "", ErrGettingNodes.Wrap(err) + } + if len(nodes.Items) == 0 { + return "", ErrNoNodesFound + } + + // Use the first node for simplicity, you might need to handle multiple nodes + var nodeIP string + for _, address := range nodes.Items[0].Status.Addresses { + if address.Type == "ExternalIP" { + nodeIP = address.Address + break + } + } + return fmt.Sprintf("%s:%d", nodeIP, minioService.Spec.Ports[0].NodePort), nil + } + + return fmt.Sprintf("%s:%d", minioService.Spec.ClusterIP, minioService.Spec.Ports[0].Port), nil +} + +func checkServiceConnectivity(serviceEndpoint string) error { + conn, err := net.DialTimeout("tcp", serviceEndpoint, 2*time.Second) + if err != nil { + return ErrFailedToConnect.WithParams(serviceEndpoint).Wrap(err) + } + defer conn.Close() + return nil // success +} diff --git a/pkg/knuu/bittwister.go b/pkg/knuu/bittwister.go index 2389ed7..bb79ea7 100644 --- a/pkg/knuu/bittwister.go +++ b/pkg/knuu/bittwister.go @@ -2,7 +2,6 @@ package knuu import ( "context" - "fmt" "time" "github.com/celestiaorg/bittwister/sdk" @@ -48,10 +47,9 @@ func (c *btConfig) SetClient(client *sdk.Client) { c.client = client } -func (c *btConfig) SetNewClientByIPAddr(ip string) { - btAddress := fmt.Sprintf("%s:%d", ip, c.port) - c.client = sdk.NewClient(btAddress) - logrus.Debugf("BitTwister address '%s'", btAddress) +func (c *btConfig) SetNewClientByURL(url string) { + c.client = sdk.NewClient(url) + logrus.Debugf("BitTwister address '%s'", url) } func (c *btConfig) Port() int { diff --git a/pkg/knuu/errors.go b/pkg/knuu/errors.go index 12fbc45..e98de3a 100644 --- a/pkg/knuu/errors.go +++ b/pkg/knuu/errors.go @@ -212,4 +212,6 @@ var ( ErrMinioNotInitialized = &Error{Code: "MinioNotInitialized", Message: "minio not initialized"} ErrGeneratingK8sNameForPreloader = &Error{Code: "GeneratingK8sNameForPreloader", Message: "error generating k8s name for preloader"} ErrCannotLoadEnv = &Error{Code: "Cannot Load Env", Message: "cannot load env"} + ErrCannotDeployTraefik = &Error{Code: "Cannot Deploy Traefik", Message: "cannot deploy Traefik"} + ErrGettingBitTwisterPath = &Error{Code: "GettingBitTwisterPath", Message: "error getting BitTwister path"} ) diff --git a/pkg/knuu/instance_helper.go b/pkg/knuu/instance_helper.go index 27a6da3..71a63ef 100644 --- a/pkg/knuu/instance_helper.go +++ b/pkg/knuu/instance_helper.go @@ -8,11 +8,13 @@ import ( "os" "path/filepath" "strings" + "time" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "github.com/celestiaorg/knuu/pkg/k8s" + "github.com/celestiaorg/knuu/pkg/traefik" "github.com/google/uuid" "github.com/sirupsen/logrus" ) @@ -466,6 +468,7 @@ func (i *Instance) prepareReplicaSetConfig() k8s.ReplicaSetConfig { } // Generate the sidecar configurations sidecarConfigs := make([]k8s.ContainerConfig, 0) + annotations := make(map[string]string) for _, sidecar := range i.sidecars { sidecarConfigs = append(sidecarConfigs, k8s.ContainerConfig{ Name: sidecar.k8sName, @@ -483,6 +486,12 @@ func (i *Instance) prepareReplicaSetConfig() k8s.ReplicaSetConfig { Files: sidecar.files, SecurityContext: prepareSecurityContext(sidecar.securityContext), }) + + sann := traefik.Annotate(sidecar.k8sName, sidecar.portsTCP[0]) + // append sann to the annotations + for k, v := range sann { + annotations[k] = v + } } // Generate the pod configuration podConfig := k8s.PodConfig{ @@ -493,6 +502,7 @@ func (i *Instance) prepareReplicaSetConfig() k8s.ReplicaSetConfig { FsGroup: i.fsGroup, ContainerConfig: containerConfig, SidecarConfigs: sidecarConfigs, + Annotations: annotations, } // Generate the ReplicaSet configuration statefulSetConfig := k8s.ReplicaSetConfig{ @@ -578,17 +588,33 @@ func (i *Instance) createBitTwisterInstance() (*Instance, error) { return nil, ErrSettingBitTwisterImage.Wrap(err) } - // We need to add the port here so the instance will get an IP - if err := i.AddPortTCP(i.BitTwister.Port()); err != nil { + // This is needed for reverse proxy annotation + if err := bt.AddPortTCP(i.BitTwister.Port()); err != nil { return nil, ErrAddingBitTwisterPort.Wrap(err) } - ip, err := i.GetIP() + + // We need to add the port here so the instance will get an IP + // if err := i.AddPortTCP(1234); err != nil { + // return nil, ErrAddingBitTwisterPort.Wrap(err) + // } + // ip, err := i.GetIP() + // if err != nil { + // return nil, ErrGettingInstanceIP.WithParams(i.name).Wrap(err) + // } + // logrus.Debugf("IP of instance '%s' is '%s'", i.name, ip) + + // TODO: remove this when pkg refactor + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + + btURL, err := traefikClient.URL(ctx, bt.k8sName) if err != nil { - return nil, ErrGettingInstanceIP.WithParams(i.name).Wrap(err) + return nil, ErrGettingBitTwisterPath.Wrap(err) } - logrus.Debugf("IP of instance '%s' is '%s'", i.name, ip) + logrus.Debugf("BitTwister URL: ", btURL) + logrus.Info("BitTwister URL: ", btURL) - i.BitTwister.SetNewClientByIPAddr("http://" + ip) + i.BitTwister.SetNewClientByURL(btURL) if err := bt.Commit(); err != nil { return nil, ErrCommittingBitTwisterInstance.Wrap(err) diff --git a/pkg/knuu/knuu.go b/pkg/knuu/knuu.go index 7c503f0..38a2948 100644 --- a/pkg/knuu/knuu.go +++ b/pkg/knuu/knuu.go @@ -22,6 +22,7 @@ import ( "github.com/celestiaorg/knuu/pkg/builder/kaniko" "github.com/celestiaorg/knuu/pkg/k8s" "github.com/celestiaorg/knuu/pkg/minio" + "github.com/celestiaorg/knuu/pkg/traefik" ) var ( @@ -31,8 +32,9 @@ var ( timeout time.Duration imageBuilder builder.Builder - // TODO: this temporary until we refactor knuu pkg - k8sClient *k8s.Client + // TODO: these are temporary until we refactor knuu pkg + k8sClient *k8s.Client + traefikClient *traefik.Traefik ) // Initialize initializes knuu with a unique scope @@ -96,15 +98,28 @@ func InitializeWithScope(scope string) error { return ErrCannotInitializeK8s.Wrap(err) } - if err := handleTimeout(); err != nil { - return ErrCannotHandleTimeout.Wrap(err) + traefikClient = &traefik.Traefik{ + K8s: k8sClient, + } + if err := traefikClient.Deploy(ctx); err != nil { + return ErrCannotDeployTraefik.Wrap(err) + } + + publicIP, err := traefikClient.Endpoint(ctx) + if err != nil { + fmt.Printf("Error getting traefik Endpoint: %v", err) } + fmt.Printf("Traefik publicIP: %v\n", publicIP) minioClient = &minio.Minio{ Clientset: k8sClient.Clientset(), Namespace: k8sClient.Namespace(), } + if err := handleTimeout(); err != nil { + return ErrCannotHandleTimeout.Wrap(err) + } + builderType := os.Getenv("KNUU_BUILDER") switch builderType { case "kubernetes": diff --git a/pkg/traefik/errors.go b/pkg/traefik/errors.go new file mode 100644 index 0000000..3a829b3 --- /dev/null +++ b/pkg/traefik/errors.go @@ -0,0 +1,46 @@ +package traefik + +import ( + "fmt" +) + +type Error struct { + Code string + Message string + Err error + Params []interface{} +} + +func (e *Error) Error() string { + msg := fmt.Sprintf(e.Message, e.Params...) + if e.Err != nil { + return fmt.Sprintf("%s: %v", msg, e.Err) + } + return msg +} + +func (e *Error) Wrap(err error) error { + e.Err = err + return e +} + +func (e *Error) WithParams(params ...interface{}) *Error { + e.Params = params + return e +} + +var ( + ErrTraefikDeploymentCreationFailed = &Error{Code: "TraefikDeploymentCreationFailed", Message: "error creating Traefik deployment"} + ErrTraefikServiceCreationFailed = &Error{Code: "TraefikServiceCreationFailed", Message: "error creating Traefik service"} + ErrTraefikClientNotInitialized = &Error{Code: "TraefikClientNotInitialized", Message: "Traefik client not initialized"} + ErrTraefikIPNotFound = &Error{Code: "TraefikIPNotFound", Message: "Traefik IP not found"} + ErrTraefikFailedToGetService = &Error{Code: "TraefikFailedToGetService", Message: "error getting Traefik service"} + ErrTraefikLoadBalancerIPNotAvailable = &Error{Code: "TraefikLoadBalancerIPNotAvailable", Message: "Traefik LoadBalancer IP not available"} + ErrTraefikFailedToGetNodes = &Error{Code: "TraefikFailedToGetNodes", Message: "error getting Traefik nodes"} + ErrTraefikNoNodesFound = &Error{Code: "TraefikNoNodesFound", Message: "no Traefik nodes found"} + ErrTraefikTimeoutWaitingForReady = &Error{Code: "TraefikTimeoutWaitingForReady", Message: "Traefik timeout waiting for ready"} + ErrTraefikFailedToCreateService = &Error{Code: "TraefikFailedToCreateService", Message: "error creating Traefik service"} + ErrTraefikRoleCreationFailed = &Error{Code: "TraefikRoleCreationFailed", Message: "error creating Traefik role"} + ErrTraefikRoleBindingCreationFailed = &Error{Code: "TraefikRoleBindingCreationFailed", Message: "error creating Traefik role binding"} + ErrFailedToCreateServiceAccount = &Error{Code: "FailedToCreateServiceAccount", Message: "error creating service account"} +) diff --git a/pkg/traefik/traefik.go b/pkg/traefik/traefik.go new file mode 100644 index 0000000..a506925 --- /dev/null +++ b/pkg/traefik/traefik.go @@ -0,0 +1,213 @@ +package traefik + +import ( + "context" + "fmt" + "time" + + "github.com/celestiaorg/knuu/pkg/k8s" + "github.com/celestiaorg/knuu/pkg/names" + "github.com/sirupsen/logrus" + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" +) + +const ( + traefikServiceName = "traefik" + Port = 80 + PortSecure = 443 + deploymentName = "traefik-deployment" + roleName = "traefik-role" + containerName = "traefik" + image = "traefik:v3.0" + appLabel = "app" + appLabelValue = "traefik" + replicas = 2 + waitRetry = 5 * time.Second +) + +type Traefik struct { + K8s *k8s.Client + endpoint string +} + +func (t *Traefik) Deploy(ctx context.Context) error { + if t.K8s == nil { + return ErrTraefikClientNotInitialized + } + + // Create a dedicated service account for Traefik + serviceAccountName, err := names.NewRandomK8("traefik-service-account") + if err != nil { + return err + } + if err := t.K8s.CreateServiceAccount(ctx, serviceAccountName, nil); err != nil { + return ErrFailedToCreateServiceAccount.Wrap(err) + } + + // // Define and create a role for Traefik + // err = t.K8s.CreateRole(ctx, roleName, nil, []rbacv1.PolicyRule{ + // { + // APIGroups: []string{""}, + // Resources: []string{"pods"}, + // Verbs: []string{"get", "list", "watch"}, + // }, + // }) + + // if err != nil { + // return ErrTraefikRoleCreationFailed.Wrap(err) + // } + + // Create the Traefik deployment using the service account + traefikDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: deploymentName, + Namespace: t.K8s.Namespace(), + Labels: map[string]string{appLabel: appLabelValue}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: ptr.To[int32](replicas), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{appLabel: appLabelValue}, + }, + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{appLabel: appLabelValue}, + }, + Spec: v1.PodSpec{ + ServiceAccountName: serviceAccountName, + Containers: []v1.Container{ + { + Name: containerName, + Image: image, + Args: []string{ + "--api.insecure=true", + "--providers.kubernetesIngress", + "--providers.kubernetesCRD", + fmt.Sprintf("--entrypoints.web.Address=:%d", Port), + fmt.Sprintf("--entrypoints.websecure.Address=:%d", PortSecure), + }, + Ports: []v1.ContainerPort{ + {ContainerPort: Port, Name: "web"}, + {ContainerPort: PortSecure, Name: "websecure"}, + }, + }, + }, + }, + }, + }, + } + _, err = t.K8s.Clientset().AppsV1().Deployments(t.K8s.Namespace()).Create(ctx, traefikDeployment, metav1.CreateOptions{}) + if err != nil { + return ErrTraefikDeploymentCreationFailed.Wrap(err) + } + + if err := t.K8s.WaitForDeployment(ctx, deploymentName); err != nil { + return err + } + + if err := t.createService(ctx); err != nil { + return err + } + + if err := t.K8s.WaitForService(ctx, traefikServiceName); err != nil { + return err + } + + return nil +} + +func (t *Traefik) IP(ctx context.Context) (string, error) { + if t.K8s == nil { + return "", ErrTraefikClientNotInitialized + } + + return t.K8s.GetServiceIP(ctx, traefikServiceName) +} + +func (t *Traefik) URL(ctx context.Context, name string) (string, error) { + if t.endpoint == "" { + var err error + if t.endpoint, err = t.Endpoint(ctx); err != nil { + return "", ErrTraefikIPNotFound.Wrap(err) + } + } + return fmt.Sprintf("http://%s/%s", t.endpoint, name), nil +} + +func (t *Traefik) Endpoint(ctx context.Context) (string, error) { + if t.K8s == nil { + return "", ErrTraefikClientNotInitialized + } + return t.K8s.GetServiceEndpoint(ctx, traefikServiceName) +} + +func (t *Traefik) AddHost(ctx context.Context, serviceName string, port int) error { + middleware := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "traefik.io/v1alpha1", + "kind": "Middleware", + "metadata": map[string]interface{}{ + "name": "strip-dummy", + "namespace": "my-traefik-namespace", + }, + "spec": map[string]interface{}{ + "stripPrefix": map[string]interface{}{ + "prefixes": []string{"/dummy"}, + }, + }, + }, + } + + middlewareResource := schema.GroupVersionResource{ + Group: "traefik.io", + Version: "v1alpha1", + Resource: "middlewares", + } + + t.K8s.Clientset(). + +} + +// TODO: need to update the k8s pkg to handle service creation in more custom way +func (t *Traefik) createService(ctx context.Context) error { + sCli := t.K8s.Clientset().CoreV1().Services(t.K8s.Namespace()) + + srv := &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: traefikServiceName, + Namespace: t.K8s.Namespace(), + Labels: map[string]string{appLabel: appLabelValue}, + }, + Spec: v1.ServiceSpec{ + Selector: map[string]string{appLabel: appLabelValue}, + Ports: []v1.ServicePort{ + { + Name: "web", + Protocol: v1.ProtocolTCP, + Port: Port, + TargetPort: intstr.FromInt(Port), + }, + { + Name: "websecure", + Protocol: v1.ProtocolTCP, + Port: PortSecure, + TargetPort: intstr.FromInt(PortSecure), + }, + }, + Type: v1.ServiceTypeLoadBalancer, + }, + } + + if _, err := sCli.Create(ctx, srv, metav1.CreateOptions{}); err != nil { + return ErrTraefikFailedToCreateService.Wrap(err) + } + + logrus.Debugf("Service %s created successfully.", traefikServiceName) + return nil +} From 69b5d4cbc572ac574bbf4d7eb389c04bd6a1f938 Mon Sep 17 00:00:00 2001 From: Mojtaba Date: Wed, 15 May 2024 12:13:38 +0200 Subject: [PATCH 2/9] chore: changes added as a draft to fix an issue --- e2e/basic/reverse_proxy_test.go | 20 +++- go.mod | 15 ++- go.sum | 18 +--- pkg/k8s/errors.go | 159 ++++++++++++++++------------- pkg/k8s/k8s.go | 4 + pkg/k8s/k8s_endpoint.go | 86 ++++++++++++++++ pkg/knuu/errors.go | 4 + pkg/knuu/instance_helper.go | 84 ++++++++++++---- pkg/traefik/errors.go | 3 + pkg/traefik/traefik.go | 171 +++++++++++++++++++++++++------- proxy_sa.sh | 136 +++++++++++++++++++++++++ 11 files changed, 549 insertions(+), 151 deletions(-) create mode 100644 pkg/k8s/k8s_endpoint.go create mode 100755 proxy_sa.sh diff --git a/e2e/basic/reverse_proxy_test.go b/e2e/basic/reverse_proxy_test.go index 312c3dd..2f0b395 100644 --- a/e2e/basic/reverse_proxy_test.go +++ b/e2e/basic/reverse_proxy_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "os/signal" "testing" "time" @@ -48,7 +49,22 @@ func TestReverseProxy(t *testing.T) { { out, err := main.BitTwister.Client().AllServicesStatus() fmt.Printf("err: %v\n", err) - time.Sleep(10 * time.Minute) + + // wait for Ctrl+C signal for 10 minutes + // Setup a channel to listen for the interrupt signal (Ctrl+C) + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt) + + // Create a timeout to stop waiting after 10 minutes + timeout := time.After(30 * time.Minute) + + select { + case <-sigChan: + fmt.Println("Ctrl+C received, stopping the test.") + case <-timeout: + fmt.Println("30 minutes passed without Ctrl+C, continuing the test.") + } + require.NoError(t, err, "Error getting all services status") assert.NotEmpty(t, out, "No services found") } @@ -69,4 +85,4 @@ func TestReverseProxy(t *testing.T) { } } -// reset && LOG_LEVEL=debug go test -v ./e2e/basic/ --run TestReverseProxy +// reset && LOG_LEVEL=debug go test -v ./e2e/basic/ --run TestReverseProxy -timeout 60m diff --git a/go.mod b/go.mod index 3dc6737..4348f6f 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( k8s.io/api v0.28.2 k8s.io/apimachinery v0.28.2 k8s.io/client-go v0.28.2 + k8s.io/utils v0.0.0-20230726121419-3b25d923346b ) require ( @@ -39,7 +40,6 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/Workiva/go-datastructures v1.0.53 // indirect - github.com/acroca/go-symbols v0.1.1 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/aws/aws-sdk-go v1.44.122 // indirect github.com/benbjohnson/clock v1.3.5 // indirect @@ -278,7 +278,6 @@ require ( github.com/quic-go/quic-go v0.42.0 // indirect github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/rakyll/statik v0.1.7 // indirect - github.com/ramya-rao-a/go-outline v0.0.0-20210608161538-9736a4bde949 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/regen-network/cosmos-proto v0.3.1 // indirect @@ -326,16 +325,15 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect - golang.org/x/mod v0.15.0 // indirect - golang.org/x/net v0.23.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.0 // indirect - golang.org/x/tools/cmd/guru v0.1.1-deprecated // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect gonum.org/v1/gonum v0.14.0 // indirect google.golang.org/api v0.162.0 // indirect @@ -352,7 +350,6 @@ require ( gotest.tools/v3 v3.5.1 // indirect k8s.io/klog/v2 v2.120.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect lukechampine.com/blake3 v1.2.1 // indirect nhooyr.io/websocket v1.8.7 // indirect rsc.io/tmplfunc v0.0.3 // indirect diff --git a/go.sum b/go.sum index 44fa05a..9a4cea5 100644 --- a/go.sum +++ b/go.sum @@ -2530,8 +2530,6 @@ golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2593,8 +2591,6 @@ golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2686,8 +2682,6 @@ golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2883,8 +2877,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2893,8 +2887,6 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2911,8 +2903,6 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2999,8 +2989,8 @@ golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/k8s/errors.go b/pkg/k8s/errors.go index 7f468d4..75be5b1 100644 --- a/pkg/k8s/errors.go +++ b/pkg/k8s/errors.go @@ -30,75 +30,92 @@ func (e *Error) WithParams(params ...interface{}) *Error { } var ( - ErrKnuuNotInitialized = &Error{Code: "KnuuNotInitialized", Message: "knuu is not initialized"} - ErrGettingConfigmap = &Error{Code: "ErrorGettingConfigmap", Message: "error getting configmap %s"} - ErrConfigmapAlreadyExists = &Error{Code: "ConfigmapAlreadyExists", Message: "configmap %s already exists"} - ErrCreatingConfigmap = &Error{Code: "ErrorCreatingConfigmap", Message: "error creating configmap %s"} - ErrConfigmapDoesNotExist = &Error{Code: "ConfigmapDoesNotExist", Message: "configmap %s does not exist"} - ErrDeletingConfigmap = &Error{Code: "ErrorDeletingConfigmap", Message: "error deleting configmap %s"} - ErrGettingDaemonset = &Error{Code: "ErrorGettingDaemonset", Message: "error getting daemonset %s"} - ErrCreatingDaemonset = &Error{Code: "ErrorCreatingDaemonset", Message: "error creating daemonset %s"} - ErrUpdatingDaemonset = &Error{Code: "ErrorUpdatingDaemonset", Message: "error updating daemonset %s"} - ErrDeletingDaemonset = &Error{Code: "ErrorDeletingDaemonset", Message: "error deleting daemonset %s"} - ErrCreatingNamespace = &Error{Code: "ErrorCreatingNamespace", Message: "error creating namespace %s"} - ErrDeletingNamespace = &Error{Code: "ErrorDeletingNamespace", Message: "error deleting namespace %s"} - ErrGettingNamespace = &Error{Code: "ErrorGettingNamespace", Message: "error getting namespace %s"} - ErrCreatingNetworkPolicy = &Error{Code: "ErrorCreatingNetworkPolicy", Message: "error creating network policy %s"} - ErrDeletingNetworkPolicy = &Error{Code: "ErrorDeletingNetworkPolicy", Message: "error deleting network policy %s"} - ErrGettingNetworkPolicy = &Error{Code: "ErrorGettingNetworkPolicy", Message: "error getting network policy %s"} - ErrGettingPod = &Error{Code: "ErrorGettingPod", Message: "failed to get pod %s"} - ErrPreparingPod = &Error{Code: "ErrorPreparingPod", Message: "error preparing pod"} - ErrCreatingPod = &Error{Code: "ErrorCreatingPod", Message: "failed to create pod"} - ErrDeletingPod = &Error{Code: "ErrorDeletingPod", Message: "failed to delete pod"} - ErrDeployingPod = &Error{Code: "ErrorDeployingPod", Message: "failed to deploy pod"} - ErrGettingK8sConfig = &Error{Code: "ErrorGettingK8sConfig", Message: "failed to get k8s config"} - ErrCreatingExecutor = &Error{Code: "ErrorCreatingExecutor", Message: "failed to create Executor"} - ErrExecutingCommand = &Error{Code: "ErrorExecutingCommand", Message: "failed to execute command"} - ErrCommandExecution = &Error{Code: "ErrorCommandExecution", Message: "error while executing command"} - ErrDeletingPodFailed = &Error{Code: "ErrorDeletingPodFailed", Message: "failed to delete pod %s"} - ErrParsingMemoryRequest = &Error{Code: "ErrorParsingMemoryRequest", Message: "failed to parse memory request quantity '%s'"} - ErrParsingMemoryLimit = &Error{Code: "ErrorParsingMemoryLimit", Message: "failed to parse memory limit quantity '%s'"} - ErrParsingCPURequest = &Error{Code: "ErrorParsingCPURequest", Message: "failed to parse CPU request quantity '%s'"} - ErrBuildingContainerVolumes = &Error{Code: "ErrorBuildingContainerVolumes", Message: "failed to build container volumes"} - ErrBuildingResources = &Error{Code: "ErrorBuildingResources", Message: "failed to build resources"} - ErrBuildingInitContainerVolumes = &Error{Code: "ErrorBuildingInitContainerVolumes", Message: "failed to build init container volumes"} - ErrBuildingInitContainerCommand = &Error{Code: "ErrorBuildingInitContainerCommand", Message: "failed to build init container command"} - ErrBuildingPodVolumes = &Error{Code: "ErrorBuildingPodVolumes", Message: "failed to build pod volumes"} - ErrPreparingMainContainer = &Error{Code: "ErrorPreparingMainContainer", Message: "failed to prepare main container"} - ErrPreparingInitContainer = &Error{Code: "ErrorPreparingInitContainer", Message: "failed to prepare init container"} - ErrPreparingPodVolumes = &Error{Code: "ErrorPreparingPodVolumes", Message: "failed to prepare pod volumes"} - ErrPreparingSidecarContainer = &Error{Code: "ErrorPreparingSidecarContainer", Message: "failed to prepare sidecar container"} - ErrPreparingSidecarVolumes = &Error{Code: "ErrorPreparingSidecarVolumes", Message: "failed to prepare sidecar volumes"} - ErrCreatingPodSpec = &Error{Code: "ErrorCreatingPodSpec", Message: "failed to create pod spec"} - ErrGettingClusterConfig = &Error{Code: "ErrorGettingClusterConfig", Message: "failed to get cluster config"} - ErrCreatingRoundTripper = &Error{Code: "ErrorCreatingRoundTripper", Message: "failed to create round tripper"} - ErrCreatingPortForwarder = &Error{Code: "ErrorCreatingPortForwarder", Message: "failed to create port forwarder"} - ErrPortForwarding = &Error{Code: "ErrorPortForwarding", Message: "failed to port forward: %v"} - ErrForwardingPorts = &Error{Code: "ErrorForwardingPorts", Message: "error forwarding ports"} - ErrPortForwardingTimeout = &Error{Code: "ErrorPortForwardingTimeout", Message: "timed out waiting for port forwarding to be ready"} - ErrDeletingPersistentVolumeClaim = &Error{Code: "ErrorDeletingPersistentVolumeClaim", Message: "error deleting PersistentVolumeClaim %s"} - ErrCreatingPersistentVolumeClaim = &Error{Code: "ErrorCreatingPersistentVolumeClaim", Message: "error creating PersistentVolumeClaim"} - ErrGettingReplicaSet = &Error{Code: "ErrorGettingReplicaSet", Message: "failed to get ReplicaSet %s"} - ErrCreatingReplicaSet = &Error{Code: "ErrorCreatingReplicaSet", Message: "failed to create ReplicaSet"} - ErrDeletingReplicaSet = &Error{Code: "ErrorDeletingReplicaSet", Message: "failed to delete ReplicaSet %s"} - ErrWaitingForReplicaSet = &Error{Code: "ErrorWaitingForReplicaSet", Message: "error waiting for ReplicaSet to delete"} - ErrDeployingReplicaSet = &Error{Code: "ErrorDeployingReplicaSet", Message: "failed to deploy ReplicaSet"} - ErrPreparingPodSpec = &Error{Code: "ErrorPreparingPodSpec", Message: "failed to prepare pod spec"} - ErrListingPodsForReplicaSet = &Error{Code: "ErrorListingPodsForReplicaSet", Message: "failed to list pods for ReplicaSet %s"} - ErrNoPodsForReplicaSet = &Error{Code: "NoPodsForReplicaSet", Message: "no pods found for ReplicaSet %s"} - ErrGettingService = &Error{Code: "ErrorGettingService", Message: "error getting service %s"} - ErrPreparingService = &Error{Code: "ErrorPreparingService", Message: "error preparing service %s"} - ErrCreatingService = &Error{Code: "ErrorCreatingService", Message: "error creating service %s"} - ErrPatchingService = &Error{Code: "ErrorPatchingService", Message: "error patching service %s"} - ErrDeletingService = &Error{Code: "ErrorDeletingService", Message: "error deleting service %s"} - ErrNamespaceRequired = &Error{Code: "NamespaceRequired", Message: "namespace is required"} - ErrServiceNameRequired = &Error{Code: "ServiceNameRequired", Message: "service name is required"} - ErrNoPortsSpecified = &Error{Code: "NoPortsSpecified", Message: "no ports specified for service %s"} - ErrRetrievingKubernetesConfig = &Error{Code: "RetrievingKubernetesConfig", Message: "retrieving the Kubernetes config"} - ErrCreatingClientset = &Error{Code: "CreatingClientset", Message: "creating clientset for Kubernetes"} - ErrCreatingDiscoveryClient = &Error{Code: "CreatingDiscoveryClient", Message: "creating discovery client for Kubernetes"} - ErrCreatingDynamicClient = &Error{Code: "CreatingDynamicClient", Message: "creating dynamic client for Kubernetes"} - ErrGettingResourceList = &Error{Code: "GettingResourceList", Message: "getting resource list for group version %s"} - ErrResourceDoesNotExist = &Error{Code: "ResourceDoesNotExist", Message: "resource %s does not exist in group version %s"} - ErrCreatingCustomResource = &Error{Code: "CreatingCustomResource", Message: "creating custom resource %s"} + ErrKnuuNotInitialized = &Error{Code: "KnuuNotInitialized", Message: "knuu is not initialized"} + ErrGettingConfigmap = &Error{Code: "ErrorGettingConfigmap", Message: "error getting configmap %s"} + ErrConfigmapAlreadyExists = &Error{Code: "ConfigmapAlreadyExists", Message: "configmap %s already exists"} + ErrCreatingConfigmap = &Error{Code: "ErrorCreatingConfigmap", Message: "error creating configmap %s"} + ErrConfigmapDoesNotExist = &Error{Code: "ConfigmapDoesNotExist", Message: "configmap %s does not exist"} + ErrDeletingConfigmap = &Error{Code: "ErrorDeletingConfigmap", Message: "error deleting configmap %s"} + ErrGettingDaemonset = &Error{Code: "ErrorGettingDaemonset", Message: "error getting daemonset %s"} + ErrCreatingDaemonset = &Error{Code: "ErrorCreatingDaemonset", Message: "error creating daemonset %s"} + ErrUpdatingDaemonset = &Error{Code: "ErrorUpdatingDaemonset", Message: "error updating daemonset %s"} + ErrDeletingDaemonset = &Error{Code: "ErrorDeletingDaemonset", Message: "error deleting daemonset %s"} + ErrCreatingNamespace = &Error{Code: "ErrorCreatingNamespace", Message: "error creating namespace %s"} + ErrDeletingNamespace = &Error{Code: "ErrorDeletingNamespace", Message: "error deleting namespace %s"} + ErrGettingNamespace = &Error{Code: "ErrorGettingNamespace", Message: "error getting namespace %s"} + ErrCreatingNetworkPolicy = &Error{Code: "ErrorCreatingNetworkPolicy", Message: "error creating network policy %s"} + ErrDeletingNetworkPolicy = &Error{Code: "ErrorDeletingNetworkPolicy", Message: "error deleting network policy %s"} + ErrGettingNetworkPolicy = &Error{Code: "ErrorGettingNetworkPolicy", Message: "error getting network policy %s"} + ErrGettingPod = &Error{Code: "ErrorGettingPod", Message: "failed to get pod %s"} + ErrPreparingPod = &Error{Code: "ErrorPreparingPod", Message: "error preparing pod"} + ErrCreatingPod = &Error{Code: "ErrorCreatingPod", Message: "failed to create pod"} + ErrDeletingPod = &Error{Code: "ErrorDeletingPod", Message: "failed to delete pod"} + ErrDeployingPod = &Error{Code: "ErrorDeployingPod", Message: "failed to deploy pod"} + ErrGettingK8sConfig = &Error{Code: "ErrorGettingK8sConfig", Message: "failed to get k8s config"} + ErrCreatingExecutor = &Error{Code: "ErrorCreatingExecutor", Message: "failed to create Executor"} + ErrExecutingCommand = &Error{Code: "ErrorExecutingCommand", Message: "failed to execute command"} + ErrCommandExecution = &Error{Code: "ErrorCommandExecution", Message: "error while executing command"} + ErrDeletingPodFailed = &Error{Code: "ErrorDeletingPodFailed", Message: "failed to delete pod %s"} + ErrParsingMemoryRequest = &Error{Code: "ErrorParsingMemoryRequest", Message: "failed to parse memory request quantity '%s'"} + ErrParsingMemoryLimit = &Error{Code: "ErrorParsingMemoryLimit", Message: "failed to parse memory limit quantity '%s'"} + ErrParsingCPURequest = &Error{Code: "ErrorParsingCPURequest", Message: "failed to parse CPU request quantity '%s'"} + ErrBuildingContainerVolumes = &Error{Code: "ErrorBuildingContainerVolumes", Message: "failed to build container volumes"} + ErrBuildingResources = &Error{Code: "ErrorBuildingResources", Message: "failed to build resources"} + ErrBuildingInitContainerVolumes = &Error{Code: "ErrorBuildingInitContainerVolumes", Message: "failed to build init container volumes"} + ErrBuildingInitContainerCommand = &Error{Code: "ErrorBuildingInitContainerCommand", Message: "failed to build init container command"} + ErrBuildingPodVolumes = &Error{Code: "ErrorBuildingPodVolumes", Message: "failed to build pod volumes"} + ErrPreparingMainContainer = &Error{Code: "ErrorPreparingMainContainer", Message: "failed to prepare main container"} + ErrPreparingInitContainer = &Error{Code: "ErrorPreparingInitContainer", Message: "failed to prepare init container"} + ErrPreparingPodVolumes = &Error{Code: "ErrorPreparingPodVolumes", Message: "failed to prepare pod volumes"} + ErrPreparingSidecarContainer = &Error{Code: "ErrorPreparingSidecarContainer", Message: "failed to prepare sidecar container"} + ErrPreparingSidecarVolumes = &Error{Code: "ErrorPreparingSidecarVolumes", Message: "failed to prepare sidecar volumes"} + ErrCreatingPodSpec = &Error{Code: "ErrorCreatingPodSpec", Message: "failed to create pod spec"} + ErrGettingClusterConfig = &Error{Code: "ErrorGettingClusterConfig", Message: "failed to get cluster config"} + ErrCreatingRoundTripper = &Error{Code: "ErrorCreatingRoundTripper", Message: "failed to create round tripper"} + ErrCreatingPortForwarder = &Error{Code: "ErrorCreatingPortForwarder", Message: "failed to create port forwarder"} + ErrPortForwarding = &Error{Code: "ErrorPortForwarding", Message: "failed to port forward: %v"} + ErrForwardingPorts = &Error{Code: "ErrorForwardingPorts", Message: "error forwarding ports"} + ErrPortForwardingTimeout = &Error{Code: "ErrorPortForwardingTimeout", Message: "timed out waiting for port forwarding to be ready"} + ErrDeletingPersistentVolumeClaim = &Error{Code: "ErrorDeletingPersistentVolumeClaim", Message: "error deleting PersistentVolumeClaim %s"} + ErrCreatingPersistentVolumeClaim = &Error{Code: "ErrorCreatingPersistentVolumeClaim", Message: "error creating PersistentVolumeClaim"} + ErrGettingReplicaSet = &Error{Code: "ErrorGettingReplicaSet", Message: "failed to get ReplicaSet %s"} + ErrCreatingReplicaSet = &Error{Code: "ErrorCreatingReplicaSet", Message: "failed to create ReplicaSet"} + ErrDeletingReplicaSet = &Error{Code: "ErrorDeletingReplicaSet", Message: "failed to delete ReplicaSet %s"} + ErrWaitingForReplicaSet = &Error{Code: "ErrorWaitingForReplicaSet", Message: "error waiting for ReplicaSet to delete"} + ErrDeployingReplicaSet = &Error{Code: "ErrorDeployingReplicaSet", Message: "failed to deploy ReplicaSet"} + ErrPreparingPodSpec = &Error{Code: "ErrorPreparingPodSpec", Message: "failed to prepare pod spec"} + ErrListingPodsForReplicaSet = &Error{Code: "ErrorListingPodsForReplicaSet", Message: "failed to list pods for ReplicaSet %s"} + ErrNoPodsForReplicaSet = &Error{Code: "NoPodsForReplicaSet", Message: "no pods found for ReplicaSet %s"} + ErrGettingService = &Error{Code: "ErrorGettingService", Message: "error getting service %s"} + ErrPreparingService = &Error{Code: "ErrorPreparingService", Message: "error preparing service %s"} + ErrCreatingService = &Error{Code: "ErrorCreatingService", Message: "error creating service %s"} + ErrPatchingService = &Error{Code: "ErrorPatchingService", Message: "error patching service %s"} + ErrDeletingService = &Error{Code: "ErrorDeletingService", Message: "error deleting service %s"} + ErrNamespaceRequired = &Error{Code: "NamespaceRequired", Message: "namespace is required"} + ErrServiceNameRequired = &Error{Code: "ServiceNameRequired", Message: "service name is required"} + ErrNoPortsSpecified = &Error{Code: "NoPortsSpecified", Message: "no ports specified for service %s"} + ErrRetrievingKubernetesConfig = &Error{Code: "RetrievingKubernetesConfig", Message: "retrieving the Kubernetes config"} + ErrCreatingClientset = &Error{Code: "CreatingClientset", Message: "creating clientset for Kubernetes"} + ErrCreatingDiscoveryClient = &Error{Code: "CreatingDiscoveryClient", Message: "creating discovery client for Kubernetes"} + ErrCreatingDynamicClient = &Error{Code: "CreatingDynamicClient", Message: "creating dynamic client for Kubernetes"} + ErrGettingResourceList = &Error{Code: "GettingResourceList", Message: "getting resource list for group version %s"} + ErrResourceDoesNotExist = &Error{Code: "ResourceDoesNotExist", Message: "resource %s does not exist in group version %s"} + ErrCreatingCustomResource = &Error{Code: "CreatingCustomResource", Message: "creating custom resource %s"} + ErrCreatingRole = &Error{Code: "CreatingRole", Message: "creating role %s"} + ErrCreatingRoleBinding = &Error{Code: "CreatingRoleBinding", Message: "creating role binding %s"} + ErrCreatingRoleBindingFailed = &Error{Code: "CreatingRoleBindingFailed", Message: "creating role binding %s failed"} + ErrNodePortNotSet = &Error{Code: "NodePortNotSet", Message: "node port not set"} + ErrExternalIPsNotSet = &Error{Code: "ExternalIPsNotSet", Message: "external IPs not set"} + ErrGettingServiceEndpoint = &Error{Code: "GettingServiceEndpoint", Message: "getting service endpoint %s"} + ErrTimeoutWaitingForServiceReady = &Error{Code: "TimeoutWaitingForServiceReady", Message: "timed out waiting for service %s to be ready"} + ErrLoadBalancerIPNotAvailable = &Error{Code: "LoadBalancerIPNotAvailable", Message: "load balancer IP not available"} + ErrGettingNodes = &Error{Code: "GettingNodes", Message: "getting nodes"} + ErrNoNodesFound = &Error{Code: "NoNodesFound", Message: "no nodes found"} + ErrFailedToConnect = &Error{Code: "FailedToConnect", Message: "failed to connect to %s"} + ErrWaitingForDeployment = &Error{Code: "WaitingForDeployment", Message: "waiting for deployment %s to be ready"} + ErrClusterRoleAlreadyExists = &Error{Code: "ClusterRoleAlreadyExists", Message: "cluster role %s already exists"} + ErrClusterRoleBindingAlreadyExists = &Error{Code: "ClusterRoleBindingAlreadyExists", Message: "cluster role binding %s already exists"} + ErrCreateEndpoint = &Error{Code: "CreateEndpoint", Message: "failed to create endpoint for service %s"} + ErrGetEndpoint = &Error{Code: "GetEndpoint", Message: "failed to get endpoint for service %s"} + ErrUpdateEndpoint = &Error{Code: "UpdateEndpoint", Message: "failed to update endpoint for service %s"} ) diff --git a/pkg/k8s/k8s.go b/pkg/k8s/k8s.go index d91b0b4..b53f529 100644 --- a/pkg/k8s/k8s.go +++ b/pkg/k8s/k8s.go @@ -77,6 +77,10 @@ func (c *Client) Clientset() *kubernetes.Clientset { return c.clientset } +func (c *Client) DynamicClient() dynamic.Interface { + return c.dynamicClient +} + func (c *Client) Namespace() string { return c.namespace } diff --git a/pkg/k8s/k8s_endpoint.go b/pkg/k8s/k8s_endpoint.go new file mode 100644 index 0000000..ce3c3f7 --- /dev/null +++ b/pkg/k8s/k8s_endpoint.go @@ -0,0 +1,86 @@ +package k8s + +import ( + "context" + + "github.com/sirupsen/logrus" + + v1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" +) + +// EnsureEndpointWithPort ensures that an endpoint for a given service exists +// with the specified IP and port. If the endpoint does not exist, it creates a new one. +// If it exists but does not have the specified port, it updates the existing endpoint to include the port. +func (c *Client) EnsureEndpointWithPort(ctx context.Context, serviceName, ip string, port int) error { + eCli := c.clientset.CoreV1().Endpoints(c.namespace) + endpoint, err := eCli.Get(ctx, serviceName, metav1.GetOptions{}) + + if err != nil { + if !k8serrors.IsNotFound(err) { + return ErrGetEndpoint.WithParams(serviceName).Wrap(err) + } + return c.createEndpoint(ctx, eCli, serviceName, ip, port) + } + + return c.updateEndpointWithPort(ctx, eCli, endpoint, serviceName, ip, port) +} + +func (c *Client) createEndpoint(ctx context.Context, eCli corev1.EndpointsInterface, serviceName, ip string, port int) error { + endpoint := &v1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceName, + Namespace: c.namespace, + }, + Subsets: []v1.EndpointSubset{ + { + Addresses: []v1.EndpointAddress{{IP: ip}}, + Ports: []v1.EndpointPort{{Port: int32(port), Protocol: v1.ProtocolTCP}}, + }, + }, + } + + _, err := eCli.Create(ctx, endpoint, metav1.CreateOptions{}) + if err != nil { + return ErrCreateEndpoint.WithParams(serviceName).Wrap(err) + } + + logrus.Infof("Endpoint for service %s created with port %d.", serviceName, port) + return nil +} + +func (c *Client) updateEndpointWithPort(ctx context.Context, eCli corev1.EndpointsInterface, endpoint *v1.Endpoints, serviceName, ip string, port int) error { + if endpointHasPort(endpoint, port) { + logrus.Infof("Port %d already exists in endpoint for service %s, no action needed.", port, serviceName) + return nil + } + + if len(endpoint.Subsets) == 0 { + endpoint.Subsets = append(endpoint.Subsets, v1.EndpointSubset{}) + } + + subset := &endpoint.Subsets[0] + subset.Ports = append(subset.Ports, v1.EndpointPort{Port: int32(port), Protocol: v1.ProtocolTCP}) + subset.Addresses = append(subset.Addresses, v1.EndpointAddress{IP: ip}) + + _, err := eCli.Update(ctx, endpoint, metav1.UpdateOptions{}) + if err != nil { + return ErrUpdateEndpoint.WithParams(serviceName).Wrap(err) + } + + logrus.Infof("Port %d added to the existing endpoint for service %s.", port, serviceName) + return nil +} + +func endpointHasPort(endpoint *v1.Endpoints, port int) bool { + for _, subset := range endpoint.Subsets { + for _, p := range subset.Ports { + if int(p.Port) == port { + return true + } + } + } + return false +} diff --git a/pkg/knuu/errors.go b/pkg/knuu/errors.go index 13cc03c..4960bb0 100644 --- a/pkg/knuu/errors.go +++ b/pkg/knuu/errors.go @@ -216,4 +216,8 @@ var ( ErrCustomResourceDefinitionDoesNotExist = &Error{Code: "CustomResourceDefinitionDoesNotExist", Message: "custom resource definition %s does not exist"} ErrCannotDeployTraefik = &Error{Code: "Cannot Deploy Traefik", Message: "cannot deploy Traefik"} ErrGettingBitTwisterPath = &Error{Code: "GettingBitTwisterPath", Message: "error getting BitTwister path"} + ErrFailedToAddHostToTraefik = &Error{Code: "FailedToAddHostToTraefik", Message: "failed to add host to traefik"} + ErrParentInstanceIsNil = &Error{Code: "ParentInstanceIsNil", Message: "parent instance is nil for the sidecar '%s'"} + ErrFailedToGetIP = &Error{Code: "FailedToGetIP", Message: "failed to get IP for service %s"} + ErrFailedToEnsureEndpointWithPort = &Error{Code: "FailedToEnsureEndpointWithPort", Message: "failed to ensure endpoint with port"} ) diff --git a/pkg/knuu/instance_helper.go b/pkg/knuu/instance_helper.go index 4672d55..badc63b 100644 --- a/pkg/knuu/instance_helper.go +++ b/pkg/knuu/instance_helper.go @@ -14,7 +14,6 @@ import ( "k8s.io/apimachinery/pkg/api/resource" "github.com/celestiaorg/knuu/pkg/k8s" - "github.com/celestiaorg/knuu/pkg/traefik" "github.com/google/uuid" "github.com/sirupsen/logrus" ) @@ -84,9 +83,19 @@ func (i *Instance) Labels() map[string]string { // deployService deploys the service for the instance func (i *Instance) deployService(ctx context.Context) error { labels := i.getLabels() - selectorMap := i.getLabels() - service, err := k8sClient.CreateService(ctx, i.k8sName, labels, selectorMap, i.portsTCP, i.portsUDP) + // sidecars are deployed as services to the parent instance + // therefore the service name is the parent instance name + serviceName := i.k8sName + if i.isSidecar { + if i.parentInstance == nil { + return ErrParentInstanceIsNil.WithParams(i.k8sName) + } + labels["app"] = i.parentInstance.k8sName + serviceName = i.parentInstance.k8sName + } + + service, err := k8sClient.CreateService(ctx, serviceName, labels, labels, i.portsTCP, i.portsUDP) if err != nil { return ErrDeployingService.WithParams(i.k8sName).Wrap(err) } @@ -97,18 +106,27 @@ func (i *Instance) deployService(ctx context.Context) error { // patchService patches the service for the instance func (i *Instance) patchService(ctx context.Context) error { + // sidecars are deployed as services to the parent instance + // therefore the service name is the parent instance name + serviceName := i.k8sName + if i.isSidecar { + if i.parentInstance == nil { + return ErrParentInstanceIsNil.WithParams(i.k8sName) + } + serviceName = i.parentInstance.k8sName + } if i.kubernetesService == nil { - svc, err := k8sClient.GetService(ctx, i.k8sName) + svc, err := k8sClient.GetService(ctx, serviceName) if err != nil { - return ErrGettingService.WithParams(i.k8sName).Wrap(err) + return ErrGettingService.WithParams(serviceName).Wrap(err) } i.kubernetesService = svc } - err := k8sClient.PatchService(ctx, i.k8sName, i.kubernetesService.ObjectMeta.Labels, i.kubernetesService.Spec.Selector, i.portsTCP, i.portsUDP) + err := k8sClient.PatchService(ctx, serviceName, i.kubernetesService.ObjectMeta.Labels, i.kubernetesService.Spec.Selector, i.portsTCP, i.portsUDP) if err != nil { - return ErrPatchingService.WithParams(i.k8sName).Wrap(err) + return ErrPatchingService.WithParams(serviceName).Wrap(err) } - logrus.Debugf("Patched service '%s'", i.k8sName) + logrus.Debugf("Patched service '%s'", serviceName) return nil } @@ -184,17 +202,25 @@ func (i *Instance) destroyPod(ctx context.Context) error { // deployService deploys the service for the instance func (i *Instance) deployOrPatchService(ctx context.Context) error { if len(i.portsTCP) != 0 || len(i.portsUDP) != 0 { - logrus.Debugf("Ports not empty, deploying service for instance '%s'", i.k8sName) - svc, _ := k8sClient.GetService(ctx, i.k8sName) + serviceName := i.k8sName + if i.isSidecar { + if i.parentInstance == nil { + return ErrParentInstanceIsNil.WithParams(i.k8sName) + } + serviceName = i.parentInstance.k8sName + } + + logrus.Debugf("Ports not empty, deploying service for instance '%s'", serviceName) + svc, _ := k8sClient.GetService(ctx, serviceName) if svc == nil { err := i.deployService(ctx) if err != nil { - return ErrDeployingServiceForInstance.WithParams(i.k8sName).Wrap(err) + return ErrDeployingServiceForInstance.WithParams(serviceName).Wrap(err) } } else if svc != nil { err := i.patchService(ctx) if err != nil { - return ErrPatchingServiceForInstance.WithParams(i.k8sName).Wrap(err) + return ErrPatchingServiceForInstance.WithParams(serviceName).Wrap(err) } } } @@ -269,6 +295,30 @@ func (i *Instance) deployResources(ctx context.Context) error { if err := i.deployOrPatchService(ctx); err != nil { return ErrFailedToDeployOrPatchService.Wrap(err) } + + serviceName := i.k8sName + if i.isSidecar { + if i.parentInstance == nil { + return ErrParentInstanceIsNil.WithParams(i.k8sName) + } + serviceName = i.parentInstance.k8sName + } + + serviceIP, err := i.GetIP() + if err != nil { + return ErrFailedToGetIP.Wrap(err) + } + for _, port := range i.portsTCP { + err := k8sClient.EnsureEndpointWithPort(ctx, serviceName, serviceIP, port) + if err != nil { + return ErrFailedToEnsureEndpointWithPort.Wrap(err) + } + } + + err = traefikClient.AddHost(ctx, serviceName, i.k8sName, i.portsTCP...) + if err != nil { + return ErrFailedToAddHostToTraefik.Wrap(err) + } } if len(i.volumes) != 0 { if err := i.deployVolume(ctx); err != nil { @@ -473,7 +523,6 @@ func (i *Instance) prepareReplicaSetConfig() k8s.ReplicaSetConfig { } // Generate the sidecar configurations sidecarConfigs := make([]k8s.ContainerConfig, 0) - annotations := make(map[string]string) for _, sidecar := range i.sidecars { sidecarConfigs = append(sidecarConfigs, k8s.ContainerConfig{ Name: sidecar.k8sName, @@ -491,12 +540,6 @@ func (i *Instance) prepareReplicaSetConfig() k8s.ReplicaSetConfig { Files: sidecar.files, SecurityContext: prepareSecurityContext(sidecar.securityContext), }) - - sann := traefik.Annotate(sidecar.k8sName, sidecar.portsTCP[0]) - // append sann to the annotations - for k, v := range sann { - annotations[k] = v - } } // Generate the pod configuration podConfig := k8s.PodConfig{ @@ -507,7 +550,6 @@ func (i *Instance) prepareReplicaSetConfig() k8s.ReplicaSetConfig { FsGroup: i.fsGroup, ContainerConfig: containerConfig, SidecarConfigs: sidecarConfigs, - Annotations: annotations, } // Generate the ReplicaSet configuration statefulSetConfig := k8s.ReplicaSetConfig{ @@ -616,7 +658,7 @@ func (i *Instance) createBitTwisterInstance() (*Instance, error) { if err != nil { return nil, ErrGettingBitTwisterPath.Wrap(err) } - logrus.Debugf("BitTwister URL: ", btURL) + // logrus.Debugf("BitTwister URL: ", btURL) logrus.Info("BitTwister URL: ", btURL) i.BitTwister.SetNewClientByURL(btURL) diff --git a/pkg/traefik/errors.go b/pkg/traefik/errors.go index 3a829b3..807c1ab 100644 --- a/pkg/traefik/errors.go +++ b/pkg/traefik/errors.go @@ -43,4 +43,7 @@ var ( ErrTraefikRoleCreationFailed = &Error{Code: "TraefikRoleCreationFailed", Message: "error creating Traefik role"} ErrTraefikRoleBindingCreationFailed = &Error{Code: "TraefikRoleBindingCreationFailed", Message: "error creating Traefik role binding"} ErrFailedToCreateServiceAccount = &Error{Code: "FailedToCreateServiceAccount", Message: "error creating service account"} + ErrTraefikMiddlewareCreationFailed = &Error{Code: "TraefikMiddlewareCreationFailed", Message: "error creating Traefik middleware"} + ErrTraefikIngressRouteCreationFailed = &Error{Code: "TraefikIngressRouteCreationFailed", Message: "error creating Traefik ingress route"} + ErrGeneratingRandomK8sName = &Error{Code: "GeneratingRandomK8sName", Message: "error generating random K8s name"} ) diff --git a/pkg/traefik/traefik.go b/pkg/traefik/traefik.go index a506925..8bb03a5 100644 --- a/pkg/traefik/traefik.go +++ b/pkg/traefik/traefik.go @@ -10,6 +10,7 @@ import ( "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -50,18 +51,41 @@ func (t *Traefik) Deploy(ctx context.Context) error { return ErrFailedToCreateServiceAccount.Wrap(err) } - // // Define and create a role for Traefik - // err = t.K8s.CreateRole(ctx, roleName, nil, []rbacv1.PolicyRule{ - // { - // APIGroups: []string{""}, - // Resources: []string{"pods"}, - // Verbs: []string{"get", "list", "watch"}, - // }, - // }) + clusterRoleName, err := names.NewRandomK8(roleName) + if err != nil { + return err + } - // if err != nil { - // return ErrTraefikRoleCreationFailed.Wrap(err) - // } + // Define and create a ClusterRole for Traefik + err = t.K8s.CreateClusterRole(ctx, clusterRoleName, nil, []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, // Core group + Resources: []string{"pods", "endpoints", "secrets", "services"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"traefik.io"}, // Traefik specific resources + Resources: []string{ + "ingressroutes", "middlewares", "tlsstores", "serverstransporttcps", + "traefikservices", "ingressrouteudps", "middlewaretcps", "tlsoptions", + "serverstransports", "ingressroutetcps", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"networking.k8s.io"}, // Networking resources + Resources: []string{"ingresses", "ingressclasses"}, + Verbs: []string{"get", "list", "watch"}, + }, + }) + + if err != nil { + return ErrTraefikRoleCreationFailed.Wrap(err) + } + + if err := t.K8s.CreateClusterRoleBinding(ctx, clusterRoleName, nil, clusterRoleName, serviceAccountName); err != nil { + return ErrTraefikRoleBindingCreationFailed.Wrap(err) + } // Create the Traefik deployment using the service account traefikDeployment := &appsv1.Deployment{ @@ -130,14 +154,14 @@ func (t *Traefik) IP(ctx context.Context) (string, error) { return t.K8s.GetServiceIP(ctx, traefikServiceName) } -func (t *Traefik) URL(ctx context.Context, name string) (string, error) { +func (t *Traefik) URL(ctx context.Context, serviceName string) (string, error) { if t.endpoint == "" { var err error if t.endpoint, err = t.Endpoint(ctx); err != nil { return "", ErrTraefikIPNotFound.Wrap(err) } } - return fmt.Sprintf("http://%s/%s", t.endpoint, name), nil + return fmt.Sprintf("http://%s/%s", t.endpoint, serviceName), nil } func (t *Traefik) Endpoint(ctx context.Context) (string, error) { @@ -147,31 +171,18 @@ func (t *Traefik) Endpoint(ctx context.Context) (string, error) { return t.K8s.GetServiceEndpoint(ctx, traefikServiceName) } -func (t *Traefik) AddHost(ctx context.Context, serviceName string, port int) error { - middleware := &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "traefik.io/v1alpha1", - "kind": "Middleware", - "metadata": map[string]interface{}{ - "name": "strip-dummy", - "namespace": "my-traefik-namespace", - }, - "spec": map[string]interface{}{ - "stripPrefix": map[string]interface{}{ - "prefixes": []string{"/dummy"}, - }, - }, - }, +func (t *Traefik) AddHost(ctx context.Context, serviceName, prefix string, portsTCP ...int) error { + middlewareName, err := names.NewRandomK8("strip-" + prefix) + if err != nil { + return ErrGeneratingRandomK8sName.Wrap(err) } - middlewareResource := schema.GroupVersionResource{ - Group: "traefik.io", - Version: "v1alpha1", - Resource: "middlewares", + // middleware is required to strip the prefix from the service name + if err := t.createMiddleware(ctx, prefix, middlewareName); err != nil { + return err } - t.K8s.Clientset(). - + return t.createIngressRoute(ctx, serviceName, prefix, []string{middlewareName}, portsTCP) } // TODO: need to update the k8s pkg to handle service creation in more custom way @@ -211,3 +222,95 @@ func (t *Traefik) createService(ctx context.Context) error { logrus.Debugf("Service %s created successfully.", traefikServiceName) return nil } + +func (t *Traefik) createMiddleware(ctx context.Context, serviceName, middlewareName string) error { + middleware := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "traefik.io/v1alpha1", + "kind": "Middleware", + "metadata": map[string]interface{}{ + "name": middlewareName, + "namespace": t.K8s.Namespace(), + }, + "spec": map[string]interface{}{ + "stripPrefix": map[string]interface{}{ + "prefixes": []string{"/" + serviceName}, + }, + }, + }, + } + + middlewareResource := schema.GroupVersionResource{ + Group: "traefik.io", + Version: "v1alpha1", + Resource: "middlewares", + } + + _, err := t.K8s.DynamicClient().Resource(middlewareResource).Namespace(t.K8s.Namespace()).Create(ctx, middleware, metav1.CreateOptions{}) + if err != nil { + return ErrTraefikMiddlewareCreationFailed.Wrap(err) + } + return nil +} + +func (t *Traefik) createIngressRoute( + ctx context.Context, + serviceName, prefix string, + middlewaresNames []string, + ports []int, +) error { + ingressRouteGVR := schema.GroupVersionResource{ + Group: "traefik.io", + Version: "v1alpha1", + Resource: "ingressroutes", + } + + ingressRouteName, err := names.NewRandomK8("ing-route-" + prefix) + if err != nil { + return ErrTraefikIngressRouteCreationFailed.Wrap(err) + } + + services := make([]interface{}, len(ports)) + for i, port := range ports { + services[i] = map[string]interface{}{ + "name": serviceName, + "port": port, + } + } + + middlewares := make([]interface{}, len(middlewaresNames)) + for i, name := range middlewaresNames { + middlewares[i] = map[string]interface{}{ + "name": name, + } + } + + ingressRoute := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "traefik.io/v1alpha1", + "kind": "IngressRoute", + "metadata": map[string]interface{}{ + "name": ingressRouteName, + "namespace": t.K8s.Namespace(), + }, + "spec": map[string]interface{}{ + "entryPoints": []string{"web"}, + "routes": []interface{}{ + map[string]interface{}{ + "match": fmt.Sprintf("PathPrefix(`/%s`)", prefix), + "kind": "Rule", + "services": services, + "middlewares": middlewares, + }, + }, + }, + }, + } + + _, err = t.K8s.DynamicClient().Resource(ingressRouteGVR).Namespace(t.K8s.Namespace()).Create(ctx, ingressRoute, metav1.CreateOptions{}) + if err != nil { + return ErrTraefikIngressRouteCreationFailed.Wrap(err) + } + + return nil +} diff --git a/proxy_sa.sh b/proxy_sa.sh new file mode 100755 index 0000000..89ee288 --- /dev/null +++ b/proxy_sa.sh @@ -0,0 +1,136 @@ +#!/bin/bash + +# Step 1: Create a new namespace +kubectl create namespace my-traefik-namespace + +kubectl create serviceaccount traefik-service-account -n my-traefik-namespace + + +# Step 2: Deploy Traefik with a specific Service Account and Roles +helm repo add traefik https://helm.traefik.io/traefik +helm repo update +helm install traefik traefik/traefik \ + --namespace my-traefik-namespace \ + --create-namespace \ + --set="additionalArguments={--api.insecure=true,--providers.kubernetesIngress,--providers.kubernetesCRD}" \ + --set serviceAccount.create=true \ + --set serviceAccount.name=traefik-service-account \ + --set rbac.enabled=true + +# Wait for Traefik to be ready +echo "Waiting for Traefik to be ready..." +kubectl wait --namespace my-traefik-namespace --for=condition=ready pod --selector="app.kubernetes.io/name=traefik" --timeout=90s +# Step 3: Deploy a dummy webserver with its own Service Account and Role +## Create Service Account for dummy webserver +kubectl create serviceaccount dummy-webserver-account -n my-traefik-namespace + +## Create Role and RoleBinding for dummy webserver +cat < Date: Fri, 17 May 2024 13:17:33 +0200 Subject: [PATCH 3/9] fix: the service path & prefixes --- e2e/basic/reverse_proxy_test.go | 33 ------------------------- pkg/knuu/errors.go | 2 ++ pkg/knuu/instance_helper.go | 23 +++++++---------- pkg/traefik/traefik.go | 44 ++++++++++++++------------------- 4 files changed, 30 insertions(+), 72 deletions(-) diff --git a/e2e/basic/reverse_proxy_test.go b/e2e/basic/reverse_proxy_test.go index 2f0b395..f988540 100644 --- a/e2e/basic/reverse_proxy_test.go +++ b/e2e/basic/reverse_proxy_test.go @@ -2,9 +2,7 @@ package basic import ( "context" - "fmt" "os" - "os/signal" "testing" "time" @@ -38,40 +36,15 @@ func TestReverseProxy(t *testing.T) { require.NoError(t, main.Destroy(), "Error destroying instance") }) - // Prepare iperf client & server - require.NoError(t, main.EnableBitTwister(), "Error enabling BitTwister") require.NoError(t, main.Start(), "Error starting main instance") ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() - { - out, err := main.BitTwister.Client().AllServicesStatus() - fmt.Printf("err: %v\n", err) - - // wait for Ctrl+C signal for 10 minutes - // Setup a channel to listen for the interrupt signal (Ctrl+C) - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, os.Interrupt) - - // Create a timeout to stop waiting after 10 minutes - timeout := time.After(30 * time.Minute) - - select { - case <-sigChan: - fmt.Println("Ctrl+C received, stopping the test.") - case <-timeout: - fmt.Println("30 minutes passed without Ctrl+C, continuing the test.") - } - - require.NoError(t, err, "Error getting all services status") - assert.NotEmpty(t, out, "No services found") - } require.NoError(t, main.BitTwister.WaitForStart(ctx), "Error waiting for BitTwister to start") // test if BitTwister running in a sidecar is accessible - err = main.SetBandwidthLimit(1000) assert.NoError(t, err, "Error setting bandwidth limit") @@ -79,10 +52,4 @@ func TestReverseProxy(t *testing.T) { out, err := main.BitTwister.Client().AllServicesStatus() require.NoError(t, err, "Error getting all services status") assert.NotEmpty(t, out, "No services found") - - for _, svc := range out { - fmt.Printf("\nsvc: %#v\n", svc) - } } - -// reset && LOG_LEVEL=debug go test -v ./e2e/basic/ --run TestReverseProxy -timeout 60m diff --git a/pkg/knuu/errors.go b/pkg/knuu/errors.go index 72b4c41..059139b 100644 --- a/pkg/knuu/errors.go +++ b/pkg/knuu/errors.go @@ -227,4 +227,6 @@ var ( ErrParentInstanceIsNil = &Error{Code: "ParentInstanceIsNil", Message: "parent instance is nil for the sidecar '%s'"} ErrFailedToGetIP = &Error{Code: "FailedToGetIP", Message: "failed to get IP for service %s"} ErrFailedToEnsureEndpointWithPort = &Error{Code: "FailedToEnsureEndpointWithPort", Message: "failed to ensure endpoint with port"} + ErrNoParentInstance = &Error{Code: "NoParentInstance", Message: "no parent instance for the sidecar '%s'"} + ErrAddingToTraefikProxy = &Error{Code: "AddingToTraefikProxy", Message: "error adding '%s' to traefik proxy for service '%s'"} ) diff --git a/pkg/knuu/instance_helper.go b/pkg/knuu/instance_helper.go index c4fe14a..5480165 100644 --- a/pkg/knuu/instance_helper.go +++ b/pkg/knuu/instance_helper.go @@ -197,12 +197,12 @@ func (i *Instance) deployOrPatchService(ctx context.Context, portsTCP, portsUDP if svc == nil { err := i.deployService(ctx, portsTCP, portsUDP) if err != nil { - return ErrDeployingServiceForInstance.WithParams(serviceName).Wrap(err) + return ErrDeployingServiceForInstance.WithParams(i.k8sName).Wrap(err) } } else if svc != nil { err := i.patchService(ctx, portsTCP, portsUDP) if err != nil { - return ErrPatchingServiceForInstance.WithParams(serviceName).Wrap(err) + return ErrPatchingServiceForInstance.WithParams(i.k8sName).Wrap(err) } } } @@ -607,26 +607,21 @@ func (i *Instance) createBitTwisterInstance() (*Instance, error) { return nil, ErrAddingBitTwisterPort.Wrap(err) } - // We need to add the port here so the instance will get an IP - // if err := i.AddPortTCP(1234); err != nil { - // return nil, ErrAddingBitTwisterPort.Wrap(err) - // } - // ip, err := i.GetIP() - // if err != nil { - // return nil, ErrGettingInstanceIP.WithParams(i.name).Wrap(err) - // } - // logrus.Debugf("IP of instance '%s' is '%s'", i.name, ip) - // TODO: remove this when pkg refactor ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() + serviceName := i.k8sName // the main instance name + err = traefikClient.AddHost(ctx, serviceName, bt.k8sName, i.BitTwister.Port()) + if err != nil { + return nil, ErrAddingToTraefikProxy.WithParams(bt.k8sName, serviceName).Wrap(err) + } + btURL, err := traefikClient.URL(ctx, bt.k8sName) if err != nil { return nil, ErrGettingBitTwisterPath.Wrap(err) } - // logrus.Debugf("BitTwister URL: ", btURL) - logrus.Info("BitTwister URL: ", btURL) + logrus.Debugf("BitTwister URL: %s", btURL) i.BitTwister.SetNewClientByURL(btURL) diff --git a/pkg/traefik/traefik.go b/pkg/traefik/traefik.go index 8bb03a5..071d97d 100644 --- a/pkg/traefik/traefik.go +++ b/pkg/traefik/traefik.go @@ -154,14 +154,14 @@ func (t *Traefik) IP(ctx context.Context) (string, error) { return t.K8s.GetServiceIP(ctx, traefikServiceName) } -func (t *Traefik) URL(ctx context.Context, serviceName string) (string, error) { +func (t *Traefik) URL(ctx context.Context, prefix string) (string, error) { if t.endpoint == "" { var err error if t.endpoint, err = t.Endpoint(ctx); err != nil { return "", ErrTraefikIPNotFound.Wrap(err) } } - return fmt.Sprintf("http://%s/%s", t.endpoint, serviceName), nil + return fmt.Sprintf("http://%s/%s", t.endpoint, prefix), nil } func (t *Traefik) Endpoint(ctx context.Context) (string, error) { @@ -171,7 +171,7 @@ func (t *Traefik) Endpoint(ctx context.Context) (string, error) { return t.K8s.GetServiceEndpoint(ctx, traefikServiceName) } -func (t *Traefik) AddHost(ctx context.Context, serviceName, prefix string, portsTCP ...int) error { +func (t *Traefik) AddHost(ctx context.Context, serviceName, prefix string, portTCP int) error { middlewareName, err := names.NewRandomK8("strip-" + prefix) if err != nil { return ErrGeneratingRandomK8sName.Wrap(err) @@ -182,7 +182,7 @@ func (t *Traefik) AddHost(ctx context.Context, serviceName, prefix string, ports return err } - return t.createIngressRoute(ctx, serviceName, prefix, []string{middlewareName}, portsTCP) + return t.createIngressRoute(ctx, serviceName, prefix, middlewareName, portTCP) } // TODO: need to update the k8s pkg to handle service creation in more custom way @@ -256,8 +256,8 @@ func (t *Traefik) createMiddleware(ctx context.Context, serviceName, middlewareN func (t *Traefik) createIngressRoute( ctx context.Context, serviceName, prefix string, - middlewaresNames []string, - ports []int, + middlewareName string, + port int, ) error { ingressRouteGVR := schema.GroupVersionResource{ Group: "traefik.io", @@ -270,21 +270,6 @@ func (t *Traefik) createIngressRoute( return ErrTraefikIngressRouteCreationFailed.Wrap(err) } - services := make([]interface{}, len(ports)) - for i, port := range ports { - services[i] = map[string]interface{}{ - "name": serviceName, - "port": port, - } - } - - middlewares := make([]interface{}, len(middlewaresNames)) - for i, name := range middlewaresNames { - middlewares[i] = map[string]interface{}{ - "name": name, - } - } - ingressRoute := &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "traefik.io/v1alpha1", @@ -297,10 +282,19 @@ func (t *Traefik) createIngressRoute( "entryPoints": []string{"web"}, "routes": []interface{}{ map[string]interface{}{ - "match": fmt.Sprintf("PathPrefix(`/%s`)", prefix), - "kind": "Rule", - "services": services, - "middlewares": middlewares, + "match": fmt.Sprintf("PathPrefix(`/%s`)", prefix), + "kind": "Rule", + "services": []interface{}{ + map[string]interface{}{ + "name": serviceName, + "port": port, + }, + }, + "middlewares": []interface{}{ + map[string]interface{}{ + "name": middlewareName, + }, + }, }, }, }, From f10dc8c1510eb4ad736138cab494f30431692802 Mon Sep 17 00:00:00 2001 From: Mojtaba Date: Tue, 21 May 2024 14:21:03 +0200 Subject: [PATCH 4/9] fix: applied the requested changes --- pkg/k8s/k8s_deployment.go | 26 +++++++ pkg/k8s/k8s_endpoint.go | 86 ----------------------- pkg/k8s/k8s_pod.go | 18 ----- pkg/k8s/k8s_service.go | 16 ++--- pkg/knuu/errors.go | 2 +- pkg/knuu/instance_helper.go | 2 +- pkg/knuu/knuu.go | 4 +- pkg/traefik/errors.go | 1 + pkg/traefik/traefik.go | 35 +++++++++- proxy_sa.sh | 136 ------------------------------------ 10 files changed, 73 insertions(+), 253 deletions(-) create mode 100644 pkg/k8s/k8s_deployment.go delete mode 100644 pkg/k8s/k8s_endpoint.go delete mode 100755 proxy_sa.sh diff --git a/pkg/k8s/k8s_deployment.go b/pkg/k8s/k8s_deployment.go new file mode 100644 index 0000000..41b898b --- /dev/null +++ b/pkg/k8s/k8s_deployment.go @@ -0,0 +1,26 @@ +package k8s + +import ( + "context" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (c *Client) WaitForDeployment(ctx context.Context, name string) error { + for { + deployment, err := c.clientset.AppsV1().Deployments(c.namespace).Get(ctx, name, metav1.GetOptions{}) + if err == nil && deployment.Status.ReadyReplicas > 0 { + break + } + + select { + case <-ctx.Done(): + return ErrWaitingForDeployment.WithParams(name).Wrap(err) + case <-time.After(waitRetry): + // Retry after some seconds + } + } + + return nil +} diff --git a/pkg/k8s/k8s_endpoint.go b/pkg/k8s/k8s_endpoint.go deleted file mode 100644 index ce3c3f7..0000000 --- a/pkg/k8s/k8s_endpoint.go +++ /dev/null @@ -1,86 +0,0 @@ -package k8s - -import ( - "context" - - "github.com/sirupsen/logrus" - - v1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - corev1 "k8s.io/client-go/kubernetes/typed/core/v1" -) - -// EnsureEndpointWithPort ensures that an endpoint for a given service exists -// with the specified IP and port. If the endpoint does not exist, it creates a new one. -// If it exists but does not have the specified port, it updates the existing endpoint to include the port. -func (c *Client) EnsureEndpointWithPort(ctx context.Context, serviceName, ip string, port int) error { - eCli := c.clientset.CoreV1().Endpoints(c.namespace) - endpoint, err := eCli.Get(ctx, serviceName, metav1.GetOptions{}) - - if err != nil { - if !k8serrors.IsNotFound(err) { - return ErrGetEndpoint.WithParams(serviceName).Wrap(err) - } - return c.createEndpoint(ctx, eCli, serviceName, ip, port) - } - - return c.updateEndpointWithPort(ctx, eCli, endpoint, serviceName, ip, port) -} - -func (c *Client) createEndpoint(ctx context.Context, eCli corev1.EndpointsInterface, serviceName, ip string, port int) error { - endpoint := &v1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: serviceName, - Namespace: c.namespace, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{{IP: ip}}, - Ports: []v1.EndpointPort{{Port: int32(port), Protocol: v1.ProtocolTCP}}, - }, - }, - } - - _, err := eCli.Create(ctx, endpoint, metav1.CreateOptions{}) - if err != nil { - return ErrCreateEndpoint.WithParams(serviceName).Wrap(err) - } - - logrus.Infof("Endpoint for service %s created with port %d.", serviceName, port) - return nil -} - -func (c *Client) updateEndpointWithPort(ctx context.Context, eCli corev1.EndpointsInterface, endpoint *v1.Endpoints, serviceName, ip string, port int) error { - if endpointHasPort(endpoint, port) { - logrus.Infof("Port %d already exists in endpoint for service %s, no action needed.", port, serviceName) - return nil - } - - if len(endpoint.Subsets) == 0 { - endpoint.Subsets = append(endpoint.Subsets, v1.EndpointSubset{}) - } - - subset := &endpoint.Subsets[0] - subset.Ports = append(subset.Ports, v1.EndpointPort{Port: int32(port), Protocol: v1.ProtocolTCP}) - subset.Addresses = append(subset.Addresses, v1.EndpointAddress{IP: ip}) - - _, err := eCli.Update(ctx, endpoint, metav1.UpdateOptions{}) - if err != nil { - return ErrUpdateEndpoint.WithParams(serviceName).Wrap(err) - } - - logrus.Infof("Port %d added to the existing endpoint for service %s.", port, serviceName) - return nil -} - -func endpointHasPort(endpoint *v1.Endpoints, port int) bool { - for _, subset := range endpoint.Subsets { - for _, p := range subset.Ports { - if int(p.Port) == port { - return true - } - } - } - return false -} diff --git a/pkg/k8s/k8s_pod.go b/pkg/k8s/k8s_pod.go index bd57242..04c8497 100644 --- a/pkg/k8s/k8s_pod.go +++ b/pkg/k8s/k8s_pod.go @@ -315,24 +315,6 @@ func (c *Client) getPod(ctx context.Context, name string) (*v1.Pod, error) { return pod, nil } -func (c *Client) WaitForDeployment(ctx context.Context, name string) error { - for { - deployment, err := c.clientset.AppsV1().Deployments(c.namespace).Get(ctx, name, metav1.GetOptions{}) - if err == nil && deployment.Status.ReadyReplicas > 0 { - break - } - - select { - case <-ctx.Done(): - return ErrWaitingForDeployment.WithParams(name).Wrap(err) - case <-time.After(waitRetry): - // Retry after some seconds - } - } - - return nil -} - // buildEnv builds an environment variable configuration for a Pod based on the given map of key-value pairs. func buildEnv(envMap map[string]string) []v1.EnvVar { envVars := make([]v1.EnvVar, 0, len(envMap)) diff --git a/pkg/k8s/k8s_service.go b/pkg/k8s/k8s_service.go index cf54ff2..4d46651 100644 --- a/pkg/k8s/k8s_service.go +++ b/pkg/k8s/k8s_service.go @@ -172,7 +172,7 @@ func (c *Client) WaitForService(ctx context.Context, name string) error { } if err := checkServiceConnectivity(endpoint); err != nil { - time.Sleep(waitRetry) // Retry after some seconds if Minio is not reachable + time.Sleep(waitRetry) // Retry after some seconds if the service is not reachable continue } @@ -188,20 +188,20 @@ func (c *Client) WaitForService(ctx context.Context, name string) error { } func (c *Client) GetServiceEndpoint(ctx context.Context, name string) (string, error) { - minioService, err := c.clientset.CoreV1().Services(c.namespace).Get(ctx, name, metav1.GetOptions{}) + srv, err := c.clientset.CoreV1().Services(c.namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { return "", ErrGettingService.WithParams(name).Wrap(err) } - if minioService.Spec.Type == v1.ServiceTypeLoadBalancer { + if srv.Spec.Type == v1.ServiceTypeLoadBalancer { // Use the LoadBalancer's external IP - if len(minioService.Status.LoadBalancer.Ingress) > 0 { - return fmt.Sprintf("%s:%d", minioService.Status.LoadBalancer.Ingress[0].IP, minioService.Spec.Ports[0].Port), nil + if len(srv.Status.LoadBalancer.Ingress) > 0 { + return fmt.Sprintf("%s:%d", srv.Status.LoadBalancer.Ingress[0].IP, srv.Spec.Ports[0].Port), nil } return "", ErrLoadBalancerIPNotAvailable } - if minioService.Spec.Type == v1.ServiceTypeNodePort { + if srv.Spec.Type == v1.ServiceTypeNodePort { // Use the Node IP and NodePort nodes, err := c.clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { @@ -219,10 +219,10 @@ func (c *Client) GetServiceEndpoint(ctx context.Context, name string) (string, e break } } - return fmt.Sprintf("%s:%d", nodeIP, minioService.Spec.Ports[0].NodePort), nil + return fmt.Sprintf("%s:%d", nodeIP, srv.Spec.Ports[0].NodePort), nil } - return fmt.Sprintf("%s:%d", minioService.Spec.ClusterIP, minioService.Spec.Ports[0].Port), nil + return fmt.Sprintf("%s:%d", srv.Spec.ClusterIP, srv.Spec.Ports[0].Port), nil } func checkServiceConnectivity(serviceEndpoint string) error { diff --git a/pkg/knuu/errors.go b/pkg/knuu/errors.go index 059139b..1c16d9b 100644 --- a/pkg/knuu/errors.go +++ b/pkg/knuu/errors.go @@ -226,7 +226,7 @@ var ( ErrFailedToAddHostToTraefik = &Error{Code: "FailedToAddHostToTraefik", Message: "failed to add host to traefik"} ErrParentInstanceIsNil = &Error{Code: "ParentInstanceIsNil", Message: "parent instance is nil for the sidecar '%s'"} ErrFailedToGetIP = &Error{Code: "FailedToGetIP", Message: "failed to get IP for service %s"} - ErrFailedToEnsureEndpointWithPort = &Error{Code: "FailedToEnsureEndpointWithPort", Message: "failed to ensure endpoint with port"} ErrNoParentInstance = &Error{Code: "NoParentInstance", Message: "no parent instance for the sidecar '%s'"} ErrAddingToTraefikProxy = &Error{Code: "AddingToTraefikProxy", Message: "error adding '%s' to traefik proxy for service '%s'"} + ErrCannotGetTraefikEndpoint = &Error{Code: "CannotGetTraefikEndpoint", Message: "cannot get traefik endpoint"} ) diff --git a/pkg/knuu/instance_helper.go b/pkg/knuu/instance_helper.go index 5480165..1955a5a 100644 --- a/pkg/knuu/instance_helper.go +++ b/pkg/knuu/instance_helper.go @@ -602,7 +602,7 @@ func (i *Instance) createBitTwisterInstance() (*Instance, error) { return nil, ErrSettingBitTwisterImage.Wrap(err) } - // This is needed for reverse proxy annotation + // This is needed to make BT reachable if err := bt.AddPortTCP(i.BitTwister.Port()); err != nil { return nil, ErrAddingBitTwisterPort.Wrap(err) } diff --git a/pkg/knuu/knuu.go b/pkg/knuu/knuu.go index 69338cb..4c795d5 100644 --- a/pkg/knuu/knuu.go +++ b/pkg/knuu/knuu.go @@ -110,9 +110,9 @@ func InitializeWithScope(scope string) error { publicIP, err := traefikClient.Endpoint(ctx) if err != nil { - fmt.Printf("Error getting traefik Endpoint: %v", err) + return ErrCannotGetTraefikEndpoint.Wrap(err) } - fmt.Printf("Traefik publicIP: %v\n", publicIP) + logrus.Debugf("Traefik publicIP: %v\n", publicIP) minioClient = &minio.Minio{ Clientset: k8sClient.Clientset(), diff --git a/pkg/traefik/errors.go b/pkg/traefik/errors.go index 807c1ab..89a7302 100644 --- a/pkg/traefik/errors.go +++ b/pkg/traefik/errors.go @@ -46,4 +46,5 @@ var ( ErrTraefikMiddlewareCreationFailed = &Error{Code: "TraefikMiddlewareCreationFailed", Message: "error creating Traefik middleware"} ErrTraefikIngressRouteCreationFailed = &Error{Code: "TraefikIngressRouteCreationFailed", Message: "error creating Traefik ingress route"} ErrGeneratingRandomK8sName = &Error{Code: "GeneratingRandomK8sName", Message: "error generating random K8s name"} + ErrTraefikFailedToParseQuantity = &Error{Code: "TraefikFailedToParseQuantity", Message: "error parsing resource quantity"} ) diff --git a/pkg/traefik/traefik.go b/pkg/traefik/traefik.go index 071d97d..e9115c9 100644 --- a/pkg/traefik/traefik.go +++ b/pkg/traefik/traefik.go @@ -11,6 +11,7 @@ import ( appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -28,8 +29,13 @@ const ( image = "traefik:v3.0" appLabel = "app" appLabelValue = "traefik" - replicas = 2 + replicas = 1 waitRetry = 5 * time.Second + + defaultCPURequest = "1000m" + defaultMemoryRequest = "512Mi" + maxCPULimit = "2000m" + maxMemoryLimit = "1024Mi" ) type Traefik struct { @@ -87,6 +93,23 @@ func (t *Traefik) Deploy(ctx context.Context) error { return ErrTraefikRoleBindingCreationFailed.Wrap(err) } + cpuReq, err := resource.ParseQuantity(defaultCPURequest) + if err != nil { + return ErrTraefikFailedToParseQuantity.Wrap(err) + } + memReq, err := resource.ParseQuantity(defaultMemoryRequest) + if err != nil { + return ErrTraefikFailedToParseQuantity.Wrap(err) + } + cpuLimit, err := resource.ParseQuantity(maxCPULimit) + if err != nil { + return ErrTraefikFailedToParseQuantity.Wrap(err) + } + memLimit, err := resource.ParseQuantity(maxMemoryLimit) + if err != nil { + return ErrTraefikFailedToParseQuantity.Wrap(err) + } + // Create the Traefik deployment using the service account traefikDeployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ @@ -120,6 +143,16 @@ func (t *Traefik) Deploy(ctx context.Context) error { {ContainerPort: Port, Name: "web"}, {ContainerPort: PortSecure, Name: "websecure"}, }, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpuReq, + v1.ResourceMemory: memReq, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: cpuLimit, + v1.ResourceMemory: memLimit, + }, + }, }, }, }, diff --git a/proxy_sa.sh b/proxy_sa.sh deleted file mode 100755 index 89ee288..0000000 --- a/proxy_sa.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/bash - -# Step 1: Create a new namespace -kubectl create namespace my-traefik-namespace - -kubectl create serviceaccount traefik-service-account -n my-traefik-namespace - - -# Step 2: Deploy Traefik with a specific Service Account and Roles -helm repo add traefik https://helm.traefik.io/traefik -helm repo update -helm install traefik traefik/traefik \ - --namespace my-traefik-namespace \ - --create-namespace \ - --set="additionalArguments={--api.insecure=true,--providers.kubernetesIngress,--providers.kubernetesCRD}" \ - --set serviceAccount.create=true \ - --set serviceAccount.name=traefik-service-account \ - --set rbac.enabled=true - -# Wait for Traefik to be ready -echo "Waiting for Traefik to be ready..." -kubectl wait --namespace my-traefik-namespace --for=condition=ready pod --selector="app.kubernetes.io/name=traefik" --timeout=90s -# Step 3: Deploy a dummy webserver with its own Service Account and Role -## Create Service Account for dummy webserver -kubectl create serviceaccount dummy-webserver-account -n my-traefik-namespace - -## Create Role and RoleBinding for dummy webserver -cat < Date: Wed, 22 May 2024 19:16:03 +0200 Subject: [PATCH 5/9] chore: applied the requested changes --- e2e/basic/reverse_proxy_test.go | 8 +++- pkg/k8s/errors.go | 4 +- pkg/k8s/k8s_role.go | 2 +- pkg/k8s/k8s_rolebinding.go | 2 +- pkg/k8s/k8s_service.go | 73 ++++++++++++++++++--------------- pkg/traefik/errors.go | 3 +- 6 files changed, 53 insertions(+), 39 deletions(-) diff --git a/e2e/basic/reverse_proxy_test.go b/e2e/basic/reverse_proxy_test.go index f988540..4ca636c 100644 --- a/e2e/basic/reverse_proxy_test.go +++ b/e2e/basic/reverse_proxy_test.go @@ -12,6 +12,9 @@ import ( "github.com/stretchr/testify/require" ) +// TestReverseProxy is a test function that verifies the functionality of a reverse proxy setup. +// It mainly tests the ability to reach to a service running in a sidecar like BitTwister. +// It calls an endpoint of the service and checks if the response is as expected. func TestReverseProxy(t *testing.T) { t.Parallel() // Setup @@ -50,6 +53,7 @@ func TestReverseProxy(t *testing.T) { // Check if the BitTwister service is set out, err := main.BitTwister.Client().AllServicesStatus() - require.NoError(t, err, "Error getting all services status") - assert.NotEmpty(t, out, "No services found") + assert.NoError(t, err, "Error getting all services status") + assert.GreaterOrEqual(t, len(out), 1, "No services found") + assert.NotEmpty(t, out[0].Name, "Service name is empty") } diff --git a/pkg/k8s/errors.go b/pkg/k8s/errors.go index d8ec0bc..9a9408b 100644 --- a/pkg/k8s/errors.go +++ b/pkg/k8s/errors.go @@ -1,6 +1,7 @@ package k8s import ( + "errors" "fmt" ) @@ -24,7 +25,7 @@ func (e *Error) Error() string { } func (e *Error) Wrap(err error) error { - e.Err = err + e.Err = errors.Join(e.Err, err) return e } @@ -123,4 +124,5 @@ var ( ErrCreateEndpoint = &Error{Code: "CreateEndpoint", Message: "failed to create endpoint for service %s"} ErrGetEndpoint = &Error{Code: "GetEndpoint", Message: "failed to get endpoint for service %s"} ErrUpdateEndpoint = &Error{Code: "UpdateEndpoint", Message: "failed to update endpoint for service %s"} + ErrCheckingServiceReady = &Error{Code: "CheckingServiceReady", Message: "failed to check if service %s is ready"} ) diff --git a/pkg/k8s/k8s_role.go b/pkg/k8s/k8s_role.go index 17dcc23..f95f433 100644 --- a/pkg/k8s/k8s_role.go +++ b/pkg/k8s/k8s_role.go @@ -39,7 +39,7 @@ func (c *Client) CreateClusterRole( ) error { _, err := c.clientset.RbacV1().ClusterRoles().Get(ctx, name, metav1.GetOptions{}) if err == nil || !errors.IsNotFound(err) { - return ErrClusterRoleAlreadyExists.WithParams(name) + return ErrClusterRoleAlreadyExists.WithParams(name).Wrap(err) } role := &rbacv1.ClusterRole{ diff --git a/pkg/k8s/k8s_rolebinding.go b/pkg/k8s/k8s_rolebinding.go index 0f386c5..eb43d5f 100644 --- a/pkg/k8s/k8s_rolebinding.go +++ b/pkg/k8s/k8s_rolebinding.go @@ -49,7 +49,7 @@ func (c *Client) CreateClusterRoleBinding( ) error { _, err := c.clientset.RbacV1().ClusterRoleBindings().Get(ctx, name, metav1.GetOptions{}) if err == nil || !errors.IsNotFound(err) { - return ErrClusterRoleBindingAlreadyExists.WithParams(name) + return ErrClusterRoleBindingAlreadyExists.WithParams(name).Wrap(err) } role := &rbacv1.ClusterRoleBinding{ diff --git a/pkg/k8s/k8s_service.go b/pkg/k8s/k8s_service.go index 4d46651..1b09745 100644 --- a/pkg/k8s/k8s_service.go +++ b/pkg/k8s/k8s_service.go @@ -146,44 +146,36 @@ func prepareService( } func (c *Client) WaitForService(ctx context.Context, name string) error { - for { - service, err := c.clientset.CoreV1().Services(c.namespace).Get(ctx, name, metav1.GetOptions{}) - if err != nil { - return ErrGettingService.WithParams(name).Wrap(err) - } + ticker := time.NewTicker(waitRetry) + defer ticker.Stop() - if service.Spec.Type == v1.ServiceTypeLoadBalancer { - if len(service.Status.LoadBalancer.Ingress) == 0 { - time.Sleep(waitRetry) - continue // Wait until the LoadBalancer IP is available + for { + select { + case <-ctx.Done(): + return ErrTimeoutWaitingForServiceReady + + case <-ticker.C: + ready, err := c.isServiceReady(ctx, name) + if err != nil { + return ErrCheckingServiceReady.WithParams(name).Wrap(err) } - } else if service.Spec.Type == v1.ServiceTypeNodePort { - if service.Spec.Ports[0].NodePort == 0 { - return ErrNodePortNotSet + if !ready { + continue } - } else if len(service.Spec.ExternalIPs) == 0 { - return ErrExternalIPsNotSet - } - // Check if service is reachable - endpoint, err := c.GetServiceEndpoint(ctx, name) - if err != nil { - return ErrGettingServiceEndpoint.WithParams(name).Wrap(err) - } - - if err := checkServiceConnectivity(endpoint); err != nil { - time.Sleep(waitRetry) // Retry after some seconds if the service is not reachable - continue - } + // Check if service is reachable + endpoint, err := c.GetServiceEndpoint(ctx, name) + if err != nil { + return ErrGettingServiceEndpoint.WithParams(name).Wrap(err) + } - break // Service is reachable, exit the loop - } + if err := checkServiceConnectivity(endpoint); err != nil { + continue + } - select { - case <-ctx.Done(): - return ErrTimeoutWaitingForServiceReady - default: - return nil + // Service is reachable + return nil + } } } @@ -226,10 +218,25 @@ func (c *Client) GetServiceEndpoint(ctx context.Context, name string) (string, e } func checkServiceConnectivity(serviceEndpoint string) error { - conn, err := net.DialTimeout("tcp", serviceEndpoint, 2*time.Second) + conn, err := net.DialTimeout("tcp", serviceEndpoint, waitRetry) if err != nil { return ErrFailedToConnect.WithParams(serviceEndpoint).Wrap(err) } defer conn.Close() return nil // success } + +func (c *Client) isServiceReady(ctx context.Context, name string) (bool, error) { + service, err := c.GetService(ctx, name) + if err != nil { + return false, ErrGettingService.WithParams(name).Wrap(err) + } + switch service.Spec.Type { + case v1.ServiceTypeLoadBalancer: + return len(service.Status.LoadBalancer.Ingress) > 0, nil + case v1.ServiceTypeNodePort: + return service.Spec.Ports[0].NodePort != 0, nil + default: + return len(service.Spec.ExternalIPs) > 0, nil + } +} diff --git a/pkg/traefik/errors.go b/pkg/traefik/errors.go index 89a7302..51c7279 100644 --- a/pkg/traefik/errors.go +++ b/pkg/traefik/errors.go @@ -1,6 +1,7 @@ package traefik import ( + "errors" "fmt" ) @@ -20,7 +21,7 @@ func (e *Error) Error() string { } func (e *Error) Wrap(err error) error { - e.Err = err + e.Err = errors.Join(e.Err, err) return e } From 349a238ad2f7a2b27355b80984eeed70b0a9ef3f Mon Sep 17 00:00:00 2001 From: Mojtaba Date: Thu, 23 May 2024 12:16:03 +0200 Subject: [PATCH 6/9] feat: add host added to instance --- pkg/knuu/errors.go | 3 ++- pkg/knuu/instance.go | 16 ++++++++++++++++ pkg/knuu/instance_helper.go | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pkg/knuu/errors.go b/pkg/knuu/errors.go index 1c16d9b..a033233 100644 --- a/pkg/knuu/errors.go +++ b/pkg/knuu/errors.go @@ -227,6 +227,7 @@ var ( ErrParentInstanceIsNil = &Error{Code: "ParentInstanceIsNil", Message: "parent instance is nil for the sidecar '%s'"} ErrFailedToGetIP = &Error{Code: "FailedToGetIP", Message: "failed to get IP for service %s"} ErrNoParentInstance = &Error{Code: "NoParentInstance", Message: "no parent instance for the sidecar '%s'"} - ErrAddingToTraefikProxy = &Error{Code: "AddingToTraefikProxy", Message: "error adding '%s' to traefik proxy for service '%s'"} + ErrAddingToProxy = &Error{Code: "AddingToTraefikProxy", Message: "error adding '%s' to traefik proxy for service '%s'"} ErrCannotGetTraefikEndpoint = &Error{Code: "CannotGetTraefikEndpoint", Message: "cannot get traefik endpoint"} + ErrGettingProxyURL = &Error{Code: "GettingProxyURL", Message: "error getting proxy URL for service '%s'"} ) diff --git a/pkg/knuu/instance.go b/pkg/knuu/instance.go index 8cd32b1..6e207a6 100644 --- a/pkg/knuu/instance.go +++ b/pkg/knuu/instance.go @@ -2,6 +2,7 @@ package knuu import ( "context" + "fmt" "io" "os" "path/filepath" @@ -1395,3 +1396,18 @@ func (i *Instance) CreateCustomResource(gvr *schema.GroupVersionResource, obj *m func (i *Instance) CustomResourceDefinitionExists(gvr *schema.GroupVersionResource) (bool, error) { return k8sClient.CustomResourceDefinitionExists(context.TODO(), gvr), nil } + +func (i *Instance) AddHost(port int) (err error, host string) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + prefix := fmt.Sprintf("%s-%d", i.k8sName, port) + if err := traefikClient.AddHost(ctx, i.k8sName, prefix, port); err != nil { + return ErrAddingToProxy.WithParams(i.k8sName).Wrap(err), "" + } + host, err = traefikClient.URL(ctx, prefix) + if err != nil { + return ErrGettingProxyURL.WithParams(i.k8sName).Wrap(err), "" + } + return nil, host +} diff --git a/pkg/knuu/instance_helper.go b/pkg/knuu/instance_helper.go index 99376fd..9256d00 100644 --- a/pkg/knuu/instance_helper.go +++ b/pkg/knuu/instance_helper.go @@ -619,7 +619,7 @@ func (i *Instance) createBitTwisterInstance() (*Instance, error) { serviceName := i.k8sName // the main instance name err = traefikClient.AddHost(ctx, serviceName, bt.k8sName, i.BitTwister.Port()) if err != nil { - return nil, ErrAddingToTraefikProxy.WithParams(bt.k8sName, serviceName).Wrap(err) + return nil, ErrAddingToProxy.WithParams(bt.k8sName, serviceName).Wrap(err) } btURL, err := traefikClient.URL(ctx, bt.k8sName) From e19e2d965477cfe18a0490da2be063802ea79799 Mon Sep 17 00:00:00 2001 From: Mojtaba Date: Thu, 23 May 2024 17:23:45 +0200 Subject: [PATCH 7/9] Update pkg/traefik/traefik.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jose Ramon Mañes <32740567+jrmanes@users.noreply.github.com> --- pkg/traefik/traefik.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/traefik/traefik.go b/pkg/traefik/traefik.go index e9115c9..7b5e98c 100644 --- a/pkg/traefik/traefik.go +++ b/pkg/traefik/traefik.go @@ -32,10 +32,10 @@ const ( replicas = 1 waitRetry = 5 * time.Second - defaultCPURequest = "1000m" - defaultMemoryRequest = "512Mi" - maxCPULimit = "2000m" - maxMemoryLimit = "1024Mi" + defaultCPURequest = "500m" + defaultMemoryRequest = "500Mi" + maxCPULimit = "1000m" + maxMemoryLimit = "750Mi" ) type Traefik struct { From ade75af2c676efe80593315aa18470daced43c0c Mon Sep 17 00:00:00 2001 From: Mojtaba Date: Thu, 23 May 2024 17:49:08 +0200 Subject: [PATCH 8/9] fix: bittwister tests using the proxy --- Makefile | 20 ++++++++++---------- e2e/basic/bittwister_test.go | 16 ---------------- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 07c87f5..df8beb5 100644 --- a/Makefile +++ b/Makefile @@ -1,32 +1,32 @@ test-basic: - go test -v ./e2e/basic -timeout 120m + KNUU_TIMEOUT=120m go test -v ./e2e/basic -timeout 120m test-basic-file-cache: - go test -v ./e2e/basic -run=TestFileCache -count=1 -timeout 120m + KNUU_TIMEOUT=120m go test -v ./e2e/basic -run=TestFileCache -count=1 -timeout 120m test-basic-folder-cache: - go test -v ./e2e/basic -run=TestFolderCache -count=1 -timeout 120m + KNUU_TIMEOUT=120m go test -v ./e2e/basic -run=TestFolderCache -count=1 -timeout 120m test-bittwister-packetloss: - KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Packetloss -timeout 60m -count=1 + KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Packetloss -timeout 120m -count=1 test-bittwister-bandwidth: - KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Bandwidth -timeout 60m -count=1 + KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Bandwidth -timeout 120m -count=1 test-bittwister-latency: - KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Latency -timeout 60m -count=1 + KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Latency -timeout 120m -count=1 test-bittwister-jitter: - KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Jitter -timeout 60m -count=1 + KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Jitter -timeout 120m -count=1 test-celestia-app: - go test -v ./e2e/celestia_app + KNUU_TIMEOUT=120m go test -v ./e2e/celestia_app -timeout 120m test-celestia-node: - go test -v ./e2e/celestia_node + KNUU_TIMEOUT=120m go test -v ./e2e/celestia_node -timeout 120m test-all: - KNUU_TIMEOUT=300m go test -v ./e2e/... -timeout 120m + KNUU_TIMEOUT=120m go test -v ./e2e/... -timeout 120m .PHONY: test-all test-basic test-basic-file-cache test-basic-folder-cache test-bittwister-packetloss test-bittwister-bandwidth test-bittwister-latency test-bittwister-jitter test-celestia-app test-celestia-node diff --git a/e2e/basic/bittwister_test.go b/e2e/basic/bittwister_test.go index fe1439d..410e3ce 100644 --- a/e2e/basic/bittwister_test.go +++ b/e2e/basic/bittwister_test.go @@ -60,8 +60,6 @@ func TestBittwister_Bandwidth(t *testing.T) { require.NoError(t, iperfServer.EnableBitTwister(), "Error enabling BitTwister") require.NoError(t, iperfServer.Start(), "Error starting iperf-server instance") - forwardBitTwisterPort(t, iperfServer) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() require.NoError(t, iperfServer.BitTwister.WaitForStart(ctx), "Error waiting for BitTwister to start") @@ -191,8 +189,6 @@ func TestBittwister_Packetloss(t *testing.T) { require.NoError(t, target.EnableBitTwister(), "Error enabling BitTwister") require.NoError(t, target.Start(), "Error starting target instance") - forwardBitTwisterPort(t, target) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() require.NoError(t, target.BitTwister.WaitForStart(ctx), "Error waiting for BitTwister to start") @@ -321,8 +317,6 @@ func TestBittwister_Latency(t *testing.T) { require.NoError(t, target.EnableBitTwister(), "Error enabling BitTwister") require.NoError(t, target.Start(), "Error starting target instance") - forwardBitTwisterPort(t, target) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() require.NoError(t, target.BitTwister.WaitForStart(ctx), "Error waiting for BitTwister to start") @@ -468,8 +462,6 @@ func TestBittwister_Jitter(t *testing.T) { require.NoError(t, target.EnableBitTwister(), "Error enabling BitTwister") require.NoError(t, target.Start(), "Error starting target instance") - forwardBitTwisterPort(t, target) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() require.NoError(t, target.BitTwister.WaitForStart(ctx), "Error waiting for BitTwister to start") @@ -570,11 +562,3 @@ func formatBandwidth(bandwidth float64) string { bandwidth = math.Round(bandwidth*100) / 100 return fmt.Sprintf("%.2f %s", bandwidth, units[unitIndex]) } - -func forwardBitTwisterPort(t *testing.T, i *knuu.Instance) { - fwdBtPort, err := i.PortForwardTCP(i.BitTwister.Port()) - require.NoError(t, err, "Error port forwarding") - i.BitTwister.SetPort(fwdBtPort) - i.BitTwister.SetNewClientByURL("http://localhost") - t.Logf("BitTwister listening on http://localhost:%d", fwdBtPort) -} From 5be2e340da410eaa17a1e5691cb2381900428d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Ramon=20Ma=C3=B1es?= Date: Mon, 27 May 2024 09:21:59 +0200 Subject: [PATCH 9/9] refactor(makefile): fix comments + update readme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jose Ramon Mañes --- Makefile | 37 ++++++------------------------------- README.md | 25 +++++++------------------ 2 files changed, 13 insertions(+), 49 deletions(-) diff --git a/Makefile b/Makefile index df8beb5..724ba30 100644 --- a/Makefile +++ b/Makefile @@ -1,32 +1,7 @@ -test-basic: - KNUU_TIMEOUT=120m go test -v ./e2e/basic -timeout 120m - -test-basic-file-cache: - KNUU_TIMEOUT=120m go test -v ./e2e/basic -run=TestFileCache -count=1 -timeout 120m - -test-basic-folder-cache: - KNUU_TIMEOUT=120m go test -v ./e2e/basic -run=TestFolderCache -count=1 -timeout 120m - -test-bittwister-packetloss: - KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Packetloss -timeout 120m -count=1 - -test-bittwister-bandwidth: - KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Bandwidth -timeout 120m -count=1 - -test-bittwister-latency: - KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Latency -timeout 120m -count=1 - -test-bittwister-jitter: - KNUU_TIMEOUT=120m go test -v ./e2e/basic --run=TestBittwister_Jitter -timeout 120m -count=1 - -test-celestia-app: - KNUU_TIMEOUT=120m go test -v ./e2e/celestia_app -timeout 120m - -test-celestia-node: - KNUU_TIMEOUT=120m go test -v ./e2e/celestia_node -timeout 120m - -test-all: - KNUU_TIMEOUT=120m go test -v ./e2e/... -timeout 120m - -.PHONY: test-all test-basic test-basic-file-cache test-basic-folder-cache test-bittwister-packetloss test-bittwister-bandwidth test-bittwister-latency test-bittwister-jitter test-celestia-app test-celestia-node +pkgs := $(shell go list ./...) +run := . +count := 1 +test: + KNUU_TIMEOUT=120m go test -v $(pkgs) -run $(run) -count=$(count) -timeout 120m +.PHONY: test diff --git a/README.md b/README.md index 12bad2e..db76891 100644 --- a/README.md +++ b/README.md @@ -208,35 +208,24 @@ You can find the relevant documentation in the `pkg/knuu` package at: https://pk ## Run -```shell -make test-all -``` +You can use the Makefile commands to easily target whatever test by setting the pkg, run, or count flags. -Or run only the basic examples: +Targeting a directory ```shell -make test-basic -``` - -Or run BitTwister tests: - -```sh -make test-bittwister-packetloss -make test-bittwister-bandwidth -make test-bittwister-latency -make test-bittwister-jitter +make test pkgs=./e2e/basic ``` -Or the celestia-app examples: +Targeting a Test in a directory ```shell -make test-celestia-app +make test pkgs=./e2e/basic run=TestJustThisTest ``` -Or the celestia-node examples: +Run a test in a loop to debug ```shell -make test-celestia-node +make test pkgs=./e2e/basic run=TestJustThisTest10Times count=10 ``` ---