Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DNM] nasty hack for tls testrunner #45

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions installutils/helminstall/internal/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package internal_test

import (
"bytes"
"io/ioutil"
"io"

"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo/v2"
Expand Down Expand Up @@ -109,7 +109,7 @@ var _ = Describe("helm install client", func() {
It("should download Helm chart", func() {
chartUri := "chartUri.tgz"
chartFileContents := "test chart file"
chartFile := ioutil.NopCloser(bytes.NewBufferString(chartFileContents))
chartFile := io.NopCloser(bytes.NewBufferString(chartFileContents))
expectedChart := &chart.Chart{}
mockResourceFetcher.
EXPECT().
Expand Down
4 changes: 2 additions & 2 deletions manifesttestutils/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package manifesttestutils

import (
"fmt"
"io/ioutil"
"os"

extv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"

Expand Down Expand Up @@ -306,7 +306,7 @@ func (t *testManifest) mustFindObject(kind, namespace, name string) runtime.Obje
}

func mustReadManifest(relativePathToManifest string) string {
bytes, err := ioutil.ReadFile(relativePathToManifest)
bytes, err := os.ReadFile(relativePathToManifest)
Expect(err).NotTo(HaveOccurred())
return string(bytes)
}
Expand Down
22 changes: 20 additions & 2 deletions testutils/helper/curl.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"strings"
"time"

Expand Down Expand Up @@ -65,6 +64,10 @@ func (t *testContainer) CurlEventuallyShouldOutput(opts CurlOpts, substr string,
tick := time.Tick(currentTimeout / 8)

gomega.EventuallyWithOffset(ginkgoOffset+1, func() string {
if !t.CanCurl() {
return ""
}

var res string

bufChan, done, err := t.CurlAsyncChan(opts)
Expand All @@ -84,7 +87,7 @@ func (t *testContainer) CurlEventuallyShouldOutput(opts CurlOpts, substr string,
buf = r
}
}
byt, err := ioutil.ReadAll(buf)
byt, err := io.ReadAll(buf)
if err != nil {
res = err.Error()
} else {
Expand All @@ -103,6 +106,9 @@ func (t *testContainer) CurlEventuallyShouldRespond(opts CurlOpts, substr string
tick := time.Tick(currentTimeout / 8)

gomega.EventuallyWithOffset(ginkgoOffset+1, func() string {
if !t.CanCurl() {
return ""
}
res, err := t.Curl(opts)
if err != nil {
res = err.Error()
Expand Down Expand Up @@ -184,16 +190,28 @@ func (t *testContainer) buildCurlArgs(opts CurlOpts) []string {
}

func (t *testContainer) Curl(opts CurlOpts) (string, error) {
if !t.CanCurl() {
return "", fmt.Errorf("testContainer from image %s:%s cannot curl", t.containerImageName, t.imageTag)
}

args := t.buildCurlArgs(opts)
return t.Exec(args...)
}

func (t *testContainer) CurlAsync(opts CurlOpts) (io.Reader, chan struct{}, error) {
if !t.CanCurl() {
return nil, nil, fmt.Errorf("testContainer from image %s:%s cannot curl", t.containerImageName, t.imageTag)
}

args := t.buildCurlArgs(opts)
return t.TestRunnerAsync(args...)
}

func (t *testContainer) CurlAsyncChan(opts CurlOpts) (<-chan io.Reader, chan struct{}, error) {
if !t.CanCurl() {
return nil, nil, fmt.Errorf("testContainer from image %s:%s cannot curl", t.containerImageName, t.imageTag)
}

args := t.buildCurlArgs(opts)
return t.TestRunnerChan(&bytes.Buffer{}, args...)
}
32 changes: 4 additions & 28 deletions testutils/helper/http_echo.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
package helper

import (
"time"
)

type echoPod struct {
*testContainer
}

func (t *echoPod) Deploy(timeout time.Duration) error {
return t.deploy(timeout)
}

const (
defaultHttpEchoImage = "kennship/http-echo@sha256:144322e8e96be2be6675dcf6e3ee15697c5d052d14d240e8914871a2a83990af"
HttpEchoName = "http-echo"
HttpEchoPort = 3000
)

func NewEchoHttp(namespace string) (*echoPod, error) {
container, err := newTestContainer(namespace, defaultHttpEchoImage, HttpEchoName, HttpEchoPort)
if err != nil {
return nil, err
}
return &echoPod{
testContainer: container,
}, nil
func NewEchoHttp(namespace string) (TestContainer, error) {
return newTestContainer(namespace, defaultHttpEchoImage, HttpEchoName, HttpEchoPort)
}

const (
Expand All @@ -34,12 +16,6 @@ const (
TcpEchoPort = 1025
)

func NewEchoTcp(namespace string) (*echoPod, error) {
container, err := newTestContainer(namespace, defaultTcpEchoImage, TcpEchoName, TcpEchoPort)
if err != nil {
return nil, err
}
return &echoPod{
testContainer: container,
}, nil
func NewEchoTcp(namespace string) (TestContainer, error) {
return newTestContainer(namespace, defaultTcpEchoImage, TcpEchoName, TcpEchoPort)
}
72 changes: 68 additions & 4 deletions testutils/helper/test_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package helper

import (
"context"
"fmt"
"io"
"strings"
"time"

"github.com/pkg/errors"
Expand All @@ -16,16 +18,33 @@ import (
"k8s.io/client-go/kubernetes"
)

type TestRunner interface {
var _ TestRunner = &testRunner{}
var _ TestContainer = &testRunner{}
var _ TestContainer = &testContainer{}

// A TestContainer is a general-purpose abstraction over a container in which we might
// execute cURL or other, arbitrary commands via kubectl.
type TestContainer interface {
Deploy(timeout time.Duration) error
Terminate() error
Exec(command ...string) (string, error)
TestRunnerAsync(args ...string) (io.Reader, chan struct{}, error)
CanCurl() bool
// Checks the response of the request
CurlEventuallyShouldRespond(opts CurlOpts, substr string, ginkgoOffset int, timeout ...time.Duration)
// CHecks all of the output of the curl command
// Checks all of the output of the curl command
CurlEventuallyShouldOutput(opts CurlOpts, substr string, ginkgoOffset int, timeout ...time.Duration)
Curl(opts CurlOpts) (string, error)
Exec(command ...string) (string, error)
ExecAsync(args ...string) (io.Reader, chan struct{}, error)
}

// A TestRunner is an extension of a TestContainer which is typically run with the defaultTestRunnerImage
// and which has a service associated with it, and can run https.
type TestRunner interface {
TestContainer
DeployTLS(timeout time.Duration, crt, key []byte) error
DeleteService() error
TerminateAndDeleteService() error
TestRunnerAsync(args ...string) (io.Reader, chan struct{}, error)
}

func newTestContainer(namespace, imageTag, echoName string, port int32) (*testContainer, error) {
Expand Down Expand Up @@ -60,6 +79,10 @@ type testContainer struct {
port int32
}

func (t *testContainer) Deploy(timeout time.Duration) error {
return t.deploy(timeout)
}

// Deploys the http echo to the kubernetes cluster the kubeconfig is pointing to and waits for the given time for the
// http-echo pod to be running.
func (t *testContainer) deploy(timeout time.Duration) error {
Expand Down Expand Up @@ -122,7 +145,23 @@ func (t *testContainer) Terminate() error {
return errors.Wrapf(err, "deleting %s pod", t.echoName)
}
return nil
}

func (t *testContainer) DeleteService() error {
if err := testutils.Kubectl("delete", "service", "-n", t.namespace, t.echoName, "--grace-period=0"); err != nil {
return errors.Wrapf(err, "deleting %s service", t.echoName)
}
return nil
}

func (t *testContainer) TerminateAndDeleteService() error {
if err := t.Terminate(); err != nil {
return err
}
if err := t.DeleteService(); err != nil {
return err
}
return nil
}

// testContainer executes a command inside the testContainer container
Expand All @@ -131,14 +170,39 @@ func (t *testContainer) Exec(command ...string) (string, error) {
return testutils.KubectlOut(args...)
}

// Cp copies files into the testContainer container
func (t *testContainer) Cp(files map[string]string) error {
for k, v := range files {
if err := testutils.Kubectl("cp", k, fmt.Sprintf("%s/%s:%s", t.namespace, t.echoName, v)); err != nil {
return err
}
}
return nil
}

// TestRunnerAsync is deprecated; please use ExecAsync.
// TestContainerAsync executes a command inside the testContainer container
// returning a buffer that can be read from as it executes
func (t *testContainer) TestRunnerAsync(args ...string) (io.Reader, chan struct{}, error) {
args = append([]string{"exec", "-i", t.echoName, "-n", t.namespace, "--"}, args...)
return testutils.KubectlOutAsync(args...)
}

// ExecAsync executes a command inside the testContainer container
// returning a buffer that can be read from as it executes
func (t *testContainer) ExecAsync(args ...string) (io.Reader, chan struct{}, error) {
args = append([]string{"exec", "-i", t.echoName, "-n", t.namespace, "--"}, args...)
return testutils.KubectlOutAsync(args...)
}

func (t *testContainer) TestRunnerChan(r io.Reader, args ...string) (<-chan io.Reader, chan struct{}, error) {
args = append([]string{"exec", "-i", t.echoName, "-n", t.namespace, "--"}, args...)
return testutils.KubectlOutChan(r, args...)
}

func (t *testContainer) CanCurl() bool {
if out, err := t.Exec("curl", "--version"); err != nil || !strings.HasPrefix(out, "curl") {
return false
}
return true
}
12 changes: 6 additions & 6 deletions testutils/helper/test_container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ var _ = Describe("test container tests", func() {
})
Context("test runner", func() {
var (
testRunner *testRunner
testRunner TestRunner
)
BeforeEach(func() {
var err error
Expand Down Expand Up @@ -71,15 +71,15 @@ var _ = Describe("test container tests", func() {
})
})

Context("http ehco", func() {
Context("http echo", func() {
var (
httpEcho *echoPod
httpEcho TestContainer
)
BeforeEach(func() {
var err error
httpEcho, err = NewEchoHttp(namespace)
Expect(err).NotTo(HaveOccurred())
err = httpEcho.deploy(time.Minute)
err = httpEcho.(*testContainer).deploy(time.Minute)
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
Expand All @@ -105,13 +105,13 @@ var _ = Describe("test container tests", func() {

Context("tcp ehco", func() {
var (
tcpEcho *echoPod
tcpEcho TestContainer
)
BeforeEach(func() {
var err error
tcpEcho, err = NewEchoTcp(namespace)
Expect(err).NotTo(HaveOccurred())
err = tcpEcho.deploy(time.Minute)
err = tcpEcho.(*testContainer).deploy(time.Minute)
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
Expand Down
58 changes: 57 additions & 1 deletion testutils/helper/testrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package helper

import (
"fmt"
"os"
"time"

"github.com/pkg/errors"
"github.com/solo-io/go-utils/log"
)

Expand Down Expand Up @@ -45,7 +47,7 @@ const (
</html>`
)

func NewTestRunner(namespace string) (*testRunner, error) {
func NewTestRunner(namespace string) (TestRunner, error) {
testContainer, err := newTestContainer(namespace, defaultTestRunnerImage, TestrunnerName, TestRunnerPort)
if err != nil {
return nil, err
Expand Down Expand Up @@ -79,3 +81,57 @@ func (t *testRunner) Deploy(timeout time.Duration) error {
}()
return nil
}

// DeployTLS deletes the running server pod and its service, then redeploys using a server
// which serves HTTPS with the cert and key provided.
func (t *testRunner) DeployTLS(timeout time.Duration, crt, key []byte) error {
if err := t.TerminateAndDeleteService(); err != nil {
return errors.Wrap(err, "terminating pod and deleting service")
}
if err := t.testContainer.Deploy(timeout); err != nil {
return errors.Wrap(err, "deploying pod")
}

certFname := "/tmp/testrunner_tls/cert.pem"
keyFname := "/tmp/testrunner_tls/key.pem"
scriptFname := "/tmp/testrunner_tls/server.py"
os.MkdirAll("/tmp/testrunner_tls", os.ModePerm)
defer os.RemoveAll("/tmp/testrunner_tls")

if err := os.WriteFile(certFname, crt, os.ModePerm); err != nil {
return errors.Wrap(err, "writing cert")
}
if err := os.WriteFile(keyFname, key, os.ModePerm); err != nil {
return errors.Wrap(err, "writing key")
}
if err := os.WriteFile(scriptFname, []byte(fmt.Sprintf(`
import BaseHTTPServer, SimpleHTTPServer
import ssl

httpd = BaseHTTPServer.HTTPServer(('0.0.0.0', %d), SimpleHTTPServer.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket (httpd.socket, keyfile='/tmp/key.pem', certfile='/tmp/cert.pem', server_side=True)
httpd.serve_forever()
`, TestRunnerPort)), os.ModePerm); err != nil {
return errors.Wrap(err, "writing server")
}
if err := t.Cp(map[string]string{
certFname: "/tmp/cert.pem",
keyFname: "/tmp/key.pem",
scriptFname: "/tmp/server.py",
}); err != nil {
return errors.Wrap(err, "kubectl cp")
}

go func() {
start := time.Now()
log.Debugf("starting https server listening on port %v", TestRunnerPort)
// This command starts an https SimpleHttpServer and blocks until the server terminates
if _, err := t.Exec("python", "/tmp/server.py"); err != nil {
// if an error happened after 5 seconds, it's probably not an error.. just the pod terminating.
if time.Now().Sub(start).Seconds() < 5.0 {
log.Warnf("failed to start HTTPS Server in Test Runner: %v", err)
}
}
}()
return nil
}
Loading
Loading