Skip to content

Commit

Permalink
(refactor) address the e2e extract / refactor of issue knative#763
Browse files Browse the repository at this point in the history
  • Loading branch information
maximilien committed Mar 27, 2020
1 parent db569fa commit df3334c
Show file tree
Hide file tree
Showing 22 changed files with 752 additions and 653 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@
| https://github.com/knative/client/pull/[#]
////

## v0.14.0 (2020-XX-XX)

[cols="1,10,3", options="header", width="100%"]
|===
| | Description | PR

| 🐣
| Refactor `e2e` common code into `lib\test\integration`
| https://github.com/knative/client/pull/764[#764]

## v0.13.0 (2020-03-11)

[cols="1,10,3", options="header", width="100%"]
Expand Down
2 changes: 1 addition & 1 deletion hack/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

set -o pipefail

source_dirs="cmd pkg test"
source_dirs="cmd pkg test lib"

# Store for later
if [ -z "$1" ]; then
Expand Down
141 changes: 27 additions & 114 deletions test/e2e/cli.go → lib/test/integration/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,153 +12,64 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package e2e
package integration

import (
"bytes"
"fmt"
"io"
"os/exec"
"strings"
"testing"

"github.com/pkg/errors"
)

type kn struct {
namespace string
}

const (
seperatorHeavy = "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
seperatorLight = "╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍"
)

// Run the 'kn' CLI with args and opts
func (k kn) Run(args ...string) KnRunResult {
return RunKn(k.namespace, args)
}

// Helper methods for calling out to the test cluster
type kubectl struct {
type Kn struct {
namespace string
}

// Run the 'kubectl' CLI with args and opts
func (k kubectl) Run(args ...string) (string, error) {
return RunKubectl(k.namespace, args...)
// New Kn object
func NewKn() Kn {
return Kn{}
}

// Collector for results
type KnRunResultCollector struct {
results []KnRunResult
extraDumps []string
t *testing.T
}

func NewKnRunResultCollector(t *testing.T) *KnRunResultCollector {
return &KnRunResultCollector{
results: []KnRunResult{},
t: t,
extraDumps: []string{},
}
}

func (c *KnRunResultCollector) AssertNoError(result KnRunResult) {
c.results = append(c.results, result)
if result.Error != nil {
c.t.Logf("ERROR: %v", result.Stderr)
c.t.FailNow()
}
}

func (c *KnRunResultCollector) AssertError(result KnRunResult) {
c.results = append(c.results, result)
if result.Error == nil {
c.t.Log("ERROR: Error expected but no error happened")
c.t.FailNow()
}
// Run the 'kn' CLI with args and opts
func (k Kn) Run(args ...string) KnRunResult {
return RunKn(k.namespace, args)
}

// AddDump adds extra dump information to the collector which is printed
// out if an error occurs
func (c *KnRunResultCollector) AddDump(kind string, name string, namespace string) {
dumpInfo := extractDumpInfoWithName(kind, name, namespace)
if dumpInfo != "" {
c.extraDumps = append(c.extraDumps, dumpInfo)
}
// Namespace that this Kn instance uses
func (k Kn) Namespace() string {
return k.namespace
}

func (c *KnRunResultCollector) DumpIfFailed() {
if c.t.Failed() {
c.t.Log(c.errorDetails())
}
// Helper methods for calling out to the test cluster
type Kubectl struct {
namespace string
}

func (c *KnRunResultCollector) errorDetails() string {
var out = bytes.Buffer{}
fmt.Fprintln(&out, "=== FAIL: =======================[[ERROR]]========================")
c.printCommands(&out)
var dumpInfos []string
if len(c.results) > 0 {
dumpInfo := c.results[len(c.results)-1].DumpInfo
if dumpInfo != "" {
dumpInfos = append(dumpInfos, dumpInfo)
}
}
dumpInfos = append(dumpInfos, c.extraDumps...)
for _, d := range dumpInfos {
fmt.Fprintln(&out, "--------------------------[[DUMP]]-------------------------------")
fmt.Fprintf(&out, d)
// New Kn object
func NewKubectl(namespace string) Kubectl {
return Kubectl{
namespace: namespace,
}

fmt.Fprintln(&out, "=================================================================")
return out.String()
}

func (c *KnRunResultCollector) printCommands(out io.Writer) {
for i, result := range c.results {
c.printCommand(out, result)
if i < len(c.results)-1 {
fmt.Fprintf(out, "┣━%s\n", seperatorHeavy)
}
}
// Run the 'kubectl' CLI with args and opts
func (k Kubectl) Run(args ...string) (string, error) {
return RunKubectl(k.namespace, args...)
}

func (c *KnRunResultCollector) printCommand(out io.Writer, result KnRunResult) {
fmt.Fprintf(out, "🦆 %s\n", result.CmdLine)
for _, l := range strings.Split(result.Stdout, "\n") {
fmt.Fprintf(out, "┃ %s\n", l)
}
if result.Stderr != "" {
errorPrefix := "🔥"
if result.ErrorExpected {
errorPrefix = "︙"
}
for _, l := range strings.Split(result.Stderr, "\n") {
fmt.Fprintf(out, "%s %s\n", errorPrefix, l)
}
}
// Namespace that this Kn instance uses
func (k Kubectl) Namespace() string {
return k.namespace
}

// ========================================================
// Functions:

// Result of a "kn" call
type KnRunResult struct {
// Command line called
CmdLine string
// Standard output of command
Stdout string
// Standard error of command
Stderr string
// And extra dump informations in case of an unexpected error
DumpInfo string
// Error occurred during execution
Error error
// Was an error expected ?
ErrorExpected bool
}
// Public functions

// RunKn runs "kn" in a given namespace
func RunKn(namespace string, args []string) KnRunResult {
Expand Down Expand Up @@ -195,6 +106,8 @@ func RunKubectl(namespace string, args ...string) (string, error) {
return stdout, nil
}

// Private

func runCli(cli string, args []string) (string, string, error) {
var stderr bytes.Buffer
var stdout bytes.Buffer
Expand Down
7 changes: 4 additions & 3 deletions test/e2e/e2e_flags.go → lib/test/integration/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e
package integration

import (
"flag"
Expand All @@ -23,14 +23,15 @@ import (

// Flags holds the command line flags or defaults for settings in the user's environment.
// See ClientFlags for the list of supported fields.
var Flags = initializeFlags()
var Flags = InitializeFlags()

// ClientFlags define the flags that are needed to run the e2e tests.
type ClientFlags struct {
DockerConfigJSON string
}

func initializeFlags() *ClientFlags {
// InitializeFlags initializes the client's flags
func InitializeFlags() *ClientFlags {
var f ClientFlags

dockerConfigJSON := os.Getenv("DOCKER_CONFIG_JSON")
Expand Down
65 changes: 38 additions & 27 deletions test/e2e/common.go → lib/test/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package e2e
package integration

import (
"fmt"
Expand All @@ -38,60 +38,70 @@ var serviceMutex sync.Mutex
var serviceCount int
var namespaceCount int

type e2eTest struct {
// IntegrationTest struct
type Test struct {
namespace string
kn kn
kn Kn
}

func NewE2eTest() (*e2eTest, error) {
ns := nextNamespace()
// Teardown clean up
func (test *Test) Teardown() error {
return DeleteNamespace(test.namespace)
}

// Teardown clean up
func (test *Test) Kn() Kn {
return test.kn
}

err := createNamespace(ns)
// NewIntegrationTest creates a new ItegrationTest object
func NewIntegrationTest() (*Test, error) {
ns := NextNamespace()

err := CreateNamespace(ns)
if err != nil {
return nil, err
}
err = waitForNamespaceCreated(ns)
err = WaitForNamespaceCreated(ns)
if err != nil {
return nil, err
}

return &e2eTest{
return &Test{
namespace: ns,
kn: kn{ns},
kn: Kn{ns},
}, nil
}

func nextNamespace() string {
// NextNamespace return the next unique namespace
func NextNamespace() string {
ns := os.Getenv("KN_E2E_NAMESPACE")
if ns == "" {
ns = "kne2etests"
}
return fmt.Sprintf("%s%d", ns, getNextNamespaceId())
return fmt.Sprintf("%s%d", ns, GetNextNamespaceId())
}

func getNextNamespaceId() int {
// GetNextNamespaceId return the next unique ID for the next namespace
func GetNextNamespaceId() int {
nsMutex.Lock()
defer nsMutex.Unlock()
current := namespaceCount
namespaceCount++
return current
}

func getNextServiceName(base string) string {
// GetNextServiceName return the name for the next namespace
func GetNextServiceName(base string) string {
serviceMutex.Lock()
defer serviceMutex.Unlock()
current := serviceCount
serviceCount++
return base + strconv.Itoa(current)
}

// Teardown clean up
func (test *e2eTest) Teardown() error {
return deleteNamespace(test.namespace)
}

// createNamespace creates and tests a namesspace creation invoking kubectl
func createNamespace(namespace string) error {
// CreateNamespace creates and tests a namesspace creation invoking kubectl
func CreateNamespace(namespace string) error {
expectedOutputRegexp := fmt.Sprintf("namespace?.+%s.+created", namespace)
out, err := createNamespaceWithRetry(namespace, MaxRetries)
if err != nil {
Expand All @@ -109,9 +119,9 @@ func createNamespace(namespace string) error {
return nil
}

// createNamespace deletes and tests a namesspace deletion invoking kubectl
func deleteNamespace(namespace string) error {
kubectl := kubectl{namespace}
// DeleteNamespace deletes and tests a namesspace deletion invoking kubectl
func DeleteNamespace(namespace string) error {
kubectl := Kubectl{namespace}
out, err := kubectl.Run("delete", "namespace", namespace)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Cannot delete namespace %s", namespace))
Expand All @@ -129,7 +139,7 @@ func deleteNamespace(namespace string) error {
}

// WaitForNamespaceDeleted wait until namespace is deleted
func waitForNamespaceDeleted(namespace string) error {
func WaitForNamespaceDeleted(namespace string) error {
deleted := checkNamespace(namespace, false, MaxRetries)
if !deleted {
return fmt.Errorf("error deleting namespace %s, timed out after %d retries", namespace, MaxRetries)
Expand All @@ -138,7 +148,7 @@ func waitForNamespaceDeleted(namespace string) error {
}

// WaitForNamespaceCreated wait until namespace is created
func waitForNamespaceCreated(namespace string) error {
func WaitForNamespaceCreated(namespace string) error {
created := checkNamespace(namespace, true, MaxRetries)
if !created {
return fmt.Errorf("error creating namespace %s, timed out after %d retries", namespace, MaxRetries)
Expand All @@ -147,10 +157,11 @@ func waitForNamespaceCreated(namespace string) error {
}

// Private functions

func checkNamespace(namespace string, created bool, maxRetries int) bool {
retries := 0
for retries < MaxRetries {
output, _ := kubectl{}.Run("get", "namespace")
output, _ := Kubectl{}.Run("get", "namespace")

// check for namespace deleted
if !created && !strings.Contains(output, namespace) {
Expand All @@ -177,7 +188,7 @@ func createNamespaceWithRetry(namespace string, maxRetries int) (string, error)
)

for retries < maxRetries {
out, err = kubectl{}.Run("create", "namespace", namespace)
out, err = Kubectl{}.Run("create", "namespace", namespace)
if err == nil {
return out, nil
}
Expand Down
Loading

0 comments on commit df3334c

Please sign in to comment.