Skip to content

Commit

Permalink
Add GetJSModules support from #1858
Browse files Browse the repository at this point in the history
This allows integration tests to have separate global module instance of
all internal js modules.

For this to be supported for extension an additional type/interface will
be required a bit more added code
  • Loading branch information
mstoykov committed Mar 17, 2021
1 parent 67e33ab commit 2808568
Show file tree
Hide file tree
Showing 13 changed files with 67 additions and 74 deletions.
10 changes: 7 additions & 3 deletions js/initcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ type InitContext struct {
logger logrus.FieldLogger

sharedObjects *common.SharedObjects

modules map[string]modules.HasModuleInstancePerVU
}

// NewInitContext creates a new initcontext with the provided arguments
Expand All @@ -88,6 +90,7 @@ func NewInitContext(
compatibilityMode: compatMode,
logger: logger,
sharedObjects: common.NewSharedObjects(),
modules: modules.GetJSModules(),
}
}

Expand All @@ -114,6 +117,7 @@ func newBoundInitContext(base *InitContext, ctxPtr *context.Context, rt *goja.Ru
compatibilityMode: base.compatibilityMode,
logger: base.logger,
sharedObjects: base.sharedObjects,
modules: base.modules,
}
}

Expand All @@ -140,11 +144,11 @@ func (i *InitContext) Require(arg string) goja.Value {
}

func (i *InitContext) requireModule(name string) (goja.Value, error) {
mod := modules.Get(name)
if mod == nil {
mod, ok := i.modules[name]
if !ok {
return nil, errors.Errorf("unknown module: %s", name)
}
return i.runtime.ToValue(common.Bind(i.runtime, mod, i.ctxPtr)), nil
return i.runtime.ToValue(common.Bind(i.runtime, mod.NewModuleInstancePerVU(), i.ctxPtr)), nil
}

func (i *InitContext) requireFile(name string) (goja.Value, error) {
Expand Down
61 changes: 50 additions & 11 deletions js/internal/modules/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ package modules
import (
"fmt"
"sync"

"github.com/loadimpact/k6/js/modules/k6"
"github.com/loadimpact/k6/js/modules/k6/crypto"
"github.com/loadimpact/k6/js/modules/k6/crypto/x509"
"github.com/loadimpact/k6/js/modules/k6/data"
"github.com/loadimpact/k6/js/modules/k6/encoding"
"github.com/loadimpact/k6/js/modules/k6/grpc"
"github.com/loadimpact/k6/js/modules/k6/html"
"github.com/loadimpact/k6/js/modules/k6/http"
"github.com/loadimpact/k6/js/modules/k6/metrics"
"github.com/loadimpact/k6/js/modules/k6/ws"
)

//nolint:gochecknoglobals
Expand All @@ -31,17 +42,6 @@ var (
mx sync.RWMutex
)

// Get returns the module registered with name.
func Get(name string) interface{} {
mx.RLock()
defer mx.RUnlock()
mod := modules[name]
if i, ok := mod.(HasModuleInstancePerVU); ok {
return i.NewModuleInstancePerVU()
}
return mod
}

// HasModuleInstancePerVU should be implemented by all native Golang modules that
// would require per-VU state. k6 will call their NewModuleInstancePerVU() methods
// every time a VU imports the module and use its result as the returned object.
Expand All @@ -61,3 +61,42 @@ func Register(name string, mod interface{}) {
}
modules[name] = mod
}

// GetJSModules returns a map of all js modules
func GetJSModules() map[string]HasModuleInstancePerVU {
result := map[string]HasModuleInstancePerVU{
// TODO add others
"k6": HasModuleInstancePerVUDummyWrapper{Module: k6.New()},
"k6/crypto": HasModuleInstancePerVUDummyWrapper{Module: crypto.New()},
"k6/crypto/x509": HasModuleInstancePerVUDummyWrapper{Module: x509.New()},
"k6/data": HasModuleInstancePerVUDummyWrapper{Module: data.New()},
"k6/encoding": HasModuleInstancePerVUDummyWrapper{Module: encoding.New()},
"k6/net/grpc": HasModuleInstancePerVUDummyWrapper{Module: grpc.New()},
"k6/html": HasModuleInstancePerVUDummyWrapper{Module: html.New()},
"k6/http": http.New(),
"k6/metrics": HasModuleInstancePerVUDummyWrapper{Module: metrics.New()},
"k6/ws": HasModuleInstancePerVUDummyWrapper{Module: ws.New()},
}

for name, module := range modules {
if perInstance, ok := module.(HasModuleInstancePerVU); ok {
result[name] = perInstance
} else {
result[name] = HasModuleInstancePerVUDummyWrapper{Module: module}
}
}
return result
}

// HasModuleInstancePerVUDummyWrapper is a wrapper to be used around an module that doesn't support
// HasModuleIntancePerVU, but needs to be used in places where it is required.
type HasModuleInstancePerVUDummyWrapper struct {
Module interface{}
}

// NewModuleInstancePerVU implements HasModuleInstancePerVU
func (h HasModuleInstancePerVUDummyWrapper) NewModuleInstancePerVU() interface{} {
return h.Module
}

var _ HasModuleInstancePerVU = HasModuleInstancePerVUDummyWrapper{}
5 changes: 0 additions & 5 deletions js/modules/k6/crypto/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,8 @@ import (
"golang.org/x/crypto/ripemd160"

"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
)

func init() {
modules.Register("k6/crypto", New())
}

type Crypto struct{}

type Hasher struct {
Expand Down
7 changes: 1 addition & 6 deletions js/modules/k6/crypto/x509/x509.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,8 @@ import (
"github.com/pkg/errors"

"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
)

func init() {
modules.Register("k6/crypto/x509", New())
}

// X509 certificate functionality
type X509 struct{}

Expand Down Expand Up @@ -224,7 +219,7 @@ func iso8601(value time.Time) string {
}

func makeRdns(names []pkix.AttributeTypeAndValue) []RDN {
var result = make([]RDN, len(names))
result := make([]RDN, len(names))
for i, name := range names {
result[i] = makeRdn(name)
}
Expand Down
6 changes: 3 additions & 3 deletions js/modules/k6/data/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ import (

"github.com/dop251/goja"
"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
"github.com/loadimpact/k6/lib"
"github.com/pkg/errors"
)

type data struct{}

func init() {
modules.Register("k6/data", new(data))
// New return a new Module instance
func New() interface{} {
return new(data)
}

const sharedArrayNamePrefix = "k6/data/SharedArray."
Expand Down
5 changes: 0 additions & 5 deletions js/modules/k6/encoding/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,8 @@ import (
"encoding/base64"

"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
)

func init() {
modules.Register("k6/encoding", New())
}

type Encoding struct{}

func New() *Encoding {
Expand Down
6 changes: 0 additions & 6 deletions js/modules/k6/grpc/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,8 @@ package grpc

import (
"google.golang.org/grpc/codes"

"github.com/loadimpact/k6/js/internal/modules"
)

func init() {
modules.Register("k6/net/grpc", New())
}

// GRPC represents the gRPC protocol module for k6
type GRPC struct {
StatusOK codes.Code `js:"StatusOK"`
Expand Down
7 changes: 0 additions & 7 deletions js/modules/k6/html/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,8 @@ import (
gohtml "golang.org/x/net/html"

"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
)

func init() {
modules.Register("k6/html", New())
}

type HTML struct{}

func New() *HTML {
Expand Down Expand Up @@ -78,7 +73,6 @@ func (s Selection) varargFnCall(arg interface{},
strFilter func(string) *goquery.Selection,
selFilter func(*goquery.Selection) *goquery.Selection,
nodeFilter func(...*gohtml.Node) *goquery.Selection) Selection {

switch v := arg.(type) {
case Selection:
return Selection{s.rt, selFilter(v.sel), s.URL}
Expand Down Expand Up @@ -113,7 +107,6 @@ func (s Selection) adjacentUntil(until func(string) *goquery.Selection,
filteredUntil func(string, string) *goquery.Selection,
filteredUntilSelection func(string, *goquery.Selection) *goquery.Selection,
def ...goja.Value) Selection {

switch len(def) {
case 0:
return Selection{s.rt, until(""), s.URL}
Expand Down
12 changes: 5 additions & 7 deletions js/modules/k6/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,10 @@ import (
"context"

"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
"github.com/loadimpact/k6/lib"
"github.com/loadimpact/k6/lib/netext"
)

func init() {
modules.Register("k6/http", new(GlobalHTTP))
}

const (
HTTP_METHOD_GET = "GET"
HTTP_METHOD_POST = "POST"
Expand All @@ -46,11 +41,14 @@ const (
// ErrJarForbiddenInInitContext is used when a cookie jar was made in the init context
var ErrJarForbiddenInInitContext = common.NewInitContextError("Making cookie jars in the init context is not supported")

// New returns a new global module instance
func New() *GlobalHTTP {
return &GlobalHTTP{}
}

// GlobalHTTP is a global HTTP module for a k6 instance/test run
type GlobalHTTP struct{}

var _ modules.HasModuleInstancePerVU = new(GlobalHTTP)

// NewModuleInstancePerVU returns an HTTP instance for each VU
func (g *GlobalHTTP) NewModuleInstancePerVU() interface{} { // this here needs to return interface{}
return &HTTP{ // change the below fields to be not writable or not fields
Expand Down
5 changes: 0 additions & 5 deletions js/modules/k6/k6.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,11 @@ import (
"github.com/pkg/errors"

"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
"github.com/loadimpact/k6/lib"
"github.com/loadimpact/k6/lib/metrics"
"github.com/loadimpact/k6/stats"
)

func init() {
modules.Register("k6", New())
}

type K6 struct{}

// ErrGroupInInitContext is returned when group() are using in the init context
Expand Down
7 changes: 1 addition & 6 deletions js/modules/k6/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,10 @@ import (
"github.com/dop251/goja"

"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
"github.com/loadimpact/k6/lib"
"github.com/loadimpact/k6/stats"
)

func init() {
modules.Register("k6/metrics", New())
}

var nameRegexString = "^[\\p{L}\\p{N}\\._ !\\?/&#\\(\\)<>%-]{1,128}$"

var compileNameRegex = regexp.MustCompile(nameRegexString)
Expand All @@ -59,7 +54,7 @@ func newMetric(ctxPtr *context.Context, name string, t stats.MetricType, isTime
return nil, errors.New("metrics must be declared in the init context")
}

//TODO: move verification outside the JS
// TODO: move verification outside the JS
if !checkName(name) {
return nil, common.NewInitContextError(fmt.Sprintf("Invalid metric name: '%s'", name))
}
Expand Down
5 changes: 0 additions & 5 deletions js/modules/k6/ws/ws.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,11 @@ import (
"github.com/gorilla/websocket"

"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
"github.com/loadimpact/k6/lib"
"github.com/loadimpact/k6/lib/metrics"
"github.com/loadimpact/k6/stats"
)

func init() {
modules.Register("k6/ws", New())
}

// ErrWSInInitContext is returned when websockets are using in the init context
var ErrWSInInitContext = common.NewInitContextError("using websockets in the init context is not supported")

Expand Down
5 changes: 0 additions & 5 deletions js/modules/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ import (

const extPrefix string = "k6/x/"

// Get returns the module registered with name.
func Get(name string) interface{} {
return modules.Get(name)
}

// Register the given mod as an external JavaScript module that can be imported
// by name. The name must be unique across all registered modules and must be
// prefixed with "k6/x/", otherwise this function will panic.
Expand Down

0 comments on commit 2808568

Please sign in to comment.