Skip to content

Commit

Permalink
Merge branch 'master' into real-time-metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
na-- authored Jun 29, 2018
2 parents 748892c + cd557cf commit a9a134a
Show file tree
Hide file tree
Showing 17 changed files with 268 additions and 101 deletions.
14 changes: 10 additions & 4 deletions cmd/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"time"

"github.com/kelseyhightower/envconfig"
"github.com/loadimpact/k6/lib"
"github.com/loadimpact/k6/stats/cloud"
"github.com/loadimpact/k6/ui"
"github.com/pkg/errors"
Expand Down Expand Up @@ -168,7 +169,7 @@ This will execute the test on the Load Impact cloud service. Use "k6 login cloud
case <-ticker.C:
testProgress, progressErr = client.GetTestProgress(refID)
if progressErr == nil {
if (testProgress.RunStatus > 2) || (exitOnRunning && testProgress.RunStatus == 2) {
if (testProgress.RunStatus > lib.RunStatusRunning) || (exitOnRunning && testProgress.RunStatus == lib.RunStatusRunning) {
shouldExitLoop = true
}
progress.Progress = testProgress.Progress
Expand All @@ -180,17 +181,22 @@ This will execute the test on the Load Impact cloud service. Use "k6 login cloud
break runningLoop
}
case sig := <-sigC:
log.WithField("sig", sig).Debug("Exiting in response to signal")
log.WithField("sig", sig).Print("Exiting in response to signal...")
err := client.StopCloudTestRun(refID)
if err != nil {
log.WithError(err).Error("Stop cloud test error")
}
shouldExitLoop = true
shouldExitLoop = true // Exit after the next GetTestProgress call
}
}

if testProgress == nil {
return ExitCode{errors.New("Test progress error"), 98}
}

fmt.Fprintf(stdout, " test status: %s\n", ui.ValueColor.Sprint(testProgress.RunStatusText))

if testProgress.ResultStatus == 1 {
if testProgress.ResultStatus == cloud.ResultStatusFailed {
return ExitCode{errors.New("The test has failed"), 99}
}

Expand Down
14 changes: 4 additions & 10 deletions cmd/login_influxdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,34 +54,28 @@ This will set the default server used when just "-o influxdb" is passed.`,
}
conf = conf.Apply(urlConf)
}
if conf.Addr == "" {
conf.Addr = "http://localhost:8086"
}
if conf.DB == "" {
conf.DB = "k6"
}

form := ui.Form{
Fields: []ui.Field{
ui.StringField{
Key: "Addr",
Label: "Address",
Default: conf.Addr,
Default: conf.Addr.String,
},
ui.StringField{
Key: "DB",
Label: "Database",
Default: conf.DB,
Default: conf.DB.String,
},
ui.StringField{
Key: "Username",
Label: "Username",
Default: conf.Username,
Default: conf.Username.String,
},
ui.StringField{
Key: "Password",
Label: "Password",
Default: conf.Password,
Default: conf.Password.String,
},
},
}
Expand Down
2 changes: 1 addition & 1 deletion core/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func NewEngine(ex lib.Executor, o lib.Options) (*Engine, error) {
return e, nil
}

func (e *Engine) setRunStatus(status int) {
func (e *Engine) setRunStatus(status lib.RunStatus) {
if len(e.Collectors) == 0 {
return
}
Expand Down
25 changes: 18 additions & 7 deletions lib/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,24 @@ import (
"github.com/loadimpact/k6/stats"
)

// Run Status used by cloud collector
// RunStatus values can be used by k6 to denote how a script run ends
// and by the cloud executor and collector so that k6 knows the current
// status of a particular script run.
type RunStatus int

// Possible run status values; iota isn't used intentionally
const (
RunStatusFinished = 3
RunStatusAbortedUser = 5
RunStatusAbortedSystem = 6
RunStatusAbortedScriptError = 7
RunStatusAbortedThreshold = 8
RunStatusCreated RunStatus = -2
RunStatusValidated RunStatus = -1
RunStatusQueued RunStatus = 0
RunStatusInitializing RunStatus = 1
RunStatusRunning RunStatus = 2
RunStatusFinished RunStatus = 3
RunStatusTimedOut RunStatus = 4
RunStatusAbortedUser RunStatus = 5
RunStatusAbortedSystem RunStatus = 6
RunStatusAbortedScriptError RunStatus = 7
RunStatusAbortedThreshold RunStatus = 8
)

// A Collector abstracts the process of funneling samples to an external storage backend,
Expand All @@ -57,5 +68,5 @@ type Collector interface {
GetRequiredSystemTags() TagSet

// Set run status
SetRunStatus(status int)
SetRunStatus(status RunStatus)
}
55 changes: 55 additions & 0 deletions lib/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,64 @@ package types
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"time"

null "gopkg.in/guregu/null.v3"
)

func NullDecoder(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
typeFrom := f.String()
typeTo := t.String()

expectedType := ""
switch typeTo {
case "null.String":
if typeFrom == reflect.String.String() {
return null.StringFrom(data.(string)), nil
}
expectedType = reflect.String.String()
case "null.Bool":
if typeFrom == reflect.Bool.String() {
return null.BoolFrom(data.(bool)), nil
}
expectedType = reflect.Bool.String()
case "null.Int":
if typeFrom == reflect.Int.String() {
return null.IntFrom(int64(data.(int))), nil
}
if typeFrom == reflect.Int32.String() {
return null.IntFrom(int64(data.(int32))), nil
}
if typeFrom == reflect.Int64.String() {
return null.IntFrom(data.(int64)), nil
}
expectedType = reflect.Int.String()
case "null.Float":
if typeFrom == reflect.Float32.String() {
return null.FloatFrom(float64(data.(float32))), nil
}
if typeFrom == reflect.Float64.String() {
return null.FloatFrom(data.(float64)), nil
}
expectedType = reflect.Float32.String() + " or " + reflect.Float64.String()
case "types.NullDuration":
if typeFrom == reflect.String.String() {
var d NullDuration
err := d.UnmarshalText([]byte(data.(string)))
return d, err
}
expectedType = reflect.String.String()
}

if expectedType != "" {
return data, fmt.Errorf("expected '%s', got '%s'", expectedType, typeFrom)
}

return data, nil
}

// Duration is an alias for time.Duration that de/serialises to JSON as human-readable strings.
type Duration time.Duration

Expand Down
71 changes: 71 additions & 0 deletions lib/types/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,83 @@ package types

import (
"encoding/json"
"fmt"
"testing"
"time"

"github.com/mitchellh/mapstructure"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
null "gopkg.in/guregu/null.v3"
)

func TestNullDecoder(t *testing.T) {
type foo struct {
Strs []string
Str null.String
Boolean null.Bool
Integer null.Int
Integer32 null.Int
Integer64 null.Int
Float32 null.Float
Float64 null.Float
Dur NullDuration
}
f := foo{}
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
DecodeHook: NullDecoder,
Result: &f,
})
require.NoError(t, err)

conf := map[string]interface{}{
"strs": []string{"fake"},
"str": "bar",
"boolean": true,
"integer": 42,
"integer32": int32(42),
"integer64": int64(42),
"float32": float32(3.14),
"float64": float64(3.14),
"dur": "1m",
}

err = dec.Decode(conf)
require.NoError(t, err)

require.Equal(t, foo{
Strs: []string{"fake"},
Str: null.StringFrom("bar"),
Boolean: null.BoolFrom(true),
Integer: null.IntFrom(42),
Integer32: null.IntFrom(42),
Integer64: null.IntFrom(42),
Float32: null.FloatFrom(3.140000104904175),
Float64: null.FloatFrom(3.14),
Dur: NewNullDuration(1*time.Minute, true),
}, f)

input := map[string][]interface{}{
"Str": {true, "string", "bool"},
"Boolean": {"invalid", "bool", "string"},
"Integer": {"invalid", "int", "string"},
"Integer32": {true, "int", "bool"},
"Integer64": {"invalid", "int", "string"},
"Float32": {true, "float32 or float64", "bool"},
"Float64": {"invalid", "float32 or float64", "string"},
"Dur": {10, "string", "int"},
}

for k, v := range input {
t.Run("Error Message/"+k, func(t *testing.T) {
err = dec.Decode(map[string]interface{}{
k: v[0],
})
assert.EqualError(t, err, fmt.Sprintf("1 error(s) decoding:\n\n* error decoding '%s': expected '%s', got '%s'", k, v[1], v[2]))
})
}
}

func TestDuration(t *testing.T) {
t.Run("String", func(t *testing.T) {
assert.Equal(t, "1m15s", Duration(75*time.Second).String())
Expand Down
1 change: 1 addition & 0 deletions release notes/upcoming.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Previously most metrics were emitted only when a script iteration ended. With th
## Bugs fixed!

* Metrics emitted by `setup()` and `teardown()` are not discarded anymore. They are emitted and have the implicit root `group` tag values of `setup` and `teardown` respectively (#678)
* Fixed a potential `nil` pointer error when the `k6 cloud` command is interrupted. (#682)


## Breaking Changes
Expand Down
25 changes: 16 additions & 9 deletions stats/cloud/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ import (
"github.com/pkg/errors"
)

type ResultStatus int

const (
ResultStatusPassed ResultStatus = 0
ResultStatusFailed ResultStatus = 1
)

type ThresholdResult map[string]map[string]bool

type TestRun struct {
Expand All @@ -50,10 +57,10 @@ type CreateTestRunResponse struct {
}

type TestProgressResponse struct {
RunStatusText string `json:"run_status_text"`
RunStatus int `json:"run_status"`
ResultStatus int `json:"result_status"`
Progress float64 `json:"progress"`
RunStatusText string `json:"run_status_text"`
RunStatus lib.RunStatus `json:"run_status"`
ResultStatus ResultStatus `json:"result_status"`
Progress float64 `json:"progress"`
}

type LoginResponse struct {
Expand Down Expand Up @@ -156,17 +163,17 @@ func (c *Client) StartCloudTestRun(name string, projectID int64, arc *lib.Archiv
return ctrr.ReferenceID, nil
}

func (c *Client) TestFinished(referenceID string, thresholds ThresholdResult, tained bool, runStatus int) error {
func (c *Client) TestFinished(referenceID string, thresholds ThresholdResult, tained bool, runStatus lib.RunStatus) error {
url := fmt.Sprintf("%s/tests/%s", c.baseURL, referenceID)

resultStatus := 0
resultStatus := ResultStatusPassed
if tained {
resultStatus = 1
resultStatus = ResultStatusFailed
}

data := struct {
ResultStatus int `json:"result_status"`
RunStatus int `json:"run_status"`
ResultStatus ResultStatus `json:"result_status"`
RunStatus lib.RunStatus `json:"run_status"`
Thresholds ThresholdResult `json:"thresholds"`
}{
resultStatus,
Expand Down
4 changes: 2 additions & 2 deletions stats/cloud/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type Collector struct {
client *Client

anonymous bool
runStatus int
runStatus lib.RunStatus

bufferMutex sync.Mutex
bufferHTTPTrails []*netext.Trail
Expand Down Expand Up @@ -457,6 +457,6 @@ func (c *Collector) GetRequiredSystemTags() lib.TagSet {
return lib.GetTagSet("name", "method", "status", "error", "check", "group")
}

func (c *Collector) SetRunStatus(status int) {
func (c *Collector) SetRunStatus(status lib.RunStatus) {
c.runStatus = status
}
9 changes: 5 additions & 4 deletions stats/dummy/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

// Collector implements the lib.Collector interface and should be used only for testing
type Collector struct {
runStatus int
RunStatus lib.RunStatus

SampleContainers []stats.SampleContainer
Samples []stats.Sample
Expand All @@ -48,7 +48,7 @@ func (c *Collector) MakeConfig() interface{} { return nil }
// Run just blocks until the context is done
func (c *Collector) Run(ctx context.Context) {
<-ctx.Done()
log.Debugf("finished status: %s", c.runStatus)
log.Debugf("finished status: %s", c.RunStatus)
}

// Collect just appends all of the samples passed to it to the internal sample slice.
Expand All @@ -74,6 +74,7 @@ func (c *Collector) GetRequiredSystemTags() lib.TagSet {
return lib.TagSet{} // There are no required tags for this collector
}

func (c *Collector) SetRunStatus(status int) {
c.runStatus = status
// SetRunStatus just saves the passed status for later inspection
func (c *Collector) SetRunStatus(status lib.RunStatus) {
c.RunStatus = status
}
5 changes: 3 additions & 2 deletions stats/influxdb/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (c *Collector) Collect(scs []stats.SampleContainer) {
}

func (c *Collector) Link() string {
return c.Config.Addr
return c.Config.Addr.String
}

func (c *Collector) commit() {
Expand Down Expand Up @@ -192,4 +192,5 @@ func (c *Collector) GetRequiredSystemTags() lib.TagSet {
return lib.TagSet{} // There are no required tags for this collector
}

func (c *Collector) SetRunStatus(status int) {}
// SetRunStatus does nothing in the InfluxDB collector
func (c *Collector) SetRunStatus(status lib.RunStatus) {}
Loading

0 comments on commit a9a134a

Please sign in to comment.