diff --git a/.travis.yml b/.travis.yml index 5395a697..4b4e1a2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ install: - "go get -d -t ./..." script: - - "if test $GIMME_OS.$GIMME_ARCH = linux.amd64; then make rebuild; ${GOPATH}/bin/gotestcover -v -coverprofile=coverage.report ./...; go tool cover -func=coverage.report; else go install ./...; fi" + - "if test $GIMME_OS.$GIMME_ARCH = linux.amd64; then make rebuild check; ${GOPATH}/bin/gotestcover -v -coverprofile=coverage.report ./...; go tool cover -func=coverage.report; else go install ./...; fi" after_script: - "if test $GIMME_OS.$GIMME_ARCH = linux.amd64; then $HOME/gopath/bin/goveralls -coverprofile=coverage.report -service=travis-ci; fi" diff --git a/Makefile b/Makefile index 60836527..a702b6e2 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ build: generate: go get github.com/progrium/go-extpoints go get github.com/jonasfj/go-import-subtree + go get github.com/taskcluster/taskcluster-worker/codegen/... go generate ./... go fmt ./... diff --git a/codegen/global-config.yml b/codegen/global-config.yml new file mode 100644 index 00000000..f6196c6b --- /dev/null +++ b/codegen/global-config.yml @@ -0,0 +1,16 @@ +$schema: http://json-schema.org/draft-04/schema# +title: Worker Config +description: |- + Schema to represent the global config object used for configuring taskcluster-worker +type: object +properties: + workerId: + title: WorkerID + description: |- + blah blah + type: string +additionalProperties: false +required: + - engines + - plugins + - workerId diff --git a/codegen/go-composite-schema/main.go b/codegen/go-composite-schema/main.go new file mode 100644 index 00000000..18cca08f --- /dev/null +++ b/codegen/go-composite-schema/main.go @@ -0,0 +1,168 @@ +// go-composite-schema is responsible for auto-generating plugin and engine code based +// on static yml json schema files +package main + +import ( + "fmt" + "go/build" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + "github.com/docopt/docopt-go" + "github.com/ghodss/yaml" + "github.com/kr/text" + "github.com/taskcluster/jsonschema2go" + "github.com/taskcluster/taskcluster-base-go/jsontest" + "golang.org/x/tools/imports" +) + +var ( + version = "go-composite-schema 1.0.0" + usage = ` +go-composite-schema +go-composite-schema is a tool for generating go source code for a function to +return a CompositeSchema based on a static json schema definition stored in a +yaml/json file in a package directory of a go project, together with some +parameters included in a "go:generate" command. See +https://godoc.org/github.com/taskcluster/taskcluster-worker/runtime#CompositeSchema +for more information. + +Although the go-composite-schema command line tool can be used wherever you +require a CompositeSchema, it is currently most appliable to taskcluster-worker +engines and plugins. + +Typically, you should include a code generation comment in your source code for +each CompositeSchema returning function you wish to create: + + //go:generate go-composite-schema [--required] PROPERTY INPUT-FILE OUTPUT-FILE + +We currently use CompositeSchemas for defining both config structures and +payload structures, for both engines and plugins. Therefore typically there +could be one or two "go:generate" lines required per plugin and probably two +per engine. This is because some plugins might not have custom config nor even +custom payload, but engines are likely to require both. + +Please note, it is recommended to set environment variable GOPATH in order for +go-composite-schema to correctly determine the correct package name. + + + Usage: + go-composite-schema [--required] PROPERTY INPUT-FILE OUTPUT-FILE + go-composite-schema -h|--help + go-composite-schema --version + + Options: + -h --help Display this help text. + --version Display the version (` + version + `). +` +) + +func main() { + // Clear all logging fields, such as timestamps etc... + log.SetFlags(0) + log.SetPrefix("go-composite-schema: ") + + // Parse the docopt string and exit on any error or help message. + args, err := docopt.Parse(usage, nil, true, version, false, true) + if err != nil { + log.Fatalf("ERROR: Cannot parse arguments: %s\n", err) + } + + // assuming non-nil, and always type bool + req := args["--required"].(bool) + + // assuming non-nil, and always type string + schemaProperty := args["PROPERTY"].(string) + inputFile := args["INPUT-FILE"].(string) + outputFile := args["OUTPUT-FILE"].(string) + + // Get working directory + currentFolder, err := os.Getwd() + if err != nil { + log.Fatalf("Unable to obtain current working directory: %s", err) + } + + // Read current package + pkg, err := build.ImportDir(currentFolder, build.AllowBinary) + if err != nil { + log.Fatalf("ERROR: Failed to determine go package inside directory '%s' - is your GOPATH set correctly ('%s')? Error: %s", currentFolder, os.Getenv("GOPATH"), err) + } + + // Generate go types... + ymlFile := filepath.Join(currentFolder, inputFile) + if _, err := os.Stat(ymlFile); err == nil { + log.Printf("Found yaml file '%v'", ymlFile) + } else { + log.Fatalf("ERROR: could not read file '%v'", ymlFile) + } + url := "file://" + ymlFile + goFile := filepath.Join(currentFolder, outputFile) + log.Printf("Generating '%v'...", goFile) + job := &jsonschema2go.Job{ + Package: pkg.Name, + URLs: []string{url}, + ExportTypes: false, + } + result, err := job.Execute() + if err != nil { + log.Fatalf("ERROR: Problem assembling content for file '%v': %s", goFile, err) + } + generatedCode := append(result.SourceCode, []byte("\n"+generateFunctions(ymlFile, result.SchemaSet.SubSchema(url).TypeName, schemaProperty, req))...) + sourceCode, err := imports.Process( + goFile, + []byte(generatedCode), + &imports.Options{ + AllErrors: true, + Comments: true, + TabIndent: true, + TabWidth: 0, + Fragment: false, + }, + ) + if err != nil { + log.Fatalf("ERROR: Could not format generated source code for file '%v': %s\nCode:\n%v", goFile, err, string(generatedCode)) + } + ioutil.WriteFile(goFile, []byte(sourceCode), 0644) + if err != nil { + log.Fatalf("ERROR: Could not write generated source code to file '%v': %s", goFile, err) + } +} + +func generateFunctions(ymlFile, goType, schemaProperty string, req bool) string { + data, err := ioutil.ReadFile(ymlFile) + if err != nil { + log.Fatalf("ERROR: Problem reading from file '%v' - %s", ymlFile, err) + } + // json is valid YAML, so we can safely convert, even if it is already json + rawJson, err := yaml.YAMLToJSON(data) + if err != nil { + log.Fatalf("ERROR: Problem converting file '%v' to json format - %s", ymlFile, err) + } + rawJson, err = jsontest.FormatJson(rawJson) + if err != nil { + log.Fatalf("ERROR: Problem pretty printing json in '%v' - %s", ymlFile, err) + } + result := "var " + goType + "Schema = func() runtime.CompositeSchema {\n" + result += "\tschema, err := runtime.NewCompositeSchema(\n" + result += "\t\t\"" + schemaProperty + "\",\n" + result += "\t\t`\n" + // the following strings.Replace function call safely escapes backticks (`) in rawJson + result += strings.Replace(text.Indent(fmt.Sprintf("%v", string(rawJson)), "\t\t")+"\n", "`", "` + \"`\" + `", -1) + result += "\t\t`,\n" + if req { + result += "\t\ttrue,\n" + } + result += "\t\tfunc() interface{} {\n" + result += "\t\t\treturn &" + goType + "{}\n" + result += "\t\t},\n" + result += "\t)\n" + result += "\tif err != nil {\n" + result += "\t\tpanic(err)\n" + result += "\t}\n" + result += "\treturn schema\n" + result += "}()\n" + return result +} diff --git a/engines/docker/config-schema.yml b/engines/docker/config-schema.yml new file mode 100644 index 00000000..01cf0f76 --- /dev/null +++ b/engines/docker/config-schema.yml @@ -0,0 +1,14 @@ +$schema: http://json-schema.org/draft-04/schema# +title: Config +description: |- + Config applicable to docker engine +type: object +properties: + rootVolume: + title: Root Volume + description: |- + Root Volume blah blah + type: string +additionalProperties: false +required: + - rootVolume diff --git a/engines/docker/docker.go b/engines/docker/docker.go new file mode 100644 index 00000000..077b13a4 --- /dev/null +++ b/engines/docker/docker.go @@ -0,0 +1,4 @@ +//go:generate go-composite-schema --required config config-schema.yml generated_configschema.go + +// comments +package docker diff --git a/engines/docker/generated_configschema.go b/engines/docker/generated_configschema.go new file mode 100644 index 00000000..879c2a96 --- /dev/null +++ b/engines/docker/generated_configschema.go @@ -0,0 +1,47 @@ +// This source code file is AUTO-GENERATED by github.com/taskcluster/jsonschema2go + +package docker + +import "github.com/taskcluster/taskcluster-worker/runtime" + +type ( + // Config applicable to docker engine + config struct { + + // Root Volume blah blah + RootVolume string `json:"rootVolume"` + } +) + +var configSchema = func() runtime.CompositeSchema { + schema, err := runtime.NewCompositeSchema( + "config", + ` + { + "$schema": "http://json-schema.org/draft-04/schema#", + "additionalProperties": false, + "description": "Config applicable to docker engine", + "properties": { + "rootVolume": { + "description": "Root Volume blah blah", + "title": "Root Volume", + "type": "string" + } + }, + "required": [ + "rootVolume" + ], + "title": "Config", + "type": "object" + } + `, + true, + func() interface{} { + return &config{} + }, + ) + if err != nil { + panic(err) + } + return schema +}() diff --git a/engines/engine.go b/engines/engine.go index 0cd24834..b2a50cce 100644 --- a/engines/engine.go +++ b/engines/engine.go @@ -11,10 +11,6 @@ type SandboxOptions struct { TaskContext *runtime.TaskContext // Result from PayloadSchema().Parse(). Implementors are safe to assert // this back to their target type. - // Note: This is passed by-value for efficiency (and to prohibit nil), if - // adding any large fields please consider adding them as pointers. - // Note: This is intended to be a simple argument wrapper, do not add methods - // to this struct. Payload interface{} } @@ -126,6 +122,12 @@ func (EngineBase) PayloadSchema() runtime.CompositeSchema { return runtime.NewEmptyCompositeSchema() } +// ConfigSchema returns an empty jsonschema indicating that no custom config is +// required. +func (EngineBase) ConfigSchema() runtime.CompositeSchema { + return runtime.NewEmptyCompositeSchema() +} + // Capabilities returns an zero value Capabilities struct indicating that // most features aren't supported. func (EngineBase) Capabilities() Capabilities { diff --git a/engines/enginetest/testutils.go b/engines/enginetest/testutils.go index 281b83c3..555f8272 100644 --- a/engines/enginetest/testutils.go +++ b/engines/enginetest/testutils.go @@ -48,7 +48,7 @@ func (p *engineProvider) ensureEngine(engineName string) { fmtPanic("Couldn't find EngineProvider: ", engineName) } // Create Engine instance - engine, err := engineProvider(extpoints.EngineOptions{ + engine, err := engineProvider.NewEngine(extpoints.EngineOptions{ Environment: p.environment, Log: p.environment.Log.WithField("engine", engineName), }) diff --git a/engines/extpoints/interfaces.go b/engines/extpoints/interfaces.go index bbf88ecb..a355ccec 100644 --- a/engines/extpoints/interfaces.go +++ b/engines/extpoints/interfaces.go @@ -34,4 +34,10 @@ type EngineOptions struct { // Any error here will be fatal and likely cause the worker to stop working. // If an implementor can determine that the platform isn't supported at // compile-time it is recommended to not register the implementation. -type EngineProvider func(options EngineOptions) (engines.Engine, error) +type EngineProvider interface { + NewEngine(options EngineOptions) (engines.Engine, error) + + // ConfigSchema returns the CompositeSchema that represents the engine + // configuration + ConfigSchema() runtime.CompositeSchema +} diff --git a/engines/mock/generated_payloadschema.go b/engines/mock/generated_payloadschema.go new file mode 100644 index 00000000..fa9a336b --- /dev/null +++ b/engines/mock/generated_payloadschema.go @@ -0,0 +1,70 @@ +// This source code file is AUTO-GENERATED by github.com/taskcluster/jsonschema2go + +package mockengine + +import "github.com/taskcluster/taskcluster-worker/runtime" + +type ( + payload struct { + Argument string `json:"argument"` + + Delay int `json:"delay"` + + // Possible values: + // * "true" + // * "false" + // * "set-volume" + // * "get-volume" + // * "ping-proxy" + // * "write-log" + // * "write-error-log" + Function string `json:"function"` + } +) + +var payloadSchema = func() runtime.CompositeSchema { + schema, err := runtime.NewCompositeSchema( + "start", + ` + { + "$schema": "http://json-schema.org/draft-04/schema#", + "additionalProperties": false, + "properties": { + "argument": { + "type": "string" + }, + "delay": { + "type": "integer" + }, + "function": { + "enum": [ + "true", + "false", + "set-volume", + "get-volume", + "ping-proxy", + "write-log", + "write-error-log" + ], + "type": "string" + } + }, + "required": [ + "delay", + "function", + "argument" + ], + "title": "Payload", + "type": "object" + } + `, + true, + func() interface{} { + return &payload{} + }, + ) + if err != nil { + panic(err) + } + return schema +}() diff --git a/engines/mock/mockengine.go b/engines/mock/mockengine.go index 54f1746e..debe78af 100644 --- a/engines/mock/mockengine.go +++ b/engines/mock/mockengine.go @@ -1,3 +1,5 @@ +//go:generate go-composite-schema --required start payload-schema.yml generated_payloadschema.go + // Package mockengine implements a MockEngine that doesn't really do anything, // but allows us to test plugins without having to run a real engine. package mockengine @@ -17,51 +19,26 @@ type engine struct { Log *logrus.Entry } +type engineProvider struct { +} + +func (e engineProvider) NewEngine(options extpoints.EngineOptions) (engines.Engine, error) { + fmt.Println(options.Log) + return engine{Log: options.Log}, nil +} + func init() { // Register the mock engine as an import side-effect - extpoints.EngineProviders.Register(func( - options extpoints.EngineOptions, - ) (engines.Engine, error) { - fmt.Println(options.Log) - return engine{Log: options.Log}, nil - }, "mock") + extpoints.EngineProviders.Register(new(engineProvider), "mock") } -// task.payload.start when engine is "mock" -type payload struct { - Function string `json:"function"` - Argument string `json:"argument"` - Delay int64 `json:"delay"` +// mock config contains no fields +func (e engineProvider) ConfigSchema() runtime.CompositeSchema { + return runtime.NewEmptyCompositeSchema() } -func (e engine) PayloadSchema() runtime.CompositeSchema { - // Declare the schema for the "task.payload.start" property - schema, err := runtime.NewCompositeSchema("start", `{ - "type": "object", - "properties": { - "delay": {"type": "integer"}, - "function": { - "type": "string", - "enum": [ - "true", - "false", - "set-volume", - "get-volume", - "ping-proxy", - "write-log", - "write-error-log" - ] - }, - "argument": {"type": "string"} - }, - "required": ["delay", "function", "argument"], - "additionalProperties": false - }`, true, func() interface{} { return &payload{} }) - if err != nil { - // Any errors here are supposed to be static - panic(err) - } - return schema +func (e engineProvider) PayloadSchema() runtime.CompositeSchema { + return payloadSchema } func (e engine) NewSandboxBuilder(options engines.SandboxOptions) (engines.SandboxBuilder, error) { diff --git a/engines/mock/payload-schema.yml b/engines/mock/payload-schema.yml new file mode 100644 index 00000000..98cf6b04 --- /dev/null +++ b/engines/mock/payload-schema.yml @@ -0,0 +1,23 @@ +$schema: http://json-schema.org/draft-04/schema# +title: "Payload" +type: "object" +properties: + delay: + type: "integer" + function: + type: "string" + enum: + - "true" + - "false" + - "set-volume" + - "get-volume" + - "ping-proxy" + - "write-log" + - "write-error-log" + argument: + type: "string" +required: + - "delay" + - "function" + - "argument" +additionalProperties: false diff --git a/engines/winnative/config-schema.yml b/engines/winnative/config-schema.yml new file mode 100644 index 00000000..27bd64c3 --- /dev/null +++ b/engines/winnative/config-schema.yml @@ -0,0 +1,14 @@ +$schema: http://json-schema.org/draft-04/schema# +title: Config +description: |- + Config applicable to windows native engine +type: object +properties: + usePsExec: + title: Use PSExec + description: |- + Whether to use PSExec for executing processes + type: boolean +additionalProperties: false +required: + - usePsExec diff --git a/engines/winnative/generated_configschema.go b/engines/winnative/generated_configschema.go new file mode 100644 index 00000000..4c7afec2 --- /dev/null +++ b/engines/winnative/generated_configschema.go @@ -0,0 +1,47 @@ +// This source code file is AUTO-GENERATED by github.com/taskcluster/jsonschema2go + +package winnative + +import "github.com/taskcluster/taskcluster-worker/runtime" + +type ( + // Config applicable to windows native engine + config struct { + + // Whether to use PSExec for executing processes + UsePsExec bool `json:"usePsExec"` + } +) + +var configSchema = func() runtime.CompositeSchema { + schema, err := runtime.NewCompositeSchema( + "config", + ` + { + "$schema": "http://json-schema.org/draft-04/schema#", + "additionalProperties": false, + "description": "Config applicable to windows native engine", + "properties": { + "usePsExec": { + "description": "Whether to use PSExec for executing processes", + "title": "Use PSExec", + "type": "boolean" + } + }, + "required": [ + "usePsExec" + ], + "title": "Config", + "type": "object" + } + `, + true, + func() interface{} { + return &config{} + }, + ) + if err != nil { + panic(err) + } + return schema +}() diff --git a/engines/winnative/winnative.go b/engines/winnative/winnative.go new file mode 100644 index 00000000..3e85ee3b --- /dev/null +++ b/engines/winnative/winnative.go @@ -0,0 +1,4 @@ +//go:generate go-composite-schema --required config config-schema.yml generated_configschema.go + +// Package comments +package winnative diff --git a/main.go b/main.go index 32b32966..85b0b948 100644 --- a/main.go +++ b/main.go @@ -63,7 +63,7 @@ func main() { runtimeEnvironment := runtime.Environment{Log: logger} - engine, err := engineProvider(extpoints.EngineOptions{ + engine, err := engineProvider.NewEngine(extpoints.EngineOptions{ Environment: &runtimeEnvironment, Log: logger.WithField("engine", engineName), }) diff --git a/plugins/extpoints/interfaces.go b/plugins/extpoints/interfaces.go index 9c693d8c..65df570a 100644 --- a/plugins/extpoints/interfaces.go +++ b/plugins/extpoints/interfaces.go @@ -23,4 +23,10 @@ type PluginOptions struct { // // If an implementor can determine that a plugin isn't available at compile-time // it is preferred not to register the plugin. -type PluginProvider func(options PluginOptions) (plugins.Plugin, error) +type PluginProvider interface { + NewPlugin(options PluginOptions) (plugins.Plugin, error) + + // ConfigSchema returns the CompositeSchema that represents the plugin + // config. + ConfigSchema() runtime.CompositeSchema +} diff --git a/plugins/extpoints/manager.go b/plugins/extpoints/manager.go index 30399757..537ee438 100644 --- a/plugins/extpoints/manager.go +++ b/plugins/extpoints/manager.go @@ -68,7 +68,7 @@ func NewPluginManager(pluginsToLoad []string, options PluginOptions) (plugins.Pl if pluginProvider == nil { return nil, errors.New("Missing plugin") } - plugin, err := pluginProvider(PluginOptions{ + plugin, err := pluginProvider.NewPlugin(PluginOptions{ environment: options.environment, engine: options.engine, log: options.log.WithField("plugin", p), @@ -81,6 +81,11 @@ func NewPluginManager(pluginsToLoad []string, options PluginOptions) (plugins.Pl return &pluginManager{plugins: pluginObjects}, nil } +// Currently no config required for plugin manager +func (m *pluginManager) ConfigSchema() runtime.CompositeSchema { + return runtime.NewEmptyCompositeSchema() +} + func (m *pluginManager) PayloadSchema() (runtime.CompositeSchema, error) { schemas := []runtime.CompositeSchema{} for _, plugin := range m.plugins { diff --git a/plugins/livelog/config-schema.yml b/plugins/livelog/config-schema.yml new file mode 100644 index 00000000..8167c0bb --- /dev/null +++ b/plugins/livelog/config-schema.yml @@ -0,0 +1,14 @@ +$schema: http://json-schema.org/draft-04/schema# +title: Config +description: |- + LiveLog config +type: object +properties: + liveLogExe: + title: LiveLogExecutable + description: |- + The executable (.exe file) to run the livelog service + type: string +additionalProperties: false +required: + - liveLogExe diff --git a/plugins/livelog/generated_configschema.go b/plugins/livelog/generated_configschema.go new file mode 100644 index 00000000..c65aa51d --- /dev/null +++ b/plugins/livelog/generated_configschema.go @@ -0,0 +1,47 @@ +// This source code file is AUTO-GENERATED by github.com/taskcluster/jsonschema2go + +package livelog + +import "github.com/taskcluster/taskcluster-worker/runtime" + +type ( + // LiveLog config + config struct { + + // The executable (.exe file) to run the livelog service + LiveLogExe string `json:"liveLogExe"` + } +) + +var configSchema = func() runtime.CompositeSchema { + schema, err := runtime.NewCompositeSchema( + "config", + ` + { + "$schema": "http://json-schema.org/draft-04/schema#", + "additionalProperties": false, + "description": "LiveLog config", + "properties": { + "liveLogExe": { + "description": "The executable (.exe file) to run the livelog service", + "title": "LiveLogExecutable", + "type": "string" + } + }, + "required": [ + "liveLogExe" + ], + "title": "Config", + "type": "object" + } + `, + true, + func() interface{} { + return &config{} + }, + ) + if err != nil { + panic(err) + } + return schema +}() diff --git a/plugins/livelog/livelog.go b/plugins/livelog/livelog.go new file mode 100644 index 00000000..8f1a52a2 --- /dev/null +++ b/plugins/livelog/livelog.go @@ -0,0 +1,4 @@ +//go:generate go-composite-schema --required config config-schema.yml generated_configschema.go + +// livelog is an awesome package +package livelog diff --git a/plugins/plugin.go b/plugins/plugin.go index 80c8c7f0..b600b23b 100644 --- a/plugins/plugin.go +++ b/plugins/plugin.go @@ -160,6 +160,11 @@ func (PluginBase) PayloadSchema() (runtime.CompositeSchema, error) { return runtime.NewEmptyCompositeSchema(), nil } +// PluginBase requires no custom config +func (PluginBase) ConfigSchema() runtime.CompositeSchema { + return runtime.NewEmptyCompositeSchema() +} + // NewTaskPlugin returns nil ignoring the request to create a TaskPlugin for // the given task. func (PluginBase) NewTaskPlugin(TaskPluginOptions) (TaskPlugin, error) { diff --git a/plugins/success/success.go b/plugins/success/success.go index 9515218c..ab894429 100644 --- a/plugins/success/success.go +++ b/plugins/success/success.go @@ -16,8 +16,20 @@ import ( "github.com/taskcluster/taskcluster-worker/engines" "github.com/taskcluster/taskcluster-worker/plugins" "github.com/taskcluster/taskcluster-worker/plugins/extpoints" + "github.com/taskcluster/taskcluster-worker/runtime" ) +type pluginProvider struct { +} + +func (pluginProvider) NewPlugin(extpoints.PluginOptions) (plugins.Plugin, error) { + return plugin{}, nil +} + +func (pluginProvider) ConfigSchema() runtime.CompositeSchema { + return runtime.NewEmptyCompositeSchema() +} + type plugin struct { plugins.PluginBase } @@ -27,11 +39,7 @@ type taskPlugin struct { } func init() { - extpoints.PluginProviders.Register(func( - extpoints.PluginOptions, - ) (plugins.Plugin, error) { - return plugin{}, nil - }, "success") + extpoints.PluginProviders.Register(new(pluginProvider), "success") } func (plugin) NewTaskPlugin(plugins.TaskPluginOptions) (plugins.TaskPlugin, error) { diff --git a/runtime/compositeschema.go b/runtime/compositeschema.go index b5a5b59c..3bdbe843 100644 --- a/runtime/compositeschema.go +++ b/runtime/compositeschema.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" + "github.com/taskcluster/taskcluster-base-go/jsontest" "github.com/xeipuuv/gojsonschema" ) @@ -81,9 +82,11 @@ func MergeCompositeSchemas(schemas ...CompositeSchema) (CompositeSchema, error) schema.visit(func(entry *schemaEntry) { for _, s := range schemas[i:] { s.visit(func(e *schemaEntry) { - if entry.property == e.property && entry.schema != e.schema { - // TODO: We probably should make an error with a custom message - hasConflict = true + if entry.property == e.property { + if schemasMatch, _, _, _ := jsontest.JsonEqual([]byte(entry.schema), []byte(e.schema)); !schemasMatch { + // TODO: We probably should make an error with a custom message + hasConflict = true + } } }) } diff --git a/runtime/compositeschema_test.go b/runtime/compositeschema_test.go index d9626e76..203f427e 100644 --- a/runtime/compositeschema_test.go +++ b/runtime/compositeschema_test.go @@ -161,9 +161,9 @@ func TestMergeComposedSchemas(t *testing.T) { t.Fatalf("Error creating a merged composite scheme. %v", err) } - m := merged.(*composedSchema) - if len(m.entries) != 2 { - t.Fatalf("Merged schema should have 2 entries but had %d", len(m.entries)) + m := merged.(composedSchema) + if len(m) != 2 { + t.Fatalf("Merged schema should have 2 entries but had %d", len(m)) } } diff --git a/subtree_imports.go b/subtree_imports.go index c2627bbc..f79e3e2e 100644 --- a/subtree_imports.go +++ b/subtree_imports.go @@ -1,8 +1,11 @@ // generated by go-import-subtree -- DO NOT EDIT package main +import _ "github.com/taskcluster/taskcluster-worker/engines/docker" import _ "github.com/taskcluster/taskcluster-worker/engines/enginetest" import _ "github.com/taskcluster/taskcluster-worker/engines/extpoints" import _ "github.com/taskcluster/taskcluster-worker/engines/mock" +import _ "github.com/taskcluster/taskcluster-worker/engines/winnative" import _ "github.com/taskcluster/taskcluster-worker/plugins/extpoints" +import _ "github.com/taskcluster/taskcluster-worker/plugins/livelog" import _ "github.com/taskcluster/taskcluster-worker/plugins/success"