Skip to content

Commit

Permalink
feat(e2e): Improvements to E2E functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveRuble committed Apr 23, 2019
1 parent b870f69 commit 29da428
Show file tree
Hide file tree
Showing 16 changed files with 330 additions and 237 deletions.
4 changes: 2 additions & 2 deletions cmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,14 +399,13 @@ var appStatusCmd = &cobra.Command{
for i := range appReleases {
appRelease := appReleases[i]
go func() {
defer wg.Done()
ctx := b.NewContext().WithAppRelease(appRelease)
err := appRelease.LoadActualState(ctx, false)
if err != nil {
ctx.Log.WithError(err).Fatal()
}
p.Add(1)

wg.Done()
}()
}
wg.Wait()
Expand Down Expand Up @@ -646,6 +645,7 @@ var appDeployCmd = addCommand(appCmd, &cobra.Command{
cmd.Flags().StringP(AppDeployTag, "t", "latest", "Set the tag used in the chart.")
cmd.Flags().Bool(ArgAppDeployDeps, false, "Also deploy all dependencies of the requested apps.")
cmd.Flags().StringSlice(ArgAppDeploySet, []string{}, "Additional values to pass to helm for this deploy.")
cmd.Flags().StringSlice(ArgSharedTenants, []string{}, "Tenants to deploy this app to, if it's per-tenant.")
})

const (
Expand Down
52 changes: 42 additions & 10 deletions cmd/e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ package cmd

import (
"fmt"
"github.com/cheynewallace/tabby"
"github.com/fatih/color"
"github.com/naveego/bosun/pkg/bosun"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
)

var e2eCmd = addCommand(rootCmd, &cobra.Command{
Expand All @@ -43,6 +45,12 @@ var e2eRunCmd = addCommand(e2eCmd, &cobra.Command{
Short: "Runs an E2E test suite.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
err := viper.BindPFlags(cmd.Flags())

if err != nil {
return err
}

b := mustGetBosun()

suite, err := b.GetTestSuite(args[0])
Expand All @@ -51,7 +59,12 @@ var e2eRunCmd = addCommand(e2eCmd, &cobra.Command{
return err
}

ctx := b.NewContext()
e2eCtx := bosun.E2EContext{
SkipSetup: viper.GetBool(ArgE2ERunSkipSetup),
SkipTeardown: viper.GetBool(ArgE2ERunSkipTeardown),
Tests: viper.GetStringSlice(ArgE2ERunTests),
}
ctx := bosun.WithE2EContext(b.NewContext(), e2eCtx)
results, err := suite.Run(ctx)

if err != nil {
Expand All @@ -70,23 +83,42 @@ var e2eRunCmd = addCommand(e2eCmd, &cobra.Command{
fmt.Printf("Ended at: %s\n", result.EndedAt)
fmt.Printf("Elapsed time: %s\n", result.Elapsed)
colorHeader.Printf("Steps:\n")
t := tabby.New()
t.AddHeader("Name", "Elapsed", "Result")
t := tablewriter.NewWriter(os.Stdout)
t.SetHeader([]string{"Name", "Elapsed", "Pass", "Fail"})
t.SetColumnColor(
tablewriter.Color(tablewriter.FgHiWhiteColor),
tablewriter.Color(tablewriter.FgHiWhiteColor),
tablewriter.Color(tablewriter.FgGreenColor),
tablewriter.Color(tablewriter.FgRedColor))
t.SetAutoWrapText(true)
t.SetReflowDuringAutoWrap(true)
t.SetColWidth(100)
for _, step := range result.Steps {
name := step.Name
elapsed := step.Elapsed
var passed string
pass := ""
fail := ""
if step.Passed {
passed = color.GreenString("PASS")
pass = "PASS"
} else {
passed = color.RedString("FAIL: %s", step.Error)
fail = step.Error
}
t.AddLine(name, elapsed, passed)
t.Append([]string{name, elapsed, pass, fail})
}

t.Print()
t.Render()
fmt.Println()
}
return nil
},
}, func(cmd *cobra.Command) {
cmd.Flags().StringSlice(ArgE2ERunTests, []string{}, "Specific tests to run.")
cmd.Flags().Bool(ArgE2ERunSkipSetup, false, "Skip setup scripts.")
cmd.Flags().Bool(ArgE2ERunSkipTeardown, false, "Skip teardown scripts.")
})

const (
ArgE2ERunTests = "tests"
ArgE2ERunSkipSetup = "skip-setup"
ArgE2ERunSkipTeardown = "skip-teardown"
)
2 changes: 2 additions & 0 deletions cmd/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,8 @@ func printOutput(out interface{}, columns ...string) error {
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(header)
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetCenterSeparator("|")
for _, m := range mapSlice {
var values []string

Expand Down
74 changes: 60 additions & 14 deletions pkg/bosun/app_actions.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package bosun

import (
"bufio"
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"github.com/hashicorp/go-getter/helper/url"
"github.com/naveego/bosun/pkg"
"github.com/naveego/bosun/pkg/mongo"
"github.com/pkg/errors"
Expand Down Expand Up @@ -330,6 +332,9 @@ func (t *TestAction) Execute(ctx BosunContext) error {

func renderTemplate(ctx BosunContext, tmpl string) (string, error) {

if !strings.Contains(tmpl, "{{") {
return tmpl, nil
}
t, err := template.New("").Parse(tmpl)
if err != nil {
return "", err
Expand Down Expand Up @@ -386,6 +391,10 @@ func (a *MongoAction) Execute(ctx BosunContext) error {
}
}

if db.Name != "" {
cmd.Conn.DBName = db.Name
}

j, _ := json.MarshalIndent(cmd, "", " ")
ctx.Log.Debugf("Executing mongo import command: \n%s", string(j))

Expand Down Expand Up @@ -445,27 +454,52 @@ type HTTPAction struct {
Method string `yaml:"method" json:"method"`
Headers map[string]string `yaml:"headers" json:"headers"`
Body map[string]interface{} `yaml:"body,omitempty" json:"body"`
Raw string `yaml:"raw,omitempty" json:"raw,omitempty"`
OKCodes []int `yaml:"okCodes,omitempty,flow" json:"okCodes,omitempty"` // codes which should be treated as OK (passing). Defaults to [200, 201, 202, 204].
}

func (a *HTTPAction) Execute(ctx BosunContext) error {

var bodyBytes []byte
var req *http.Request
var err error
if a.Body != nil {
bodyBytes, err = json.Marshal(a.Body)
if err != nil {
return errors.Wrap(err, "marshal body")
if a.Raw == "" {
var bodyBytes []byte
var err error
if a.Body != nil {
bodyBytes, err = json.Marshal(a.Body)
if err != nil {
return errors.Wrap(err, "marshal body")
}
}
}

bodyBuffer := bytes.NewBuffer(bodyBytes)
a.Method = strings.ToUpper(a.Method)
bodyBuffer := bytes.NewBuffer(bodyBytes)
a.Method = strings.ToUpper(a.Method)

ctx.Log.Debugf("Making %s request to %s...", a.Method, a.URL)
ctx.Log.Debugf("Making %s request to %s...", a.Method, a.URL)

req, err := http.NewRequest(a.Method, a.URL, bodyBuffer)
if err != nil {
return errors.Wrap(err, "create req")
req, err = http.NewRequest(a.Method, a.URL, bodyBuffer)
if err != nil {
return errors.Wrap(err, "create req")
}
for k, v := range a.Headers {
req.Header.Add(k, v)
}
} else {
if !strings.Contains(a.Raw, "\n\n") {
a.Raw = a.Raw + "\n\n"
}

r := bufio.NewReader(strings.NewReader(a.Raw))

req, err = http.ReadRequest(r)
if err != nil {
return errors.Wrapf(err, "create req from raw input:\n%s", a.Raw)
}
req.URL, err = url.Parse(req.RequestURI)
if err != nil {
return errors.Wrapf(err, "parse url %q", req.RequestURI)
}
req.RequestURI = ""
}

resp, err := http.DefaultClient.Do(req)
Expand All @@ -475,10 +509,22 @@ func (a *HTTPAction) Execute(ctx BosunContext) error {

ctx.Log.Debugf("Request returned %d - %s.", resp.StatusCode, resp.Status)

if len(a.OKCodes) == 0 {
a.OKCodes = []int{http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent}
}

defer resp.Body.Close()
if resp.StatusCode >= 400 {
isOK := false
for _, okCode := range a.OKCodes {
if resp.StatusCode == okCode {
isOK = true
break
}
}

if !isOK {
respBody, _ := ioutil.ReadAll(resp.Body)
err = errors.Errorf("Response had non-success status code %d: %s", resp.StatusCode, string(respBody))
err = errors.Errorf("Response had non-success status code %d (OKCodes: %v): %s", resp.StatusCode, a.OKCodes, string(respBody))
return err
}

Expand Down
18 changes: 18 additions & 0 deletions pkg/bosun/app_actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,23 @@ var _ = Describe("AppActions", func() {

Expect(sut.Execute(ctx)).To(Succeed())
})

It("should execute raw request", func() {

sut := HTTPAction{
URL: "",
Raw: `GET https://google.com HTTP/1.1
Host: google.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
`,
}

ctx := NewTestBosunContext()

Expect(sut.Execute(ctx)).To(Succeed())
})
})
})
31 changes: 18 additions & 13 deletions pkg/bosun/app_values.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ func (v Values) MarshalJSON() ([]byte, error) {
// return json.Marshal(x)
}


func (v *Values) UnmarshalYAML(unmarshal func(interface{}) error) error {
m := map[string]interface{}(*v)
err := unmarshal(&m)
Expand Down Expand Up @@ -63,12 +62,11 @@ func (v Values) ToEnv(prefix string) map[string]string {
return out
}


func (v Values) toEnv(prefix string, acc map[string]string) {
for k, v := range v {
key := prefix + strings.ToUpper(k)
if values, ok := v.(Values); ok {
values.toEnv(key + "_", acc)
values.toEnv(key+"_", acc)
} else {
acc[key] = fmt.Sprint(v)
}
Expand Down Expand Up @@ -124,11 +122,21 @@ func (v Values) Encode(w io.Writer) error {

// SetAtPath adds value to Values at the provided path, which can be a compound name.
// If there table missing from the path, they will be created.
func (v Values) GetAtPath(path string) (interface{},error) {
func (v Values) GetAtPath(path string) (interface{}, error) {
segs := strings.Split(path, ".")
return v.getAtPath(segs)
}

// MustSetAtPath adds value to Values at the provided path, which can be a compound name.
// This will panic if path is invalid, so it should only be used with literal string paths,
// not user provided ones.
func (v Values) MustSetAtPath(path string, value interface{}) {
err := v.SetAtPath(path, value)
if err != nil {
panic(fmt.Sprintf("invalid path; this method should only be passed a valid string literal: %s", err))
}
}

// SetAtPath adds value to Values at the provided path, which can be a compound name.
// If there table missing from the path, they will be created.
func (v Values) SetAtPath(path string, value interface{}) error {
Expand All @@ -151,7 +159,6 @@ func (v Values) AddEnvAsPath(prefix, envName string, value interface{}) error {
return err
}


func (v Values) getAtPath(path []string) (interface{}, error) {

if len(path) == 0 {
Expand Down Expand Up @@ -192,7 +199,7 @@ func (v Values) getAtPath(path []string) (interface{}, error) {

func (v Values) setAtPath(path []string, value interface{}) error {

if len(path) == 0{
if len(path) == 0 {
panic("invalid path")
}
name := path[0]
Expand Down Expand Up @@ -238,10 +245,10 @@ func (v Values) Merge(src Values) {
srcType := fmt.Sprintf("%T", srcVal)
destType := fmt.Sprintf("%T", destVal)
match := srcType == destType
validSrc :=istable(srcVal)
validDest :=istable(destVal)
validSrc := istable(srcVal)
validDest := istable(destVal)

if found &&match && validSrc && validDest {
if found && match && validSrc && validDest {
destMap := destVal.(Values)
srcMap := srcVal.(Values)
destMap.Merge(srcMap)
Expand Down Expand Up @@ -271,8 +278,6 @@ func istable(v interface{}) bool {
return ok
}



func tableLookup(v Values, simple string) (Values, error) {
v2, ok := v[simple]
if !ok {
Expand Down Expand Up @@ -312,7 +317,7 @@ func ReadValuesFile(filename string) (Values, error) {
return ReadValues(data)
}

func (v Values) cleanUp(){
func (v Values) cleanUp() {
for k, child := range v {
switch c := child.(type) {
case map[interface{}]interface{}:
Expand All @@ -329,4 +334,4 @@ func (v Values) cleanUp(){
default:
}
}
}
}
1 change: 1 addition & 0 deletions pkg/bosun/bosun.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func (b *Bosun) GetScripts() []*Script {

scripts := make([]*Script, len(env.Scripts))
copy(scripts, env.Scripts)
copy(scripts, b.GetMergedConfig().Scripts)
for _, app := range b.GetAppsSortedByName() {
for _, script := range app.Scripts {
script.Name = fmt.Sprintf("%s-%s", app.Name, script.Name)
Expand Down
2 changes: 1 addition & 1 deletion pkg/bosun/command_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (c *CommandValue) Resolve(ctx BosunContext) (string, error) {
c.resolved = true

if c.Value != "" {
c.resolvedValue = c.Value
c.resolvedValue, err = renderTemplate(ctx, c.Value)
} else {
c.resolvedValue, err = c.Command.Execute(ctx, CommandOpts{IgnoreDryRun: true})
// trim whitespace, as script output may contain line breaks at the end
Expand Down
Loading

0 comments on commit 29da428

Please sign in to comment.