From cf284ed8f24f44c0914bd10d06bfd3b29164f16a Mon Sep 17 00:00:00 2001 From: Sunil Thaha Date: Wed, 14 Oct 2020 19:48:36 +1000 Subject: [PATCH] Integrate Hub commands into tkn This PR integrates hub commands into the Tekton CLI by making minimal changes to tkn. This is achieved by vendoring hub commands and adding those into the tkn root command. This approach allows us to provide the same UX `tkn hub ` with very little change to the tkn cli. Signed-off-by: Sunil Thaha --- README.md | 1 + docs/cmd/tkn.md | 1 + docs/cmd/tkn_hub.md | 27 + docs/cmd/tkn_hub_get.md | 34 + docs/cmd/tkn_hub_get_pipeline.md | 46 + docs/cmd/tkn_hub_get_task.md | 46 + docs/cmd/tkn_hub_search.md | 49 + docs/man/man1/tkn-hub-get-pipeline.1 | 73 + docs/man/man1/tkn-hub-get-task.1 | 73 + docs/man/man1/tkn-hub-get.1 | 44 + docs/man/man1/tkn-hub-search.1 | 85 + docs/man/man1/tkn-hub.1 | 34 + docs/man/man1/tkn.1 | 2 +- go.mod | 3 +- go.sum | 108 +- pkg/cmd/root.go | 3 + test/e2e-tests.sh | 9 + .../dimfeld/httptreemux/v5/.gitignore | 23 + .../dimfeld/httptreemux/v5/.travis.yml | 14 + .../github.com/dimfeld/httptreemux/v5/LICENSE | 21 + .../dimfeld/httptreemux/v5/README.md | 260 +++ .../dimfeld/httptreemux/v5/context.go | 194 +++ .../github.com/dimfeld/httptreemux/v5/go.mod | 3 + .../dimfeld/httptreemux/v5/group.go | 241 +++ .../dimfeld/httptreemux/v5/panichandler.go | 211 +++ .../github.com/dimfeld/httptreemux/v5/path.go | 127 ++ .../dimfeld/httptreemux/v5/router.go | 300 ++++ .../github.com/dimfeld/httptreemux/v5/tree.go | 340 ++++ .../dimfeld/httptreemux/v5/treemux_16.go | 86 + .../dimfeld/httptreemux/v5/treemux_17.go | 149 ++ .../dimfeld/httptreemux/v5/unescape_17.go | 9 + .../dimfeld/httptreemux/v5/unescape_18.go | 9 + .../github.com/gorilla/websocket/.gitignore | 25 + vendor/github.com/gorilla/websocket/AUTHORS | 9 + vendor/github.com/gorilla/websocket/LICENSE | 22 + vendor/github.com/gorilla/websocket/README.md | 64 + vendor/github.com/gorilla/websocket/client.go | 395 +++++ .../gorilla/websocket/client_clone.go | 16 + .../gorilla/websocket/client_clone_legacy.go | 38 + .../gorilla/websocket/compression.go | 148 ++ vendor/github.com/gorilla/websocket/conn.go | 1201 ++++++++++++++ .../gorilla/websocket/conn_write.go | 15 + .../gorilla/websocket/conn_write_legacy.go | 18 + vendor/github.com/gorilla/websocket/doc.go | 227 +++ vendor/github.com/gorilla/websocket/go.mod | 3 + vendor/github.com/gorilla/websocket/go.sum | 0 vendor/github.com/gorilla/websocket/join.go | 42 + vendor/github.com/gorilla/websocket/json.go | 60 + vendor/github.com/gorilla/websocket/mask.go | 54 + .../github.com/gorilla/websocket/mask_safe.go | 15 + .../github.com/gorilla/websocket/prepared.go | 102 ++ vendor/github.com/gorilla/websocket/proxy.go | 77 + vendor/github.com/gorilla/websocket/server.go | 363 +++++ vendor/github.com/gorilla/websocket/trace.go | 19 + .../github.com/gorilla/websocket/trace_17.go | 12 + vendor/github.com/gorilla/websocket/util.go | 283 ++++ .../gorilla/websocket/x_net_proxy.go | 473 ++++++ .../github.com/mitchellh/go-homedir/LICENSE | 21 + .../github.com/mitchellh/go-homedir/README.md | 14 + vendor/github.com/mitchellh/go-homedir/go.mod | 1 + .../mitchellh/go-homedir/homedir.go | 167 ++ vendor/github.com/tektoncd/hub/api/LICENSE | 201 +++ .../hub/api/gen/http/resource/client/cli.go | 221 +++ .../api/gen/http/resource/client/client.go | 225 +++ .../gen/http/resource/client/encode_decode.go | 956 +++++++++++ .../hub/api/gen/http/resource/client/paths.go | 47 + .../hub/api/gen/http/resource/client/types.go | 1413 +++++++++++++++++ .../tektoncd/hub/api/gen/resource/client.go | 110 ++ .../hub/api/gen/resource/endpoints.go | 147 ++ .../tektoncd/hub/api/gen/resource/service.go | 902 +++++++++++ .../hub/api/gen/resource/views/view.go | 617 +++++++ .../tektoncd/hub/api/pkg/cli/app/app.go | 55 + .../tektoncd/hub/api/pkg/cli/cmd/get/get.go | 123 ++ .../tektoncd/hub/api/pkg/cli/cmd/root.go | 53 + .../hub/api/pkg/cli/cmd/search/search.go | 168 ++ .../tektoncd/hub/api/pkg/cli/flag/validate.go | 63 + .../hub/api/pkg/cli/formatter/field.go | 54 + .../hub/api/pkg/cli/formatter/json.go | 30 + .../hub/api/pkg/cli/hub/get_resource.go | 107 ++ .../tektoncd/hub/api/pkg/cli/hub/hub.go | 98 ++ .../tektoncd/hub/api/pkg/cli/hub/search.go | 101 ++ .../tektoncd/hub/api/pkg/cli/printer/print.go | 66 + .../tektoncd/hub/api/pkg/git/fetch_spec.go | 42 + .../tektoncd/hub/api/pkg/git/git.go | 162 ++ .../tektoncd/hub/api/pkg/git/repo.go | 66 + .../tektoncd/hub/api/pkg/parser/catalog.go | 396 +++++ .../tektoncd/hub/api/pkg/parser/kind.go | 41 + .../tektoncd/hub/api/pkg/parser/parser.go | 46 + .../tektoncd/hub/api/pkg/parser/resource.go | 35 + .../tektoncd/hub/api/pkg/parser/result.go | 92 ++ vendor/goa.design/goa/v3/LICENSE | 21 + vendor/goa.design/goa/v3/http/client.go | 220 +++ vendor/goa.design/goa/v3/http/doc.go | 7 + vendor/goa.design/goa/v3/http/encoding.go | 305 ++++ vendor/goa.design/goa/v3/http/error.go | 70 + vendor/goa.design/goa/v3/http/mux.go | 92 ++ vendor/goa.design/goa/v3/http/server.go | 21 + vendor/goa.design/goa/v3/http/websocket.go | 27 + vendor/goa.design/goa/v3/pkg/doc.go | 19 + vendor/goa.design/goa/v3/pkg/endpoint.go | 24 + vendor/goa.design/goa/v3/pkg/error.go | 209 +++ vendor/goa.design/goa/v3/pkg/validation.go | 201 +++ vendor/goa.design/goa/v3/pkg/version.go | 45 + vendor/gotest.tools/v3/assert/assert.go | 17 +- vendor/modules.txt | 27 +- 105 files changed, 14439 insertions(+), 12 deletions(-) create mode 100644 docs/cmd/tkn_hub.md create mode 100644 docs/cmd/tkn_hub_get.md create mode 100644 docs/cmd/tkn_hub_get_pipeline.md create mode 100644 docs/cmd/tkn_hub_get_task.md create mode 100644 docs/cmd/tkn_hub_search.md create mode 100644 docs/man/man1/tkn-hub-get-pipeline.1 create mode 100644 docs/man/man1/tkn-hub-get-task.1 create mode 100644 docs/man/man1/tkn-hub-get.1 create mode 100644 docs/man/man1/tkn-hub-search.1 create mode 100644 docs/man/man1/tkn-hub.1 create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/.gitignore create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/.travis.yml create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/LICENSE create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/README.md create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/context.go create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/go.mod create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/group.go create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/panichandler.go create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/path.go create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/router.go create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/tree.go create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/treemux_16.go create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/treemux_17.go create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/unescape_17.go create mode 100644 vendor/github.com/dimfeld/httptreemux/v5/unescape_18.go create mode 100644 vendor/github.com/gorilla/websocket/.gitignore create mode 100644 vendor/github.com/gorilla/websocket/AUTHORS create mode 100644 vendor/github.com/gorilla/websocket/LICENSE create mode 100644 vendor/github.com/gorilla/websocket/README.md create mode 100644 vendor/github.com/gorilla/websocket/client.go create mode 100644 vendor/github.com/gorilla/websocket/client_clone.go create mode 100644 vendor/github.com/gorilla/websocket/client_clone_legacy.go create mode 100644 vendor/github.com/gorilla/websocket/compression.go create mode 100644 vendor/github.com/gorilla/websocket/conn.go create mode 100644 vendor/github.com/gorilla/websocket/conn_write.go create mode 100644 vendor/github.com/gorilla/websocket/conn_write_legacy.go create mode 100644 vendor/github.com/gorilla/websocket/doc.go create mode 100644 vendor/github.com/gorilla/websocket/go.mod create mode 100644 vendor/github.com/gorilla/websocket/go.sum create mode 100644 vendor/github.com/gorilla/websocket/join.go create mode 100644 vendor/github.com/gorilla/websocket/json.go create mode 100644 vendor/github.com/gorilla/websocket/mask.go create mode 100644 vendor/github.com/gorilla/websocket/mask_safe.go create mode 100644 vendor/github.com/gorilla/websocket/prepared.go create mode 100644 vendor/github.com/gorilla/websocket/proxy.go create mode 100644 vendor/github.com/gorilla/websocket/server.go create mode 100644 vendor/github.com/gorilla/websocket/trace.go create mode 100644 vendor/github.com/gorilla/websocket/trace_17.go create mode 100644 vendor/github.com/gorilla/websocket/util.go create mode 100644 vendor/github.com/gorilla/websocket/x_net_proxy.go create mode 100644 vendor/github.com/mitchellh/go-homedir/LICENSE create mode 100644 vendor/github.com/mitchellh/go-homedir/README.md create mode 100644 vendor/github.com/mitchellh/go-homedir/go.mod create mode 100644 vendor/github.com/mitchellh/go-homedir/homedir.go create mode 100644 vendor/github.com/tektoncd/hub/api/LICENSE create mode 100644 vendor/github.com/tektoncd/hub/api/gen/http/resource/client/cli.go create mode 100644 vendor/github.com/tektoncd/hub/api/gen/http/resource/client/client.go create mode 100644 vendor/github.com/tektoncd/hub/api/gen/http/resource/client/encode_decode.go create mode 100644 vendor/github.com/tektoncd/hub/api/gen/http/resource/client/paths.go create mode 100644 vendor/github.com/tektoncd/hub/api/gen/http/resource/client/types.go create mode 100644 vendor/github.com/tektoncd/hub/api/gen/resource/client.go create mode 100644 vendor/github.com/tektoncd/hub/api/gen/resource/endpoints.go create mode 100644 vendor/github.com/tektoncd/hub/api/gen/resource/service.go create mode 100644 vendor/github.com/tektoncd/hub/api/gen/resource/views/view.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/cli/app/app.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/get/get.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/root.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/search/search.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/cli/flag/validate.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/cli/formatter/field.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/cli/formatter/json.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/cli/hub/get_resource.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/cli/hub/hub.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/cli/hub/search.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/cli/printer/print.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/git/fetch_spec.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/git/git.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/git/repo.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/parser/catalog.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/parser/kind.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/parser/parser.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/parser/resource.go create mode 100644 vendor/github.com/tektoncd/hub/api/pkg/parser/result.go create mode 100644 vendor/goa.design/goa/v3/LICENSE create mode 100644 vendor/goa.design/goa/v3/http/client.go create mode 100644 vendor/goa.design/goa/v3/http/doc.go create mode 100644 vendor/goa.design/goa/v3/http/encoding.go create mode 100644 vendor/goa.design/goa/v3/http/error.go create mode 100644 vendor/goa.design/goa/v3/http/mux.go create mode 100644 vendor/goa.design/goa/v3/http/server.go create mode 100644 vendor/goa.design/goa/v3/http/websocket.go create mode 100644 vendor/goa.design/goa/v3/pkg/doc.go create mode 100644 vendor/goa.design/goa/v3/pkg/endpoint.go create mode 100644 vendor/goa.design/goa/v3/pkg/error.go create mode 100644 vendor/goa.design/goa/v3/pkg/validation.go create mode 100644 vendor/goa.design/goa/v3/pkg/version.go diff --git a/README.md b/README.md index d6171746a5..96d27bca69 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ The following commands help you understand and effectively use the Tekton CLI: * [`tkn completion:`](docs/cmd/tkn_completion.md) Outputs a BASH or ZSH completion script for `tkn` to allow command completion with Tab. * [`tkn condition:`](docs/cmd/tkn_condition.md) Parent command of the Condition command group. * [`tkn eventlistener:`](docs/cmd/tkn_eventlistener.md) Parent command of the Eventlistener command group. + * [`tkn hub:`](docs/cmd/tkn_hub.md) Search and install Tekton Resources from [Hub](https://hub-preview.tekton.dev) * [`tkn pipeline:`](docs/cmd/tkn_pipeline.md) Parent command of the Pipeline command group. * [`tkn pipelinerun:`](docs/cmd/tkn_pipelinerun.md) Parent command of the Pipelinerun command group. * [`tkn resource:`](docs/cmd/tkn_resource.md) Parent command of the Resource command group. diff --git a/docs/cmd/tkn.md b/docs/cmd/tkn.md index 859c41ee82..46343058ec 100644 --- a/docs/cmd/tkn.md +++ b/docs/cmd/tkn.md @@ -25,6 +25,7 @@ CLI for tekton pipelines * [tkn completion](tkn_completion.md) - Prints shell completion scripts * [tkn condition](tkn_condition.md) - Manage Conditions * [tkn eventlistener](tkn_eventlistener.md) - Manage EventListeners +* [tkn hub](tkn_hub.md) - Interact with tekton hub * [tkn pipeline](tkn_pipeline.md) - Manage pipelines * [tkn pipelinerun](tkn_pipelinerun.md) - Manage PipelineRuns * [tkn resource](tkn_resource.md) - Manage pipeline resources diff --git a/docs/cmd/tkn_hub.md b/docs/cmd/tkn_hub.md new file mode 100644 index 0000000000..bb39a8ba19 --- /dev/null +++ b/docs/cmd/tkn_hub.md @@ -0,0 +1,27 @@ +## tkn hub + +Interact with tekton hub + +### Usage + +``` +tkn hub +``` + +### Synopsis + +Interact with tekton hub + +### Options + +``` + --api-server string Hub API Server URL (default "https://api.hub.tekton.dev") + -h, --help help for hub +``` + +### SEE ALSO + +* [tkn](tkn.md) - CLI for tekton pipelines +* [tkn hub get](tkn_hub_get.md) - Get resource manifest by its name, kind, catalog, and version +* [tkn hub search](tkn_hub_search.md) - Search resource by a combination of name, kind, and tags + diff --git a/docs/cmd/tkn_hub_get.md b/docs/cmd/tkn_hub_get.md new file mode 100644 index 0000000000..ffe75a2f1e --- /dev/null +++ b/docs/cmd/tkn_hub_get.md @@ -0,0 +1,34 @@ +## tkn hub get + +Get resource manifest by its name, kind, catalog, and version + +### Usage + +``` +tkn hub get +``` + +### Synopsis + +Get resource manifest by its name, kind, catalog, and version + +### Options + +``` + --from string Name of Catalog to which resource belongs to. (default "tekton") + -h, --help help for get + --version string Version of Resource +``` + +### Options inherited from parent commands + +``` + --api-server string Hub API Server URL (default "https://api.hub.tekton.dev") +``` + +### SEE ALSO + +* [tkn hub](tkn_hub.md) - Interact with tekton hub +* [tkn hub get pipeline](tkn_hub_get_pipeline.md) - Get pipeline by name, catalog and version +* [tkn hub get task](tkn_hub_get_task.md) - Get task by name, catalog and version + diff --git a/docs/cmd/tkn_hub_get_pipeline.md b/docs/cmd/tkn_hub_get_pipeline.md new file mode 100644 index 0000000000..85dfccf4c4 --- /dev/null +++ b/docs/cmd/tkn_hub_get_pipeline.md @@ -0,0 +1,46 @@ +## tkn hub get pipeline + +Get pipeline by name, catalog and version + +### Usage + +``` +tkn hub get pipeline +``` + +### Synopsis + +Get pipeline by name, catalog and version + +### Examples + + +Get a pipeline of name 'foo': + + tkn hub get pipeline foo + +or + +Get a pipeline of name 'foo' of version '0.3': + + tkn hub get pipeline foo --version 0.3 + + +### Options + +``` + -h, --help help for pipeline +``` + +### Options inherited from parent commands + +``` + --api-server string Hub API Server URL (default "https://api.hub.tekton.dev") + --from string Name of Catalog to which resource belongs to. (default "tekton") + --version string Version of Resource +``` + +### SEE ALSO + +* [tkn hub get](tkn_hub_get.md) - Get resource manifest by its name, kind, catalog, and version + diff --git a/docs/cmd/tkn_hub_get_task.md b/docs/cmd/tkn_hub_get_task.md new file mode 100644 index 0000000000..70ce4d587e --- /dev/null +++ b/docs/cmd/tkn_hub_get_task.md @@ -0,0 +1,46 @@ +## tkn hub get task + +Get task by name, catalog and version + +### Usage + +``` +tkn hub get task +``` + +### Synopsis + +Get task by name, catalog and version + +### Examples + + +Get a task of name 'foo': + + tkn hub get task foo + +or + +Get a task of name 'foo' of version '0.3': + + tkn hub get task foo --version 0.3 + + +### Options + +``` + -h, --help help for task +``` + +### Options inherited from parent commands + +``` + --api-server string Hub API Server URL (default "https://api.hub.tekton.dev") + --from string Name of Catalog to which resource belongs to. (default "tekton") + --version string Version of Resource +``` + +### SEE ALSO + +* [tkn hub get](tkn_hub_get.md) - Get resource manifest by its name, kind, catalog, and version + diff --git a/docs/cmd/tkn_hub_search.md b/docs/cmd/tkn_hub_search.md new file mode 100644 index 0000000000..86c61b04eb --- /dev/null +++ b/docs/cmd/tkn_hub_search.md @@ -0,0 +1,49 @@ +## tkn hub search + +Search resource by a combination of name, kind, and tags + +### Usage + +``` +tkn hub search +``` + +### Synopsis + +Search resource by a combination of name, kind, and tags + +### Examples + + +Search a resource of name 'foo': + + tkn hub search foo + +or + +Search resources using tag 'cli': + + tkn hub search --tags cli + + +### Options + +``` + -h, --help help for search + --kinds stringArray Accepts a comma separated list of kinds + -l, --limit uint Max number of resources to fetch + --match string Accept type of search. 'exact' or 'contains'. (default "contains") + -o, --output string Accepts output format: [table, json] (default "table") + --tags stringArray Accepts a comma separated list of tags +``` + +### Options inherited from parent commands + +``` + --api-server string Hub API Server URL (default "https://api.hub.tekton.dev") +``` + +### SEE ALSO + +* [tkn hub](tkn_hub.md) - Interact with tekton hub + diff --git a/docs/man/man1/tkn-hub-get-pipeline.1 b/docs/man/man1/tkn-hub-get-pipeline.1 new file mode 100644 index 0000000000..0856db8bd3 --- /dev/null +++ b/docs/man/man1/tkn-hub-get-pipeline.1 @@ -0,0 +1,73 @@ +.TH "TKN\-HUB\-GET\-PIPELINE" "1" "" "Auto generated by spf13/cobra" "" +.nh +.ad l + + +.SH NAME +.PP +tkn\-hub\-get\-pipeline \- Get pipeline by name, catalog and version + + +.SH SYNOPSIS +.PP +\fBtkn hub get pipeline\fP + + +.SH DESCRIPTION +.PP +Get pipeline by name, catalog and version + + +.SH OPTIONS +.PP +\fB\-h\fP, \fB\-\-help\fP[=false] + help for pipeline + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +.PP +\fB\-\-api\-server\fP=" +\[la]https://api.hub.tekton.dev"\[ra] + Hub API Server URL + +.PP +\fB\-\-from\fP="tekton" + Name of Catalog to which resource belongs to. + +.PP +\fB\-\-version\fP="" + Version of Resource + + +.SH EXAMPLE +.PP +Get a pipeline of name 'foo': + +.PP +.RS + +.nf +tkn hub get pipeline foo + +.fi +.RE + +.PP +or + +.PP +Get a pipeline of name 'foo' of version '0.3': + +.PP +.RS + +.nf +tkn hub get pipeline foo \-\-version 0.3 + +.fi +.RE + + +.SH SEE ALSO +.PP +\fBtkn\-hub\-get(1)\fP diff --git a/docs/man/man1/tkn-hub-get-task.1 b/docs/man/man1/tkn-hub-get-task.1 new file mode 100644 index 0000000000..fe5727f061 --- /dev/null +++ b/docs/man/man1/tkn-hub-get-task.1 @@ -0,0 +1,73 @@ +.TH "TKN\-HUB\-GET\-TASK" "1" "" "Auto generated by spf13/cobra" "" +.nh +.ad l + + +.SH NAME +.PP +tkn\-hub\-get\-task \- Get task by name, catalog and version + + +.SH SYNOPSIS +.PP +\fBtkn hub get task\fP + + +.SH DESCRIPTION +.PP +Get task by name, catalog and version + + +.SH OPTIONS +.PP +\fB\-h\fP, \fB\-\-help\fP[=false] + help for task + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +.PP +\fB\-\-api\-server\fP=" +\[la]https://api.hub.tekton.dev"\[ra] + Hub API Server URL + +.PP +\fB\-\-from\fP="tekton" + Name of Catalog to which resource belongs to. + +.PP +\fB\-\-version\fP="" + Version of Resource + + +.SH EXAMPLE +.PP +Get a task of name 'foo': + +.PP +.RS + +.nf +tkn hub get task foo + +.fi +.RE + +.PP +or + +.PP +Get a task of name 'foo' of version '0.3': + +.PP +.RS + +.nf +tkn hub get task foo \-\-version 0.3 + +.fi +.RE + + +.SH SEE ALSO +.PP +\fBtkn\-hub\-get(1)\fP diff --git a/docs/man/man1/tkn-hub-get.1 b/docs/man/man1/tkn-hub-get.1 new file mode 100644 index 0000000000..d1fcf681dc --- /dev/null +++ b/docs/man/man1/tkn-hub-get.1 @@ -0,0 +1,44 @@ +.TH "TKN\-HUB\-GET" "1" "" "Auto generated by spf13/cobra" "" +.nh +.ad l + + +.SH NAME +.PP +tkn\-hub\-get \- Get resource manifest by its name, kind, catalog, and version + + +.SH SYNOPSIS +.PP +\fBtkn hub get\fP + + +.SH DESCRIPTION +.PP +Get resource manifest by its name, kind, catalog, and version + + +.SH OPTIONS +.PP +\fB\-\-from\fP="tekton" + Name of Catalog to which resource belongs to. + +.PP +\fB\-h\fP, \fB\-\-help\fP[=false] + help for get + +.PP +\fB\-\-version\fP="" + Version of Resource + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +.PP +\fB\-\-api\-server\fP=" +\[la]https://api.hub.tekton.dev"\[ra] + Hub API Server URL + + +.SH SEE ALSO +.PP +\fBtkn\-hub(1)\fP, \fBtkn\-hub\-get\-pipeline(1)\fP, \fBtkn\-hub\-get\-task(1)\fP diff --git a/docs/man/man1/tkn-hub-search.1 b/docs/man/man1/tkn-hub-search.1 new file mode 100644 index 0000000000..1168f90311 --- /dev/null +++ b/docs/man/man1/tkn-hub-search.1 @@ -0,0 +1,85 @@ +.TH "TKN\-HUB\-SEARCH" "1" "" "Auto generated by spf13/cobra" "" +.nh +.ad l + + +.SH NAME +.PP +tkn\-hub\-search \- Search resource by a combination of name, kind, and tags + + +.SH SYNOPSIS +.PP +\fBtkn hub search\fP + + +.SH DESCRIPTION +.PP +Search resource by a combination of name, kind, and tags + + +.SH OPTIONS +.PP +\fB\-h\fP, \fB\-\-help\fP[=false] + help for search + +.PP +\fB\-\-kinds\fP=[] + Accepts a comma separated list of kinds + +.PP +\fB\-l\fP, \fB\-\-limit\fP=0 + Max number of resources to fetch + +.PP +\fB\-\-match\fP="contains" + Accept type of search. 'exact' or 'contains'. + +.PP +\fB\-o\fP, \fB\-\-output\fP="table" + Accepts output format: [table, json] + +.PP +\fB\-\-tags\fP=[] + Accepts a comma separated list of tags + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +.PP +\fB\-\-api\-server\fP=" +\[la]https://api.hub.tekton.dev"\[ra] + Hub API Server URL + + +.SH EXAMPLE +.PP +Search a resource of name 'foo': + +.PP +.RS + +.nf +tkn hub search foo + +.fi +.RE + +.PP +or + +.PP +Search resources using tag 'cli': + +.PP +.RS + +.nf +tkn hub search \-\-tags cli + +.fi +.RE + + +.SH SEE ALSO +.PP +\fBtkn\-hub(1)\fP diff --git a/docs/man/man1/tkn-hub.1 b/docs/man/man1/tkn-hub.1 new file mode 100644 index 0000000000..3317e7b1a8 --- /dev/null +++ b/docs/man/man1/tkn-hub.1 @@ -0,0 +1,34 @@ +.TH "TKN\-HUB" "1" "" "Auto generated by spf13/cobra" "" +.nh +.ad l + + +.SH NAME +.PP +tkn\-hub \- Interact with tekton hub + + +.SH SYNOPSIS +.PP +\fBtkn hub\fP + + +.SH DESCRIPTION +.PP +Interact with tekton hub + + +.SH OPTIONS +.PP +\fB\-\-api\-server\fP=" +\[la]https://api.hub.tekton.dev"\[ra] + Hub API Server URL + +.PP +\fB\-h\fP, \fB\-\-help\fP[=false] + help for hub + + +.SH SEE ALSO +.PP +\fBtkn(1)\fP, \fBtkn\-hub\-get(1)\fP, \fBtkn\-hub\-search(1)\fP diff --git a/docs/man/man1/tkn.1 b/docs/man/man1/tkn.1 index a1f15eb659..265cd462a7 100644 --- a/docs/man/man1/tkn.1 +++ b/docs/man/man1/tkn.1 @@ -26,4 +26,4 @@ CLI for tekton pipelines .SH SEE ALSO .PP -\fBtkn\-clustertask(1)\fP, \fBtkn\-clustertriggerbinding(1)\fP, \fBtkn\-completion(1)\fP, \fBtkn\-condition(1)\fP, \fBtkn\-eventlistener(1)\fP, \fBtkn\-pipeline(1)\fP, \fBtkn\-pipelinerun(1)\fP, \fBtkn\-resource(1)\fP, \fBtkn\-task(1)\fP, \fBtkn\-taskrun(1)\fP, \fBtkn\-triggerbinding(1)\fP, \fBtkn\-triggertemplate(1)\fP, \fBtkn\-version(1)\fP +\fBtkn\-clustertask(1)\fP, \fBtkn\-clustertriggerbinding(1)\fP, \fBtkn\-completion(1)\fP, \fBtkn\-condition(1)\fP, \fBtkn\-eventlistener(1)\fP, \fBtkn\-hub(1)\fP, \fBtkn\-pipeline(1)\fP, \fBtkn\-pipelinerun(1)\fP, \fBtkn\-resource(1)\fP, \fBtkn\-task(1)\fP, \fBtkn\-taskrun(1)\fP, \fBtkn\-triggerbinding(1)\fP, \fBtkn\-triggertemplate(1)\fP, \fBtkn\-version(1)\fP diff --git a/go.mod b/go.mod index 0d7188d16e..ae33921714 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 + github.com/tektoncd/hub/api v0.0.0-20201021041518-f3b44fba7d18 github.com/tektoncd/pipeline v0.17.1-0.20201007165454-9611f3e4509e github.com/tektoncd/plumbing v0.0.0-20200430135134-e53521e1d887 github.com/tektoncd/triggers v0.8.2-0.20201007153255-cb1879311818 @@ -27,7 +28,7 @@ require ( golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 gopkg.in/yaml.v2 v2.3.0 gotest.tools v2.2.0+incompatible - gotest.tools/v3 v3.0.1 + gotest.tools/v3 v3.0.2 k8s.io/api v0.18.9 k8s.io/apimachinery v0.19.0 k8s.io/cli-runtime v0.17.6 diff --git a/go.sum b/go.sum index 8c9019732a..746b27290c 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,7 @@ bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxo cloud.google.com/go v0.25.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= @@ -328,8 +329,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As= +github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= github.com/denisenkom/go-mssqldb v0.0.0-20190111225525-2fea367d496d/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -338,6 +341,10 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbrt36utu8jEmeU05dPC9AB5tsLYVVi+ZHfyuwI= +github.com/dimfeld/httptreemux/v5 v5.0.2/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw= +github.com/dimfeld/httptreemux/v5 v5.2.2 h1:8JAUcuNrLbL5uwmvQ4lZVCjuQ/Ioojc+7VGt89aMElU= +github.com/dimfeld/httptreemux/v5 v5.2.2/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw= github.com/djherbis/atime v1.0.0/go.mod h1:5W+KBIuTwVGcqjIfaTwt+KSYX1o6uep8dtevevQP/f8= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/cli v0.0.0-20190925022749-754388324470/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -410,6 +417,7 @@ github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdk github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell v1.2.0 h1:ikixzsxc8K8o3V2/CEmyoEW8mJZaNYQQ3NP3VIQdUe4= github.com/gdamore/tcell v1.2.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= +github.com/getkin/kin-openapi v0.18.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -442,10 +450,12 @@ github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpR github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= @@ -466,6 +476,7 @@ github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q= github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= @@ -483,6 +494,7 @@ github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pL github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= @@ -501,6 +513,7 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-testfixtures/testfixtures/v3 v3.2.0/go.mod h1:RZctY24ixituGC73XlAV1gkCwYMVwiSwPm26MNlQIhE= github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= @@ -517,14 +530,38 @@ github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2 github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= github.com/gobuffalo/envy v1.9.0/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -637,6 +674,7 @@ github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4/go.mod h1:Pw1H1OjSNHiqeuxAduB1BKYXIwFtsyrY47nEqSgEiCM= github.com/google/licenseclassifier v0.0.0-20190926221455-842c0d70d702/go.mod h1:qsqn2hxC+vURpyBRygGUuinTO42MFRLcsmQ/P8v94+M= github.com/google/licenseclassifier v0.0.0-20200402202327-879cb1424de0/go.mod h1:qsqn2hxC+vURpyBRygGUuinTO42MFRLcsmQ/P8v94+M= github.com/google/licenseclassifier v0.0.0-20200708223521-3d09a0ea2f39/go.mod h1:qsqn2hxC+vURpyBRygGUuinTO42MFRLcsmQ/P8v94+M= @@ -696,6 +734,8 @@ github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= @@ -722,6 +762,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4G github.com/grpc-ecosystem/grpc-gateway v1.14.8 h1:hXClj+iFpmLM8i3lkO6i4Psli4P2qObQuQReiII26U8= github.com/grpc-ecosystem/grpc-gateway v1.14.8/go.mod h1:NZE8t6vs6TnwLL/ITkaK8W3ecMLGAbh2jXTclvpiwYo= github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hako/durafmt v0.0.0-20191009132224-3f39dc1ed9f4 h1:60gBOooTSmNtrqNaRvrDbi8VAne0REaek2agjnITKSw= github.com/hako/durafmt v0.0.0-20191009132224-3f39dc1ed9f4/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -764,6 +806,8 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ikawaha/goahttpcheck v1.3.1/go.mod h1:RFH8+oq3GOW2yiRwI5tcx3FrVmONlT4DMmu80dsoBRE= +github.com/ikawaha/httpcheck v1.2.0/go.mod h1:eYUWSdmssaYCdUzcOUe8NMF0vgr4MKq7Iqbb3eXtpxI= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -784,9 +828,12 @@ github.com/jenkins-x/go-scm v1.5.117/go.mod h1:PCT338UhP/pQ0IeEeMEf/hoLTYKcH7qjG github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s= github.com/jinzhu/gorm v0.0.0-20170316141641-572d0a0ab1eb/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= +github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v0.0.0-20190603042836-f5c5f50e6090/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v0.0.0-20181116074157-8ec929ed50c3/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= @@ -818,6 +865,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kelseyhightower/envconfig v1.3.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= @@ -830,6 +879,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= @@ -852,6 +902,7 @@ github.com/ktr0731/go-fuzzyfinder v0.2.0/go.mod h1:Ol2Z6Rc1tu/uUSlD6b67wnhB4nGQt github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac h1:+2b6iGRJe3hvV/yVXrd41yVEjxuFHxasJqDhkIjS4gk= @@ -872,8 +923,14 @@ github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.1-0.20191009090205-6c0755d89d1e h1:jcoUdG1TzY/M/eM5BLFLP8DJeMximx5NQYSlLL9YeWc= github.com/mailru/easyjson v0.7.1-0.20191009090205-6c0755d89d1e/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8= +github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d/go.mod h1:WZy8Q5coAB1zhY9AOBJP0O6J4BuDfbupUDavKY+I3+s= +github.com/manveru/gobdd v0.0.0-20131210092515-f1a17fdd710b/go.mod h1:Bj8LjjP0ReT1eKt5QlKjwgi5AFm5mI6O1A2G4ChI0Ag= github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0= @@ -901,7 +958,9 @@ github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLt github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v0.0.0-20160514122348-38ee283dabf1/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mattn/go-zglob v0.0.2/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= @@ -937,6 +996,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= @@ -958,6 +1018,7 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.0/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= @@ -1015,6 +1076,7 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.3.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -1098,9 +1160,12 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.17.2/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I= github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -1132,6 +1197,7 @@ github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvH github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.1.1/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= @@ -1183,10 +1249,15 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= +github.com/tektoncd/hub/api v0.0.0-20201019045143-d9a32160df56 h1:W0UJpnGH1O2aPZzgbxEOwSO6+mDUA0RpgipDFMy+IwA= +github.com/tektoncd/hub/api v0.0.0-20201019045143-d9a32160df56/go.mod h1:wwlAvyj5lS0efH9FjduQFy/Po2sDtLG+46ok8YOBBtM= +github.com/tektoncd/hub/api v0.0.0-20201021041518-f3b44fba7d18 h1:cbm1BWN2w+FERrrhynZEANibQyPhDty4nMr1CG0q13I= +github.com/tektoncd/hub/api v0.0.0-20201021041518-f3b44fba7d18/go.mod h1:wwlAvyj5lS0efH9FjduQFy/Po2sDtLG+46ok8YOBBtM= github.com/tektoncd/pipeline v0.8.0/go.mod h1:IZzJdiX9EqEMuUcgdnElozdYYRh0/ZRC+NKMLj1K3Yw= github.com/tektoncd/pipeline v0.10.1/go.mod h1:D2X0exT46zYx95BU7ByM8+erpjoN7thmUBvlKThOszU= github.com/tektoncd/pipeline v0.11.0/go.mod h1:hlkH32S92+/UODROH0dmxzyuMxfRFp/Nc3e29MewLn8= github.com/tektoncd/pipeline v0.13.1-0.20200625065359-44f22a067b75/go.mod h1:R5AlT46x/F8n/pFJFjZ1U1q71GWtVXgG7RZkkoRL554= +github.com/tektoncd/pipeline v0.15.2/go.mod h1:SaAxUpYbcFik2hKGOTX7NnvOa1VXTzXRGNZsY2TSwTs= github.com/tektoncd/pipeline v0.17.1-0.20201006183654-1710b688c023/go.mod h1:FoQiX9l6e/B3wGGAR4zgMHU849OYa6as3HG4xhRaFiE= github.com/tektoncd/pipeline v0.17.1-0.20201007165454-9611f3e4509e h1:cBER5+Wl0YtCwziuOTUgbE8Ec76Y2cRbs7wQYR+awvY= github.com/tektoncd/pipeline v0.17.1-0.20201007165454-9611f3e4509e/go.mod h1:FoQiX9l6e/B3wGGAR4zgMHU849OYa6as3HG4xhRaFiE= @@ -1245,6 +1316,7 @@ github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfD github.com/xanzy/go-gitlab v0.32.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= @@ -1262,6 +1334,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea/go.mod h1:eNr558nEUjP8acGw8FFjTeWvSgU1stO7FAO6eknhHe4= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.etcd.io/bbolt v1.3.1-etcd.7/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1270,6 +1344,7 @@ go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mI go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.17.0/go.mod h1:mp1VrMQxhlqqDpKvH4UcQUa4YwlzNmymAjPrDdfxNpI= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -1306,6 +1381,11 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +goa.design/goa/v3 v3.0.9/go.mod h1:oJR8VOFa4HV7wCQv4XhPtvyknz0B3VFFRUWDdEmI0FI= +goa.design/goa/v3 v3.1.3/go.mod h1:GEog3KvHosQPKrrZSlpXDSnm7PpmzZEiy3mLxI/FtXM= +goa.design/goa/v3 v3.2.2 h1:DDFof4YcjgRPJsl5T/iayigCX/nO857hEcVXdwvsSJM= +goa.design/goa/v3 v3.2.2/go.mod h1:3SDdN/ynQsyXW4Zl6A5YA2yXi9KyqmTH577z5P4bnAs= +goa.design/plugins/v3 v3.1.3/go.mod h1:j4gOWwZpEBsNmONKitV/tBlxG6Yu7unFCU1HJmqjiC8= gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180608092829-8ac0e0d97ce4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1313,6 +1393,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1320,6 +1401,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1417,6 +1499,7 @@ golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1451,6 +1534,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= @@ -1476,11 +1560,13 @@ golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1504,6 +1590,7 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191119060738-e882bf8e40c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1533,6 +1620,7 @@ golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200802091954-4b90ce9b60b3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f h1:Fqb3ao1hUmOR3GkUOg/Y+BadLwykBIzs5q8Ez2SbHyc= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1570,11 +1658,15 @@ golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -1586,6 +1678,7 @@ golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDq golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190807223507-b346f7fd45de/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1594,6 +1687,7 @@ golang.org/x/tools v0.0.0-20191010171213-8abd42400456/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191030232956-1e24073be82c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112005509-a3f652f18032/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1622,6 +1716,7 @@ golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200303214625-2b0b585e22fe/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200313205530-4303120df7d8/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= @@ -1642,10 +1737,12 @@ golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200701000337-a32c0cb1d5b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200709181711-e327e1019dfe/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200725200936-102e7d357031/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200811215021-48a8ffc5b207/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200827163409-021d7c6f1ec3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -1726,6 +1823,7 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= @@ -1820,6 +1918,9 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/gormigrate.v1 v1.6.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw= +gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0= +gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1858,8 +1959,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.1 h1:xT3Ou4AZrSCcl+gadYdfJsl87tvanhptiJ71SctTVDE= -gotest.tools/v3 v3.0.1/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= helm.sh/helm/v3 v3.1.1/go.mod h1:WYsFJuMASa/4XUqLyv54s0U/f3mlAaRErGmyy4z921g= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1977,6 +2078,7 @@ knative.dev/pkg v0.0.0-20200428194351-90fc61bae7f7/go.mod h1:o+e8OVEJKIuvXPsGVPI knative.dev/pkg v0.0.0-20200505191044-3da93ebb24c2/go.mod h1:Q6sL35DdGs8hIQZKdaCXJGgY8f90BmNBKSb8z6d/BTM= knative.dev/pkg v0.0.0-20200515002500-16d7b963416f/go.mod h1:tMOHGbxtRz8zYFGEGpV/bpoTEM1o89MwYFC4YJXl3GY= knative.dev/pkg v0.0.0-20200528142800-1c6815d7e4c9/go.mod h1:QgNZTxnwpB/oSpNcfnLVlw+WpEwwyKAvJlvR3hgeltA= +knative.dev/pkg v0.0.0-20200702222342-ea4d6e985ba0/go.mod h1:7T15JzvjKXWnvIKcohz4brrsVq8jvwAcJwWY9xigAc0= knative.dev/pkg v0.0.0-20200711004937-22502028e31a/go.mod h1:AqAJV6rYi8IGikDjJ/9ZQd9qKdkXVlesVnVjwx62YB8= knative.dev/pkg v0.0.0-20200922164940-4bf40ad82aab h1:4Q+sGqdN4ecLPZnIJcLDdQArEE9oOtRYiU2J6NPCCjU= knative.dev/pkg v0.0.0-20200922164940-4bf40ad82aab/go.mod h1:MCyt5KqLaai6ENUitOgRAQsu7Gw7cAIIiXQ2IS+O0NI= @@ -1984,6 +2086,7 @@ knative.dev/test-infra v0.0.0-20200407185800-1b88cb3b45a5/go.mod h1:xcdUkMJrLlBs knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55/go.mod h1:WqF1Azka+FxPZ20keR2zCNtiQA1MP9ZB4BH4HuI+SIU= knative.dev/test-infra v0.0.0-20200513011557-d03429a76034/go.mod h1:aMif0KXL4g19YCYwsy4Ocjjz5xgPlseYV+B95Oo4JGE= knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9/go.mod h1:A5b2OAXTOeHT3hHhVQm3dmtbuWvIDP7qzgtqxA3/2pE= +knative.dev/test-infra v0.0.0-20200630141629-15f40fe97047/go.mod h1:30tMsI1VXrG2m4ut7CFZbLg1VbcRsslPfGU+GWILm6E= knative.dev/test-infra v0.0.0-20200707183444-aed09e56ddc7/go.mod h1:RjYAhXnZqeHw9+B0zsbqSPlae0lCvjekO/nw5ZMpLCs= knative.dev/test-infra v0.0.0-20200921012245-37f1a12adbd3/go.mod h1:Pmg2c7Z7q7BGFUV/GOpU5BlrD3ePJft4MPqx8AYBplc= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= @@ -2004,6 +2107,7 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= sigs.k8s.io/boskos v0.0.0-20200526191642-45fc818e2d00/go.mod h1:L1ubP7d1CCMSQSjKiZv6dGbh7b4kfoG+dFPj8cfYDnI= +sigs.k8s.io/boskos v0.0.0-20200530174753-71e795271860/go.mod h1:L1ubP7d1CCMSQSjKiZv6dGbh7b4kfoG+dFPj8cfYDnI= sigs.k8s.io/boskos v0.0.0-20200617235605-f289ba6555ba/go.mod h1:ZO5RV+VxJS9mb6DvZ1yAjywoyq/wQ8b0vDoZxcIA5kE= sigs.k8s.io/boskos v0.0.0-20200729174948-794df80db9c9/go.mod h1:ZO5RV+VxJS9mb6DvZ1yAjywoyq/wQ8b0vDoZxcIA5kE= sigs.k8s.io/controller-runtime v0.3.0/go.mod h1:Cw6PkEg0Sa7dAYovGT4R0tRkGhHXpYijwNxYhAnAZZk= diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 7d2f8f12ce..a48879aecd 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -34,6 +34,8 @@ import ( "github.com/tektoncd/cli/pkg/cmd/triggertemplate" "github.com/tektoncd/cli/pkg/cmd/version" "github.com/tektoncd/cli/pkg/suggestion" + hubApp "github.com/tektoncd/hub/api/pkg/cli/app" + hub "github.com/tektoncd/hub/api/pkg/cli/cmd" ) const usageTemplate = `Usage:{{if .Runnable}} @@ -93,6 +95,7 @@ func Root(p cli.Params) *cobra.Command { triggerbinding.Command(p), triggertemplate.Command(p), version.Command(p), + hub.Root(hubApp.New()), ) visitCommands(cmd, reconfigureCmdWithSubcmd) diff --git a/test/e2e-tests.sh b/test/e2e-tests.sh index 79181c1d96..6c38058086 100755 --- a/test/e2e-tests.sh +++ b/test/e2e-tests.sh @@ -147,6 +147,15 @@ must_fail "describe deleted resource" tkn resource describe skaffold-git must_fail "show logs deleted taskrun" tkn taskrun logs test-template-volume must_fail "describe deleted eventlistener" tkn eventlistener describe github-listener-interceptor +# Hub tests +echo "Hub" +echo "....................." +run_test "hub command exists" tkn hub --help +run_test "hub search exists" tkn hub search --help +run_test "hub get exists" tkn hub get --help +must_fail "hub search cli" tkn hub --api-server=api.nonexistent.server search cli +must_fail "hub get task cli" tkn hub --api-server=api.nonexistent.server get task cli + # Make sure that eveything is cleaned up in the current namespace. for res in pipelineresources tasks pipelines taskruns pipelineruns; do kubectl delete --ignore-not-found=true ${res}.tekton.dev --all diff --git a/vendor/github.com/dimfeld/httptreemux/v5/.gitignore b/vendor/github.com/dimfeld/httptreemux/v5/.gitignore new file mode 100644 index 0000000000..836562412f --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/github.com/dimfeld/httptreemux/v5/.travis.yml b/vendor/github.com/dimfeld/httptreemux/v5/.travis.yml new file mode 100644 index 0000000000..9267a02e76 --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/.travis.yml @@ -0,0 +1,14 @@ +language: go + +gobuild_args: "-v -race" +go: + - 1.5 + - 1.6 + - 1.7 + - 1.8 + - 1.9 + - tip + +matrix: + allow_failures: + - go: tip diff --git a/vendor/github.com/dimfeld/httptreemux/v5/LICENSE b/vendor/github.com/dimfeld/httptreemux/v5/LICENSE new file mode 100644 index 0000000000..32c75c9b1e --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014,2015 Daniel Imfeld + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/dimfeld/httptreemux/v5/README.md b/vendor/github.com/dimfeld/httptreemux/v5/README.md new file mode 100644 index 0000000000..0d82f1ded7 --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/README.md @@ -0,0 +1,260 @@ +httptreemux [![Build Status](https://travis-ci.org/dimfeld/httptreemux.png?branch=master)](https://travis-ci.org/dimfeld/httptreemux) [![GoDoc](https://godoc.org/github.com/dimfeld/httptreemux?status.svg)](https://godoc.org/github.com/dimfeld/httptreemux) +=========== + +High-speed, flexible, tree-based HTTP router for Go. + +This is inspired by [Julien Schmidt's httprouter](https://www.github.com/julienschmidt/httprouter), in that it uses a patricia tree, but the implementation is rather different. Specifically, the routing rules are relaxed so that a single path segment may be a wildcard in one route and a static token in another. This gives a nice combination of high performance with a lot of convenience in designing the routing patterns. In [benchmarks](https://github.com/julienschmidt/go-http-routing-benchmark), httptreemux is close to, but slightly slower than, httprouter. + +Release notes may be found using the [Github releases tab](https://github.com/dimfeld/httptreemux/releases). Version numbers are compatible with the [Semantic Versioning 2.0.0](http://semver.org/) convention, and a new release is made after every change to the code. + +## Installing with Go Modules + +When using Go Modules, import this repository with `import "github.com/dimfeld/httptreemux/v5"` to ensure that you get the right version. + +## Why? +There are a lot of good routers out there. But looking at the ones that were really lightweight, I couldn't quite get something that fit with the route patterns I wanted. The code itself is simple enough, so I spent an evening writing this. + +## Handler +The handler is a simple function with the prototype `func(w http.ResponseWriter, r *http.Request, params map[string]string)`. The params argument contains the parameters parsed from wildcards and catch-alls in the URL, as described below. This type is aliased as httptreemux.HandlerFunc. + +### Using http.HandlerFunc +Due to the inclusion of the [context](https://godoc.org/context) package as of Go 1.7, `httptreemux` now supports handlers of type [http.HandlerFunc](https://godoc.org/net/http#HandlerFunc). There are two ways to enable this support. + +#### Adapting an Existing Router + +The `UsingContext` method will wrap the router or group in a new group at the same path, but adapted for use with `context` and `http.HandlerFunc`. + +```go +router := httptreemux.New() + +group := router.NewGroup("/api") +group.GET("/v1/:id", func(w http.ResponseWriter, r *http.Request, params map[string]string) { + id := params["id"] + fmt.Fprintf(w, "GET /api/v1/%s", id) +}) + +// UsingContext returns a version of the router or group with context support. +ctxGroup := group.UsingContext() // sibling to 'group' node in tree +ctxGroup.GET("/v2/:id", func(w http.ResponseWriter, r *http.Request) { + ctxData := httptreemux.ContextData(r.Context()) + params := ctxData.Params() + id := params["id"] + + // Useful for middleware to see which route was hit without dealing with wildcards + routePath := ctxData.Route() + + // Prints GET /api/v2/:id id=... + fmt.Fprintf(w, "GET %s id=%s", routePath, id) +}) + +http.ListenAndServe(":8080", router) +``` + +#### New Router with Context Support + +The `NewContextMux` function returns a router preconfigured for use with `context` and `http.HandlerFunc`. + +```go +router := httptreemux.NewContextMux() + +router.GET("/:page", func(w http.ResponseWriter, r *http.Request) { + params := httptreemux.ContextParams(r.Context()) + fmt.Fprintf(w, "GET /%s", params["page"]) +}) + +group := router.NewGroup("/api") +group.GET("/v1/:id", func(w http.ResponseWriter, r *http.Request) { + ctxData := httptreemux.ContextData(r.Context()) + params := ctxData.Params() + id := params["id"] + + // Useful for middleware to see which route was hit without dealing with wildcards + routePath := ctxData.Route() + + // Prints GET /api/v1/:id id=... + fmt.Fprintf(w, "GET %s id=%s", routePath, id) +}) + +http.ListenAndServe(":8080", router) +``` + + + +## Routing Rules +The syntax here is also modeled after httprouter. Each variable in a path may match on one segment only, except for an optional catch-all variable at the end of the URL. + +Some examples of valid URL patterns are: +* `/post/all` +* `/post/:postid` +* `/post/:postid/page/:page` +* `/post/:postid/:page` +* `/images/*path` +* `/favicon.ico` +* `/:year/:month/` +* `/:year/:month/:post` +* `/:page` + +Note that all of the above URL patterns may exist concurrently in the router. + +Path elements starting with `:` indicate a wildcard in the path. A wildcard will only match on a single path segment. That is, the pattern `/post/:postid` will match on `/post/1` or `/post/1/`, but not `/post/1/2`. + +A path element starting with `*` is a catch-all, whose value will be a string containing all text in the URL matched by the wildcards. For example, with a pattern of `/images/*path` and a requested URL `images/abc/def`, path would contain `abc/def`. A catch-all path will not match an empty string, so in this example a separate route would need to be installed if you also want to match `/images/`. + +#### Using : and * in routing patterns + +The characters `:` and `*` can be used at the beginning of a path segment by escaping them with a backslash. A double backslash at the beginning of a segment is interpreted as a single backslash. These escapes are only checked at the very beginning of a path segment; they are not necessary or processed elsewhere in a token. + +```go +router.GET("/foo/\\*starToken", handler) // matches /foo/*starToken +router.GET("/foo/star*inTheMiddle", handler) // matches /foo/star*inTheMiddle +router.GET("/foo/starBackslash\\*", handler) // matches /foo/starBackslash\* +router.GET("/foo/\\\\*backslashWithStar") // matches /foo/\*backslashWithStar +``` + +### Routing Groups +Lets you create a new group of routes with a given path prefix. Makes it easier to create clusters of paths like: +* `/api/v1/foo` +* `/api/v1/bar` + +To use this you do: +```go +router = httptreemux.New() +api := router.NewGroup("/api/v1") +api.GET("/foo", fooHandler) // becomes /api/v1/foo +api.GET("/bar", barHandler) // becomes /api/v1/bar +``` + +### Routing Priority +The priority rules in the router are simple. + +1. Static path segments take the highest priority. If a segment and its subtree are able to match the URL, that match is returned. +2. Wildcards take second priority. For a particular wildcard to match, that wildcard and its subtree must match the URL. +3. Finally, a catch-all rule will match when the earlier path segments have matched, and none of the static or wildcard conditions have matched. Catch-all rules must be at the end of a pattern. + +So with the following patterns adapted from [simpleblog](https://www.github.com/dimfeld/simpleblog), we'll see certain matches: +```go +router = httptreemux.New() +router.GET("/:page", pageHandler) +router.GET("/:year/:month/:post", postHandler) +router.GET("/:year/:month", archiveHandler) +router.GET("/images/*path", staticHandler) +router.GET("/favicon.ico", staticHandler) +``` + +#### Example scenarios + +- `/abc` will match `/:page` +- `/2014/05` will match `/:year/:month` +- `/2014/05/really-great-blog-post` will match `/:year/:month/:post` +- `/images/CoolImage.gif` will match `/images/*path` +- `/images/2014/05/MayImage.jpg` will also match `/images/*path`, with all the text after `/images` stored in the variable path. +- `/favicon.ico` will match `/favicon.ico` + +### Special Method Behavior +If TreeMux.HeadCanUseGet is set to true, the router will call the GET handler for a pattern when a HEAD request is processed, if no HEAD handler has been added for that pattern. This behavior is enabled by default. + +Go's http.ServeContent and related functions already handle the HEAD method correctly by sending only the header, so in most cases your handlers will not need any special cases for it. + +By default TreeMux.OptionsHandler is a null handler that doesn't affect your routing. If you set the handler, it will be called on OPTIONS requests to a path already registered by another method. If you set a path specific handler by using `router.OPTIONS`, it will override the global Options Handler for that path. + +### Trailing Slashes +The router has special handling for paths with trailing slashes. If a pattern is added to the router with a trailing slash, any matches on that pattern without a trailing slash will be redirected to the version with the slash. If a pattern does not have a trailing slash, matches on that pattern with a trailing slash will be redirected to the version without. + +The trailing slash flag is only stored once for a pattern. That is, if a pattern is added for a method with a trailing slash, all other methods for that pattern will also be considered to have a trailing slash, regardless of whether or not it is specified for those methods too. +However this behavior can be turned off by setting TreeMux.RedirectTrailingSlash to false. By default it is set to true. + +One exception to this rule is catch-all patterns. By default, trailing slash redirection is disabled on catch-all patterns, since the structure of the entire URL and the desired patterns can not be predicted. If trailing slash removal is desired on catch-all patterns, set TreeMux.RemoveCatchAllTrailingSlash to true. + +```go +router = httptreemux.New() +router.GET("/about", pageHandler) +router.GET("/posts/", postIndexHandler) +router.POST("/posts", postFormHandler) + +GET /about will match normally. +GET /about/ will redirect to /about. +GET /posts will redirect to /posts/. +GET /posts/ will match normally. +POST /posts will redirect to /posts/, because the GET method used a trailing slash. +``` + +### Custom Redirects + +RedirectBehavior sets the behavior when the router redirects the request to the canonical version of the requested URL using RedirectTrailingSlash or RedirectClean. The default behavior is to return a 301 status, redirecting the browser to the version of the URL that matches the given pattern. + +These are the values accepted for RedirectBehavior. You may also add these values to the RedirectMethodBehavior map to define custom per-method redirect behavior. + +* Redirect301 - HTTP 301 Moved Permanently; this is the default. +* Redirect307 - HTTP/1.1 Temporary Redirect +* Redirect308 - RFC7538 Permanent Redirect +* UseHandler - Don't redirect to the canonical path. Just call the handler instead. + +#### Rationale/Usage +On a POST request, most browsers that receive a 301 will submit a GET request to the redirected URL, meaning that any data will likely be lost. If you want to handle and avoid this behavior, you may use Redirect307, which causes most browsers to resubmit the request using the original method and request body. + +Since 307 is supposed to be a temporary redirect, the new 308 status code has been proposed, which is treated the same, except it indicates correctly that the redirection is permanent. The big caveat here is that the RFC is relatively recent, and older or non-compliant browsers will not handle it. Therefore its use is not recommended unless you really know what you're doing. + +Finally, the UseHandler value will simply call the handler function for the pattern, without redirecting to the canonical version of the URL. + +### RequestURI vs. URL.Path + +#### Escaped Slashes +Go automatically processes escaped characters in a URL, converting + to a space and %XX to the corresponding character. This can present issues when the URL contains a %2f, which is unescaped to '/'. This isn't an issue for most applications, but it will prevent the router from correctly matching paths and wildcards. + +For example, the pattern `/post/:post` would not match on `/post/abc%2fdef`, which is unescaped to `/post/abc/def`. The desired behavior is that it matches, and the `post` wildcard is set to `abc/def`. + +Therefore, this router defaults to using the raw URL, stored in the Request.RequestURI variable. Matching wildcards and catch-alls are then unescaped, to give the desired behavior. + +TL;DR: If a requested URL contains a %2f, this router will still do the right thing. Some Go HTTP routers may not due to [Go issue 3659](https://code.google.com/p/go/issues/detail?id=3659). + +#### Escaped Characters + +As mentioned above, characters in the URL are not unescaped when using RequestURI to determine the matched route. If this is a problem for you and you are unable to switch to URL.Path for the above reasons, you may set `router.EscapeAddedRoutes` to `true`. This option will run each added route through the `URL.EscapedPath` function, and add an additional route if the escaped version differs. + +#### http Package Utility Functions + +Although using RequestURI avoids the issue described above, certain utility functions such as `http.StripPrefix` modify URL.Path, and expect that the underlying router is using that field to make its decision. If you are using some of these functions, set the router's `PathSource` member to `URLPath`. This will give up the proper handling of escaped slashes described above, while allowing the router to work properly with these utility functions. + +## Concurrency + +The router contains an `RWMutex` that arbitrates access to the tree. This allows routes to be safely added from multiple goroutines at once. + +No concurrency controls are needed when only reading from the tree, so the default behavior is to not use the `RWMutex` when serving a request. This avoids a theoretical slowdown under high-usage scenarios from competing atomic integer operations inside the `RWMutex`. If your application adds routes to the router after it has begun serving requests, you should avoid potential race conditions by setting `router.SafeAddRoutesWhileRunning` to `true` to use the `RWMutex` when serving requests. + +## Error Handlers + +### NotFoundHandler +TreeMux.NotFoundHandler can be set to provide custom 404-error handling. The default implementation is Go's `http.NotFound` function. + +### MethodNotAllowedHandler +If a pattern matches, but the pattern does not have an associated handler for the requested method, the router calls the MethodNotAllowedHandler. The default +version of this handler just writes the status code `http.StatusMethodNotAllowed` and sets the response header's `Allowed` field appropriately. + +### Panic Handling +TreeMux.PanicHandler can be set to provide custom panic handling. The `SimplePanicHandler` just writes the status code `http.StatusInternalServerError`. The function `ShowErrorsPanicHandler`, adapted from [gocraft/web](https://github.com/gocraft/web), will print panic errors to the browser in an easily-readable format. + +## Unexpected Differences from Other Routers + +This router is intentionally light on features in the name of simplicity and +performance. When coming from another router that does heavier processing behind +the scenes, you may encounter some unexpected behavior. This list is by no means +exhaustive, but covers some nonobvious cases that users have encountered. + +### gorilla/pat query string modifications + +When matching on parameters in a route, the `gorilla/pat` router will modify +`Request.URL.RawQuery` to make it appear like the parameters were in the +query string. `httptreemux` does not do this. See [Issue #26](https://github.com/dimfeld/httptreemux/issues/26) for more details and a +code snippet that can perform this transformation for you, should you want it. + +### httprouter and catch-all parameters + +When using `httprouter`, a route with a catch-all parameter (e.g. `/images/*path`) will match on URLs like `/images/` where the catch-all parameter is empty. This router does not match on empty catch-all parameters, but the behavior can be duplicated by adding a route without the catch-all (e.g. `/images/`). + +## Middleware +This package provides no middleware. But there are a lot of great options out there and it's pretty easy to write your own. The router provides the `Use` and `UseHandler` functions to ease the creation of middleware chains. (Real documentation of these functions coming soon.) + +# Acknowledgements + +* Inspiration from Julien Schmidt's [httprouter](https://github.com/julienschmidt/httprouter) +* Show Errors panic handler from [gocraft/web](https://github.com/gocraft/web) diff --git a/vendor/github.com/dimfeld/httptreemux/v5/context.go b/vendor/github.com/dimfeld/httptreemux/v5/context.go new file mode 100644 index 0000000000..30ecf4fb43 --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/context.go @@ -0,0 +1,194 @@ +// +build go1.7 + +package httptreemux + +import ( + "context" + "net/http" +) + +// ContextGroup is a wrapper around Group, with the purpose of mimicking its API, but with the use of http.HandlerFunc-based handlers. +// Instead of passing a parameter map via the handler (i.e. httptreemux.HandlerFunc), the path parameters are accessed via the request +// object's context. +type ContextGroup struct { + group *Group +} + +// Use appends a middleware handler to the Group middleware stack. +func (cg *ContextGroup) Use(fn MiddlewareFunc) { + cg.group.Use(fn) +} + +// UseHandler is like Use but accepts http.Handler middleware. +func (cg *ContextGroup) UseHandler(middleware func(http.Handler) http.Handler) { + cg.group.UseHandler(middleware) +} + +// UsingContext wraps the receiver to return a new instance of a ContextGroup. +// The returned ContextGroup is a sibling to its wrapped Group, within the parent TreeMux. +// The choice of using a *Group as the receiver, as opposed to a function parameter, allows chaining +// while method calls between a TreeMux, Group, and ContextGroup. For example: +// +// tree := httptreemux.New() +// group := tree.NewGroup("/api") +// +// group.GET("/v1", func(w http.ResponseWriter, r *http.Request, params map[string]string) { +// w.Write([]byte(`GET /api/v1`)) +// }) +// +// group.UsingContext().GET("/v2", func(w http.ResponseWriter, r *http.Request) { +// w.Write([]byte(`GET /api/v2`)) +// }) +// +// http.ListenAndServe(":8080", tree) +// +func (g *Group) UsingContext() *ContextGroup { + return &ContextGroup{g} +} + +// NewContextGroup adds a child context group to its path. +func (cg *ContextGroup) NewContextGroup(path string) *ContextGroup { + return &ContextGroup{cg.group.NewGroup(path)} +} + +func (cg *ContextGroup) NewGroup(path string) *ContextGroup { + return cg.NewContextGroup(path) +} + +// Handle allows handling HTTP requests via an http.HandlerFunc, as opposed to an httptreemux.HandlerFunc. +// Any parameters from the request URL are stored in a map[string]string in the request's context. +func (cg *ContextGroup) Handle(method, path string, handler http.HandlerFunc) { + fullPath := cg.group.path + path + cg.group.Handle(method, path, func(w http.ResponseWriter, r *http.Request, params map[string]string) { + routeData := &contextData{ + route: fullPath, + params: params, + } + r = r.WithContext(AddRouteDataToContext(r.Context(), routeData)) + handler(w, r) + }) +} + +// Handler allows handling HTTP requests via an http.Handler interface, as opposed to an httptreemux.HandlerFunc. +// Any parameters from the request URL are stored in a map[string]string in the request's context. +func (cg *ContextGroup) Handler(method, path string, handler http.Handler) { + fullPath := cg.group.path + path + cg.group.Handle(method, path, func(w http.ResponseWriter, r *http.Request, params map[string]string) { + routeData := &contextData{ + route: fullPath, + params: params, + } + r = r.WithContext(AddRouteDataToContext(r.Context(), routeData)) + handler.ServeHTTP(w, r) + }) +} + +// GET is convenience method for handling GET requests on a context group. +func (cg *ContextGroup) GET(path string, handler http.HandlerFunc) { + cg.Handle("GET", path, handler) +} + +// POST is convenience method for handling POST requests on a context group. +func (cg *ContextGroup) POST(path string, handler http.HandlerFunc) { + cg.Handle("POST", path, handler) +} + +// PUT is convenience method for handling PUT requests on a context group. +func (cg *ContextGroup) PUT(path string, handler http.HandlerFunc) { + cg.Handle("PUT", path, handler) +} + +// DELETE is convenience method for handling DELETE requests on a context group. +func (cg *ContextGroup) DELETE(path string, handler http.HandlerFunc) { + cg.Handle("DELETE", path, handler) +} + +// PATCH is convenience method for handling PATCH requests on a context group. +func (cg *ContextGroup) PATCH(path string, handler http.HandlerFunc) { + cg.Handle("PATCH", path, handler) +} + +// HEAD is convenience method for handling HEAD requests on a context group. +func (cg *ContextGroup) HEAD(path string, handler http.HandlerFunc) { + cg.Handle("HEAD", path, handler) +} + +// OPTIONS is convenience method for handling OPTIONS requests on a context group. +func (cg *ContextGroup) OPTIONS(path string, handler http.HandlerFunc) { + cg.Handle("OPTIONS", path, handler) +} + +type contextData struct { + route string + params map[string]string +} + +func (cd *contextData) Route() string { + return cd.route +} + +func (cd *contextData) Params() map[string]string { + if cd.params != nil { + return cd.params + } + return map[string]string{} +} + +// ContextRouteData is the information associated with the matched path. +// Route() returns the matched route, without expanded wildcards. +// Params() returns a map of the route's wildcards and their matched values. +type ContextRouteData interface { + Route() string + Params() map[string]string +} + +// ContextParams returns a map of the route's wildcards and their matched values. +func ContextParams(ctx context.Context) map[string]string { + if cd := ContextData(ctx); cd != nil { + return cd.Params() + } + return map[string]string{} +} + +// ContextRoute returns the matched route, without expanded wildcards. +func ContextRoute(ctx context.Context) string { + if cd := ContextData(ctx); cd != nil { + return cd.Route() + } + return "" +} + +// ContextData returns the ContextRouteData associated with the matched path +func ContextData(ctx context.Context) ContextRouteData { + if p, ok := ctx.Value(contextDataKey).(ContextRouteData); ok { + return p + } + return nil +} + +// AddRouteDataToContext can be used for testing handlers, to insert route data into the request's `Context`. +func AddRouteDataToContext(ctx context.Context, data ContextRouteData) context.Context { + return context.WithValue(ctx, contextDataKey, data) +} + +// AddParamsToContext inserts a parameters map into a context using +// the package's internal context key. +func AddParamsToContext(ctx context.Context, params map[string]string) context.Context { + return AddRouteDataToContext(ctx, &contextData{ + params: params, + }) +} + +// AddRouteToContext inserts a route into a context using +// the package's internal context key. +func AddRouteToContext(ctx context.Context, route string) context.Context { + return AddRouteDataToContext(ctx, &contextData{ + route: route, + }) +} + +type contextKey int + +// contextDataKey is used to retrieve the path's params map and matched route +// from a request's context. +const contextDataKey contextKey = 0 diff --git a/vendor/github.com/dimfeld/httptreemux/v5/go.mod b/vendor/github.com/dimfeld/httptreemux/v5/go.mod new file mode 100644 index 0000000000..627765e225 --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/go.mod @@ -0,0 +1,3 @@ +module github.com/dimfeld/httptreemux/v5 + +go 1.9 diff --git a/vendor/github.com/dimfeld/httptreemux/v5/group.go b/vendor/github.com/dimfeld/httptreemux/v5/group.go new file mode 100644 index 0000000000..826f12bce1 --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/group.go @@ -0,0 +1,241 @@ +package httptreemux + +import ( + "fmt" + "net/http" + "net/url" + "strings" +) + +type MiddlewareFunc func(next HandlerFunc) HandlerFunc + +func handlerWithMiddlewares(handler HandlerFunc, stack []MiddlewareFunc) HandlerFunc { + for i := len(stack) - 1; i >= 0; i-- { + handler = stack[i](handler) + } + return handler +} + +type Group struct { + path string + mux *TreeMux + stack []MiddlewareFunc +} + +// Add a sub-group to this group +func (g *Group) NewGroup(path string) *Group { + if len(path) < 1 { + panic("Group path must not be empty") + } + + checkPath(path) + path = g.path + path + //Don't want trailing slash as all sub-paths start with slash + if path[len(path)-1] == '/' { + path = path[:len(path)-1] + } + return &Group{ + path: path, + mux: g.mux, + stack: g.stack[:len(g.stack):len(g.stack)], + } +} + +// Use appends a middleware handler to the Group middleware stack. +func (g *Group) Use(fn MiddlewareFunc) { + g.stack = append(g.stack, fn) +} + +type handlerWithParams struct { + handler HandlerFunc + params map[string]string +} + +func (h handlerWithParams) ServeHTTP(w http.ResponseWriter, r *http.Request) { + h.handler(w, r, h.params) +} + +// UseHandler is like Use but accepts http.Handler middleware. +func (g *Group) UseHandler(middleware func(http.Handler) http.Handler) { + g.stack = append(g.stack, func(next HandlerFunc) HandlerFunc { + return func(w http.ResponseWriter, r *http.Request, params map[string]string) { + nextHandler := handlerWithParams{ + handler: next, + params: params, + } + middleware(nextHandler).ServeHTTP(w, r) + } + }) +} + +// Path elements starting with : indicate a wildcard in the path. A wildcard will only match on a +// single path segment. That is, the pattern `/post/:postid` will match on `/post/1` or `/post/1/`, +// but not `/post/1/2`. +// +// A path element starting with * is a catch-all, whose value will be a string containing all text +// in the URL matched by the wildcards. For example, with a pattern of `/images/*path` and a +// requested URL `images/abc/def`, path would contain `abc/def`. +// +// # Routing Rule Priority +// +// The priority rules in the router are simple. +// +// 1. Static path segments take the highest priority. If a segment and its subtree are able to match the URL, that match is returned. +// +// 2. Wildcards take second priority. For a particular wildcard to match, that wildcard and its subtree must match the URL. +// +// 3. Finally, a catch-all rule will match when the earlier path segments have matched, and none of the static or wildcard conditions have matched. Catch-all rules must be at the end of a pattern. +// +// So with the following patterns, we'll see certain matches: +// router = httptreemux.New() +// router.GET("/:page", pageHandler) +// router.GET("/:year/:month/:post", postHandler) +// router.GET("/:year/:month", archiveHandler) +// router.GET("/images/*path", staticHandler) +// router.GET("/favicon.ico", staticHandler) +// +// /abc will match /:page +// /2014/05 will match /:year/:month +// /2014/05/really-great-blog-post will match /:year/:month/:post +// /images/CoolImage.gif will match /images/*path +// /images/2014/05/MayImage.jpg will also match /images/*path, with all the text after /images stored in the variable path. +// /favicon.ico will match /favicon.ico +// +// # Trailing Slashes +// +// The router has special handling for paths with trailing slashes. If a pattern is added to the +// router with a trailing slash, any matches on that pattern without a trailing slash will be +// redirected to the version with the slash. If a pattern does not have a trailing slash, matches on +// that pattern with a trailing slash will be redirected to the version without. +// +// The trailing slash flag is only stored once for a pattern. That is, if a pattern is added for a +// method with a trailing slash, all other methods for that pattern will also be considered to have a +// trailing slash, regardless of whether or not it is specified for those methods too. +// +// This behavior can be turned off by setting TreeMux.RedirectTrailingSlash to false. By +// default it is set to true. The specifics of the redirect depend on RedirectBehavior. +// +// One exception to this rule is catch-all patterns. By default, trailing slash redirection is +// disabled on catch-all patterns, since the structure of the entire URL and the desired patterns +// can not be predicted. If trailing slash removal is desired on catch-all patterns, set +// TreeMux.RemoveCatchAllTrailingSlash to true. +// +// router = httptreemux.New() +// router.GET("/about", pageHandler) +// router.GET("/posts/", postIndexHandler) +// router.POST("/posts", postFormHandler) +// +// GET /about will match normally. +// GET /about/ will redirect to /about. +// GET /posts will redirect to /posts/. +// GET /posts/ will match normally. +// POST /posts will redirect to /posts/, because the GET method used a trailing slash. +func (g *Group) Handle(method string, path string, handler HandlerFunc) { + g.mux.mutex.Lock() + defer g.mux.mutex.Unlock() + + if len(g.stack) > 0 { + handler = handlerWithMiddlewares(handler, g.stack) + } + + addSlash := false + addOne := func(thePath string) { + node := g.mux.root.addPath(thePath[1:], nil, false) + if addSlash { + node.addSlash = true + } + node.setHandler(method, handler, false) + + if g.mux.HeadCanUseGet && method == "GET" && node.leafHandler["HEAD"] == nil { + node.setHandler("HEAD", handler, true) + } + } + + checkPath(path) + path = g.path + path + if len(path) == 0 { + panic("Cannot map an empty path") + } + + if len(path) > 1 && path[len(path)-1] == '/' && g.mux.RedirectTrailingSlash { + addSlash = true + path = path[:len(path)-1] + } + + if g.mux.EscapeAddedRoutes { + u, err := url.ParseRequestURI(path) + if err != nil { + panic("URL parsing error " + err.Error() + " on url " + path) + } + escapedPath := unescapeSpecial(u.String()) + + if escapedPath != path { + addOne(escapedPath) + } + } + + addOne(path) +} + +// Syntactic sugar for Handle("GET", path, handler) +func (g *Group) GET(path string, handler HandlerFunc) { + g.Handle("GET", path, handler) +} + +// Syntactic sugar for Handle("POST", path, handler) +func (g *Group) POST(path string, handler HandlerFunc) { + g.Handle("POST", path, handler) +} + +// Syntactic sugar for Handle("PUT", path, handler) +func (g *Group) PUT(path string, handler HandlerFunc) { + g.Handle("PUT", path, handler) +} + +// Syntactic sugar for Handle("DELETE", path, handler) +func (g *Group) DELETE(path string, handler HandlerFunc) { + g.Handle("DELETE", path, handler) +} + +// Syntactic sugar for Handle("PATCH", path, handler) +func (g *Group) PATCH(path string, handler HandlerFunc) { + g.Handle("PATCH", path, handler) +} + +// Syntactic sugar for Handle("HEAD", path, handler) +func (g *Group) HEAD(path string, handler HandlerFunc) { + g.Handle("HEAD", path, handler) +} + +// Syntactic sugar for Handle("OPTIONS", path, handler) +func (g *Group) OPTIONS(path string, handler HandlerFunc) { + g.Handle("OPTIONS", path, handler) +} + +func checkPath(path string) { + // All non-empty paths must start with a slash + if len(path) > 0 && path[0] != '/' { + panic(fmt.Sprintf("Path %s must start with slash", path)) + } +} + +func unescapeSpecial(s string) string { + // Look for sequences of \*, *, and \: that were escaped, and undo some of that escaping. + + // Unescape /* since it references a wildcard token. + s = strings.Replace(s, "/%2A", "/*", -1) + + // Unescape /\: since it references a literal colon + s = strings.Replace(s, "/%5C:", "/\\:", -1) + + // Replace escaped /\\: with /\: + s = strings.Replace(s, "/%5C%5C:", "/%5C:", -1) + + // Replace escaped /\* with /* + s = strings.Replace(s, "/%5C%2A", "/%2A", -1) + + // Replace escaped /\\* with /\* + s = strings.Replace(s, "/%5C%5C%2A", "/%5C%2A", -1) + + return s +} diff --git a/vendor/github.com/dimfeld/httptreemux/v5/panichandler.go b/vendor/github.com/dimfeld/httptreemux/v5/panichandler.go new file mode 100644 index 0000000000..cebb661bc4 --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/panichandler.go @@ -0,0 +1,211 @@ +package httptreemux + +import ( + "bufio" + "encoding/json" + "html/template" + "net/http" + "os" + "runtime" + "strings" +) + +// SimplePanicHandler just returns error 500. +func SimplePanicHandler(w http.ResponseWriter, r *http.Request, err interface{}) { + w.WriteHeader(http.StatusInternalServerError) +} + +// ShowErrorsPanicHandler prints a nice representation of an error to the browser. +// This was taken from github.com/gocraft/web, which adapted it from the Traffic project. +func ShowErrorsPanicHandler(w http.ResponseWriter, r *http.Request, err interface{}) { + const size = 4096 + stack := make([]byte, size) + stack = stack[:runtime.Stack(stack, false)] + renderPrettyError(w, r, err, stack) +} + +func makeErrorData(r *http.Request, err interface{}, stack []byte, filePath string, line int) map[string]interface{} { + + data := map[string]interface{}{ + "Stack": string(stack), + "Params": r.URL.Query(), + "Method": r.Method, + "FilePath": filePath, + "Line": line, + "Lines": readErrorFileLines(filePath, line), + } + + if e, ok := err.(error); ok { + data["Error"] = e.Error() + } else { + data["Error"] = err + } + + return data +} + +func renderPrettyError(rw http.ResponseWriter, req *http.Request, err interface{}, stack []byte) { + _, filePath, line, _ := runtime.Caller(5) + + data := makeErrorData(req, err, stack, filePath, line) + rw.Header().Set("Content-Type", "text/html") + rw.WriteHeader(http.StatusInternalServerError) + + tpl := template.Must(template.New("ErrorPage").Parse(panicPageTpl)) + tpl.Execute(rw, data) +} + +func ShowErrorsJsonPanicHandler(w http.ResponseWriter, r *http.Request, err interface{}) { + const size = 4096 + stack := make([]byte, size) + stack = stack[:runtime.Stack(stack, false)] + + _, filePath, line, _ := runtime.Caller(4) + data := makeErrorData(r, err, stack, filePath, line) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + json.NewEncoder(w).Encode(data) +} + +func readErrorFileLines(filePath string, errorLine int) map[int]string { + lines := make(map[int]string) + + file, err := os.Open(filePath) + if err != nil { + return lines + } + + defer file.Close() + + reader := bufio.NewReader(file) + currentLine := 0 + for { + line, err := reader.ReadString('\n') + if err != nil || currentLine > errorLine+5 { + break + } + + currentLine++ + + if currentLine >= errorLine-5 { + lines[currentLine] = strings.Replace(line, "\n", "", -1) + } + } + + return lines +} + +const panicPageTpl string = ` + + + Panic + + + + +
+
+

Error

+
+
+ +
+

{{ .Error }}

+
+ +
+

+ In {{ .FilePath }}:{{ .Line }}

+

+ + + + + + +
+
{{ range $lineNumber, $line :=  .Lines }}{{ $lineNumber }}{{ end }}
+
+
{{ range $lineNumber, $line :=  .Lines }}{{ $line }}
{{ end }}
+
+

Stack

+
{{ .Stack }}
+

Request

+

Method: {{ .Method }}

+

Parameters:

+
    + {{ range $key, $value := .Params }} +
  • {{ $key }}: {{ $value }}
  • + {{ end }} +
+
+ + + ` diff --git a/vendor/github.com/dimfeld/httptreemux/v5/path.go b/vendor/github.com/dimfeld/httptreemux/v5/path.go new file mode 100644 index 0000000000..506ac38469 --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/path.go @@ -0,0 +1,127 @@ +// Copyright 2013 Julien Schmidt. All rights reserved. +// Based on the path package, Copyright 2009 The Go Authors. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +package httptreemux + +// Clean is the URL version of path.Clean, it returns a canonical URL path +// for p, eliminating . and .. elements. +// +// The following rules are applied iteratively until no further processing can +// be done: +// 1. Replace multiple slashes with a single slash. +// 2. Eliminate each . path name element (the current directory). +// 3. Eliminate each inner .. path name element (the parent directory) +// along with the non-.. element that precedes it. +// 4. Eliminate .. elements that begin a rooted path: +// that is, replace "/.." by "/" at the beginning of a path. +// +// If the result of this process is an empty string, "/" is returned +func Clean(p string) string { + if p == "" { + return "/" + } + + n := len(p) + var buf []byte + + // Invariants: + // reading from path; r is index of next byte to process. + // writing to buf; w is index of next byte to write. + + // path must start with '/' + r := 1 + w := 1 + + if p[0] != '/' { + r = 0 + buf = make([]byte, n+1) + buf[0] = '/' + } + + trailing := n > 2 && p[n-1] == '/' + + // A bit more clunky without a 'lazybuf' like the path package, but the loop + // gets completely inlined (bufApp). So in contrast to the path package this + // loop has no expensive function calls (except 1x make) + + for r < n { + switch { + case p[r] == '/': + // empty path element, trailing slash is added after the end + r++ + + case p[r] == '.' && r+1 == n: + trailing = true + r++ + + case p[r] == '.' && p[r+1] == '/': + // . element + r++ + + case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'): + // .. element: remove to last / + r += 2 + + if w > 1 { + // can backtrack + w-- + + if buf == nil { + for w > 1 && p[w] != '/' { + w-- + } + } else { + for w > 1 && buf[w] != '/' { + w-- + } + } + } + + default: + // real path element. + // add slash if needed + if w > 1 { + bufApp(&buf, p, w, '/') + w++ + } + + // copy element + for r < n && p[r] != '/' { + bufApp(&buf, p, w, p[r]) + w++ + r++ + } + } + } + + // re-append trailing slash + if trailing && w > 1 { + bufApp(&buf, p, w, '/') + w++ + } + + // Turn empty string into "/" + if w == 0 { + return "/" + } + + if buf == nil { + return p[:w] + } + return string(buf[:w]) +} + +// internal helper to lazily create a buffer if necessary +func bufApp(buf *[]byte, s string, w int, c byte) { + if *buf == nil { + if s[w] == c { + return + } + + *buf = make([]byte, len(s)) + copy(*buf, s[:w]) + } + (*buf)[w] = c +} diff --git a/vendor/github.com/dimfeld/httptreemux/v5/router.go b/vendor/github.com/dimfeld/httptreemux/v5/router.go new file mode 100644 index 0000000000..b8063e4d22 --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/router.go @@ -0,0 +1,300 @@ +// This is inspired by Julien Schmidt's httprouter, in that it uses a patricia tree, but the +// implementation is rather different. Specifically, the routing rules are relaxed so that a +// single path segment may be a wildcard in one route and a static token in another. This gives a +// nice combination of high performance with a lot of convenience in designing the routing patterns. +package httptreemux + +import ( + "fmt" + "net/http" + "net/url" +) + +// The params argument contains the parameters parsed from wildcards and catch-alls in the URL. +type HandlerFunc func(http.ResponseWriter, *http.Request, map[string]string) +type PanicHandler func(http.ResponseWriter, *http.Request, interface{}) + +// RedirectBehavior sets the behavior when the router redirects the request to the +// canonical version of the requested URL using RedirectTrailingSlash or RedirectClean. +// The default behavior is to return a 301 status, redirecting the browser to the version +// of the URL that matches the given pattern. +// +// On a POST request, most browsers that receive a 301 will submit a GET request to +// the redirected URL, meaning that any data will likely be lost. If you want to handle +// and avoid this behavior, you may use Redirect307, which causes most browsers to +// resubmit the request using the original method and request body. +// +// Since 307 is supposed to be a temporary redirect, the new 308 status code has been +// proposed, which is treated the same, except it indicates correctly that the redirection +// is permanent. The big caveat here is that the RFC is relatively recent, and older +// browsers will not know what to do with it. Therefore its use is not recommended +// unless you really know what you're doing. +// +// Finally, the UseHandler value will simply call the handler function for the pattern. +type RedirectBehavior int + +type PathSource int + +const ( + Redirect301 RedirectBehavior = iota // Return 301 Moved Permanently + Redirect307 // Return 307 HTTP/1.1 Temporary Redirect + Redirect308 // Return a 308 RFC7538 Permanent Redirect + UseHandler // Just call the handler function + + RequestURI PathSource = iota // Use r.RequestURI + URLPath // Use r.URL.Path +) + +// LookupResult contains information about a route lookup, which is returned from Lookup and +// can be passed to ServeLookupResult if the request should be served. +type LookupResult struct { + // StatusCode informs the caller about the result of the lookup. + // This will generally be `http.StatusNotFound` or `http.StatusMethodNotAllowed` for an + // error case. On a normal success, the statusCode will be `http.StatusOK`. A redirect code + // will also be used in the case + StatusCode int + handler HandlerFunc + params map[string]string + leafHandler map[string]HandlerFunc // Only has a value when StatusCode is MethodNotAllowed. +} + +// Dump returns a text representation of the routing tree. +func (t *TreeMux) Dump() string { + return t.root.dumpTree("", "") +} + +func (t *TreeMux) serveHTTPPanic(w http.ResponseWriter, r *http.Request) { + if err := recover(); err != nil { + t.PanicHandler(w, r, err) + } +} + +func (t *TreeMux) redirectStatusCode(method string) (int, bool) { + var behavior RedirectBehavior + var ok bool + if behavior, ok = t.RedirectMethodBehavior[method]; !ok { + behavior = t.RedirectBehavior + } + switch behavior { + case Redirect301: + return http.StatusMovedPermanently, true + case Redirect307: + return http.StatusTemporaryRedirect, true + case Redirect308: + // Go doesn't have a constant for this yet. Yet another sign + // that you probably shouldn't use it. + return 308, true + case UseHandler: + return 0, false + default: + return http.StatusMovedPermanently, true + } +} + +func redirectHandler(newPath string, statusCode int) HandlerFunc { + return func(w http.ResponseWriter, r *http.Request, params map[string]string) { + redirect(w, r, newPath, statusCode) + } +} + +func redirect(w http.ResponseWriter, r *http.Request, newPath string, statusCode int) { + newURL := url.URL{ + Path: newPath, + RawQuery: r.URL.RawQuery, + Fragment: r.URL.Fragment, + } + http.Redirect(w, r, newURL.String(), statusCode) +} + +func (t *TreeMux) lookup(w http.ResponseWriter, r *http.Request) (result LookupResult, found bool) { + result.StatusCode = http.StatusNotFound + path := r.RequestURI + unescapedPath := r.URL.Path + pathLen := len(path) + if pathLen > 0 && t.PathSource == RequestURI { + rawQueryLen := len(r.URL.RawQuery) + + if rawQueryLen != 0 || path[pathLen-1] == '?' { + // Remove any query string and the ?. + path = path[:pathLen-rawQueryLen-1] + pathLen = len(path) + } + } else { + // In testing with http.NewRequest, + // RequestURI is not set so just grab URL.Path instead. + path = r.URL.Path + pathLen = len(path) + } + + trailingSlash := path[pathLen-1] == '/' && pathLen > 1 + if trailingSlash && t.RedirectTrailingSlash { + path = path[:pathLen-1] + unescapedPath = unescapedPath[:len(unescapedPath)-1] + } + + n, handler, params := t.root.search(r.Method, path[1:]) + if n == nil { + if t.RedirectCleanPath { + // Path was not found. Try cleaning it up and search again. + // TODO Test this + cleanPath := Clean(unescapedPath) + n, handler, params = t.root.search(r.Method, cleanPath[1:]) + if n == nil { + // Still nothing found. + return + } + if statusCode, ok := t.redirectStatusCode(r.Method); ok { + // Redirect to the actual path + return LookupResult{statusCode, redirectHandler(cleanPath, statusCode), nil, nil}, true + } + } else { + // Not found. + return + } + } + + if handler == nil { + if r.Method == "OPTIONS" && t.OptionsHandler != nil { + handler = t.OptionsHandler + } + + if handler == nil { + result.leafHandler = n.leafHandler + result.StatusCode = http.StatusMethodNotAllowed + return + } + } + + if !n.isCatchAll || t.RemoveCatchAllTrailingSlash { + if trailingSlash != n.addSlash && t.RedirectTrailingSlash { + if statusCode, ok := t.redirectStatusCode(r.Method); ok { + var h HandlerFunc + if n.addSlash { + // Need to add a slash. + h = redirectHandler(unescapedPath+"/", statusCode) + } else if path != "/" { + // We need to remove the slash. This was already done at the + // beginning of the function. + h = redirectHandler(unescapedPath, statusCode) + } + + if h != nil { + return LookupResult{statusCode, h, nil, nil}, true + } + } + } + } + + var paramMap map[string]string + if len(params) != 0 { + if len(params) != len(n.leafWildcardNames) { + // Need better behavior here. Should this be a panic? + panic(fmt.Sprintf("httptreemux parameter list length mismatch: %v, %v", + params, n.leafWildcardNames)) + } + + paramMap = make(map[string]string) + numParams := len(params) + for index := 0; index < numParams; index++ { + paramMap[n.leafWildcardNames[numParams-index-1]] = params[index] + } + } + + return LookupResult{http.StatusOK, handler, paramMap, nil}, true +} + +// Lookup performs a lookup without actually serving the request or mutating the request or response. +// The return values are a LookupResult and a boolean. The boolean will be true when a handler +// was found or the lookup resulted in a redirect which will point to a real handler. It is false +// for requests which would result in a `StatusNotFound` or `StatusMethodNotAllowed`. +// +// Regardless of the returned boolean's value, the LookupResult may be passed to ServeLookupResult +// to be served appropriately. +func (t *TreeMux) Lookup(w http.ResponseWriter, r *http.Request) (LookupResult, bool) { + if t.SafeAddRoutesWhileRunning { + // In concurrency safe mode, we acquire a read lock on the mutex for any access. + // This is optional to avoid potential performance loss in high-usage scenarios. + t.mutex.RLock() + } + + result, found := t.lookup(w, r) + + if t.SafeAddRoutesWhileRunning { + t.mutex.RUnlock() + } + + return result, found +} + +// ServeLookupResult serves a request, given a lookup result from the Lookup function. +func (t *TreeMux) ServeLookupResult(w http.ResponseWriter, r *http.Request, lr LookupResult) { + if lr.handler == nil { + if lr.StatusCode == http.StatusMethodNotAllowed && lr.leafHandler != nil { + if t.SafeAddRoutesWhileRunning { + t.mutex.RLock() + } + + t.MethodNotAllowedHandler(w, r, lr.leafHandler) + + if t.SafeAddRoutesWhileRunning { + t.mutex.RUnlock() + } + } else { + t.NotFoundHandler(w, r) + } + } else { + r = t.setDefaultRequestContext(r) + lr.handler(w, r, lr.params) + } +} + +func (t *TreeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if t.PanicHandler != nil { + defer t.serveHTTPPanic(w, r) + } + + if t.SafeAddRoutesWhileRunning { + // In concurrency safe mode, we acquire a read lock on the mutex for any access. + // This is optional to avoid potential performance loss in high-usage scenarios. + t.mutex.RLock() + } + + result, _ := t.lookup(w, r) + + if t.SafeAddRoutesWhileRunning { + t.mutex.RUnlock() + } + + t.ServeLookupResult(w, r, result) +} + +// MethodNotAllowedHandler is the default handler for TreeMux.MethodNotAllowedHandler, +// which is called for patterns that match, but do not have a handler installed for the +// requested method. It simply writes the status code http.StatusMethodNotAllowed and fills +// in the `Allow` header value appropriately. +func MethodNotAllowedHandler(w http.ResponseWriter, r *http.Request, + methods map[string]HandlerFunc) { + + for m := range methods { + w.Header().Add("Allow", m) + } + + w.WriteHeader(http.StatusMethodNotAllowed) +} + +func New() *TreeMux { + tm := &TreeMux{ + root: &node{path: "/"}, + NotFoundHandler: http.NotFound, + MethodNotAllowedHandler: MethodNotAllowedHandler, + HeadCanUseGet: true, + RedirectTrailingSlash: true, + RedirectCleanPath: true, + RedirectBehavior: Redirect301, + RedirectMethodBehavior: make(map[string]RedirectBehavior), + PathSource: RequestURI, + EscapeAddedRoutes: false, + } + tm.Group.mux = tm + return tm +} diff --git a/vendor/github.com/dimfeld/httptreemux/v5/tree.go b/vendor/github.com/dimfeld/httptreemux/v5/tree.go new file mode 100644 index 0000000000..530d427848 --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/tree.go @@ -0,0 +1,340 @@ +package httptreemux + +import ( + "fmt" + "strings" +) + +type node struct { + path string + + priority int + + // The list of static children to check. + staticIndices []byte + staticChild []*node + + // If none of the above match, check the wildcard children + wildcardChild *node + + // If none of the above match, then we use the catch-all, if applicable. + catchAllChild *node + + // Data for the node is below. + + addSlash bool + isCatchAll bool + // If true, the head handler was set implicitly, so let it also be set explicitly. + implicitHead bool + // If this node is the end of the URL, then call the handler, if applicable. + leafHandler map[string]HandlerFunc + + // The names of the parameters to apply. + leafWildcardNames []string +} + +func (n *node) sortStaticChild(i int) { + for i > 0 && n.staticChild[i].priority > n.staticChild[i-1].priority { + n.staticChild[i], n.staticChild[i-1] = n.staticChild[i-1], n.staticChild[i] + n.staticIndices[i], n.staticIndices[i-1] = n.staticIndices[i-1], n.staticIndices[i] + i -= 1 + } +} + +func (n *node) setHandler(verb string, handler HandlerFunc, implicitHead bool) { + if n.leafHandler == nil { + n.leafHandler = make(map[string]HandlerFunc) + } + _, ok := n.leafHandler[verb] + if ok && (verb != "HEAD" || !n.implicitHead) { + panic(fmt.Sprintf("%s already handles %s", n.path, verb)) + } + n.leafHandler[verb] = handler + + if verb == "HEAD" { + n.implicitHead = implicitHead + } +} + +func (n *node) addPath(path string, wildcards []string, inStaticToken bool) *node { + leaf := len(path) == 0 + if leaf { + if wildcards != nil { + // Make sure the current wildcards are the same as the old ones. + // If not then we have an ambiguous path. + if n.leafWildcardNames != nil { + if len(n.leafWildcardNames) != len(wildcards) { + // This should never happen. + panic("Reached leaf node with differing wildcard array length. Please report this as a bug.") + } + + for i := 0; i < len(wildcards); i++ { + if n.leafWildcardNames[i] != wildcards[i] { + panic(fmt.Sprintf("Wildcards %v are ambiguous with wildcards %v", + n.leafWildcardNames, wildcards)) + } + } + } else { + // No wildcards yet, so just add the existing set. + n.leafWildcardNames = wildcards + } + } + + return n + } + + c := path[0] + nextSlash := strings.Index(path, "/") + var thisToken string + var tokenEnd int + + if c == '/' { + // Done processing the previous token, so reset inStaticToken to false. + thisToken = "/" + tokenEnd = 1 + } else if nextSlash == -1 { + thisToken = path + tokenEnd = len(path) + } else { + thisToken = path[0:nextSlash] + tokenEnd = nextSlash + } + remainingPath := path[tokenEnd:] + + if c == '*' && !inStaticToken { + // Token starts with a *, so it's a catch-all + thisToken = thisToken[1:] + if n.catchAllChild == nil { + n.catchAllChild = &node{path: thisToken, isCatchAll: true} + } + + if path[1:] != n.catchAllChild.path { + panic(fmt.Sprintf("Catch-all name in %s doesn't match %s. You probably tried to define overlapping catchalls", + path, n.catchAllChild.path)) + } + + if nextSlash != -1 { + panic("/ after catch-all found in " + path) + } + + if wildcards == nil { + wildcards = []string{thisToken} + } else { + wildcards = append(wildcards, thisToken) + } + n.catchAllChild.leafWildcardNames = wildcards + + return n.catchAllChild + } else if c == ':' && !inStaticToken { + // Token starts with a : + thisToken = thisToken[1:] + + if wildcards == nil { + wildcards = []string{thisToken} + } else { + wildcards = append(wildcards, thisToken) + } + + if n.wildcardChild == nil { + n.wildcardChild = &node{path: "wildcard"} + } + + return n.wildcardChild.addPath(remainingPath, wildcards, false) + + } else { + // if strings.ContainsAny(thisToken, ":*") { + // panic("* or : in middle of path component " + path) + // } + + unescaped := false + if len(thisToken) >= 2 && !inStaticToken { + if thisToken[0] == '\\' && (thisToken[1] == '*' || thisToken[1] == ':' || thisToken[1] == '\\') { + // The token starts with a character escaped by a backslash. Drop the backslash. + c = thisToken[1] + thisToken = thisToken[1:] + unescaped = true + } + } + + // Set inStaticToken to ensure that the rest of this token is not mistaken + // for a wildcard if a prefix split occurs at a '*' or ':'. + inStaticToken = (c != '/') + + // Do we have an existing node that starts with the same letter? + for i, index := range n.staticIndices { + if c == index { + // Yes. Split it based on the common prefix of the existing + // node and the new one. + child, prefixSplit := n.splitCommonPrefix(i, thisToken) + + child.priority++ + n.sortStaticChild(i) + if unescaped { + // Account for the removed backslash. + prefixSplit++ + } + return child.addPath(path[prefixSplit:], wildcards, inStaticToken) + } + } + + // No existing node starting with this letter, so create it. + child := &node{path: thisToken} + + if n.staticIndices == nil { + n.staticIndices = []byte{c} + n.staticChild = []*node{child} + } else { + n.staticIndices = append(n.staticIndices, c) + n.staticChild = append(n.staticChild, child) + } + return child.addPath(remainingPath, wildcards, inStaticToken) + } +} + +func (n *node) splitCommonPrefix(existingNodeIndex int, path string) (*node, int) { + childNode := n.staticChild[existingNodeIndex] + + if strings.HasPrefix(path, childNode.path) { + // No split needs to be done. Rather, the new path shares the entire + // prefix with the existing node, so the new node is just a child of + // the existing one. Or the new path is the same as the existing path, + // which means that we just move on to the next token. Either way, + // this return accomplishes that + return childNode, len(childNode.path) + } + + var i int + // Find the length of the common prefix of the child node and the new path. + for i = range childNode.path { + if i == len(path) { + break + } + if path[i] != childNode.path[i] { + break + } + } + + commonPrefix := path[0:i] + childNode.path = childNode.path[i:] + + // Create a new intermediary node in the place of the existing node, with + // the existing node as a child. + newNode := &node{ + path: commonPrefix, + priority: childNode.priority, + // Index is the first letter of the non-common part of the path. + staticIndices: []byte{childNode.path[0]}, + staticChild: []*node{childNode}, + } + n.staticChild[existingNodeIndex] = newNode + + return newNode, i +} + +func (n *node) search(method, path string) (found *node, handler HandlerFunc, params []string) { + // if test != nil { + // test.Logf("Searching for %s in %s", path, n.dumpTree("", "")) + // } + pathLen := len(path) + if pathLen == 0 { + if len(n.leafHandler) == 0 { + return nil, nil, nil + } else { + return n, n.leafHandler[method], nil + } + } + + // First see if this matches a static token. + firstChar := path[0] + for i, staticIndex := range n.staticIndices { + if staticIndex == firstChar { + child := n.staticChild[i] + childPathLen := len(child.path) + if pathLen >= childPathLen && child.path == path[:childPathLen] { + nextPath := path[childPathLen:] + found, handler, params = child.search(method, nextPath) + } + break + } + } + + // If we found a node and it had a valid handler, then return here. Otherwise + // let's remember that we found this one, but look for a better match. + if handler != nil { + return + } + + if n.wildcardChild != nil { + // Didn't find a static token, so check for a wildcard. + nextSlash := strings.IndexByte(path, '/') + if nextSlash < 0 { + nextSlash = pathLen + } + + thisToken := path[0:nextSlash] + nextToken := path[nextSlash:] + + if len(thisToken) > 0 { // Don't match on empty tokens. + wcNode, wcHandler, wcParams := n.wildcardChild.search(method, nextToken) + if wcHandler != nil || (found == nil && wcNode != nil) { + unescaped, err := unescape(thisToken) + if err != nil { + unescaped = thisToken + } + + if wcParams == nil { + wcParams = []string{unescaped} + } else { + wcParams = append(wcParams, unescaped) + } + + if wcHandler != nil { + return wcNode, wcHandler, wcParams + } + + // Didn't actually find a handler here, so remember that we + // found a node but also see if we can fall through to the + // catchall. + found = wcNode + handler = wcHandler + params = wcParams + } + } + } + + catchAllChild := n.catchAllChild + if catchAllChild != nil { + // Hit the catchall, so just assign the whole remaining path if it + // has a matching handler. + handler = catchAllChild.leafHandler[method] + // Found a handler, or we found a catchall node without a handler. + // Either way, return it since there's nothing left to check after this. + if handler != nil || found == nil { + unescaped, err := unescape(path) + if err != nil { + unescaped = path + } + + return catchAllChild, handler, []string{unescaped} + } + + } + + return found, handler, params +} + +func (n *node) dumpTree(prefix, nodeType string) string { + line := fmt.Sprintf("%s %02d %s%s [%d] %v wildcards %v\n", prefix, n.priority, nodeType, n.path, + len(n.staticChild), n.leafHandler, n.leafWildcardNames) + prefix += " " + for _, node := range n.staticChild { + line += node.dumpTree(prefix, "") + } + if n.wildcardChild != nil { + line += n.wildcardChild.dumpTree(prefix, ":") + } + if n.catchAllChild != nil { + line += n.catchAllChild.dumpTree(prefix, "*") + } + return line +} diff --git a/vendor/github.com/dimfeld/httptreemux/v5/treemux_16.go b/vendor/github.com/dimfeld/httptreemux/v5/treemux_16.go new file mode 100644 index 0000000000..6bd5f2e97a --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/treemux_16.go @@ -0,0 +1,86 @@ +// +build !go1.7 + +package httptreemux + +import ( + "net/http" + "sync" +) + +type TreeMux struct { + root *node + mutex sync.RWMutex + + Group + + // The default PanicHandler just returns a 500 code. + PanicHandler PanicHandler + + // The default NotFoundHandler is http.NotFound. + NotFoundHandler func(w http.ResponseWriter, r *http.Request) + + // Any OPTIONS request that matches a path without its own OPTIONS handler will use this handler, + // if set, instead of calling MethodNotAllowedHandler. + OptionsHandler HandlerFunc + + // MethodNotAllowedHandler is called when a pattern matches, but that + // pattern does not have a handler for the requested method. The default + // handler just writes the status code http.StatusMethodNotAllowed and adds + // the required Allowed header. + // The methods parameter contains the map of each method to the corresponding + // handler function. + MethodNotAllowedHandler func(w http.ResponseWriter, r *http.Request, + methods map[string]HandlerFunc) + + // HeadCanUseGet allows the router to use the GET handler to respond to + // HEAD requests if no explicit HEAD handler has been added for the + // matching pattern. This is true by default. + HeadCanUseGet bool + + // RedirectCleanPath allows the router to try clean the current request path, + // if no handler is registered for it, using CleanPath from github.com/dimfeld/httppath. + // This is true by default. + RedirectCleanPath bool + + // RedirectTrailingSlash enables automatic redirection in case router doesn't find a matching route + // for the current request path but a handler for the path with or without the trailing + // slash exists. This is true by default. + RedirectTrailingSlash bool + + // RemoveCatchAllTrailingSlash removes the trailing slash when a catch-all pattern + // is matched, if set to true. By default, catch-all paths are never redirected. + RemoveCatchAllTrailingSlash bool + + // RedirectBehavior sets the default redirect behavior when RedirectTrailingSlash or + // RedirectCleanPath are true. The default value is Redirect301. + RedirectBehavior RedirectBehavior + + // RedirectMethodBehavior overrides the default behavior for a particular HTTP method. + // The key is the method name, and the value is the behavior to use for that method. + RedirectMethodBehavior map[string]RedirectBehavior + + // PathSource determines from where the router gets its path to search. + // By default it pulls the data from the RequestURI member, but this can + // be overridden to use URL.Path instead. + // + // There is a small tradeoff here. Using RequestURI allows the router to handle + // encoded slashes (i.e. %2f) in the URL properly, while URL.Path provides + // better compatibility with some utility functions in the http + // library that modify the Request before passing it to the router. + PathSource PathSource + + // EscapeAddedRoutes controls URI escaping behavior when adding a route to the tree. + // If set to true, the router will add both the route as originally passed, and + // a version passed through URL.EscapedPath. This behavior is disabled by default. + EscapeAddedRoutes bool + + // SafeAddRoutesWhileRunning tells the router to protect all accesses to the tree with an RWMutex. This is only needed + // if you are going to add routes after the router has already begun serving requests. There is a potential + // performance penalty at high load. + SafeAddRoutesWhileRunning bool +} + +func (t *TreeMux) setDefaultRequestContext(r *http.Request) *http.Request { + // Nothing to do on Go 1.6 and before + return r +} diff --git a/vendor/github.com/dimfeld/httptreemux/v5/treemux_17.go b/vendor/github.com/dimfeld/httptreemux/v5/treemux_17.go new file mode 100644 index 0000000000..a80a500f3d --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/treemux_17.go @@ -0,0 +1,149 @@ +// +build go1.7 + +package httptreemux + +import ( + "context" + "net/http" + "sync" +) + +type TreeMux struct { + root *node + mutex sync.RWMutex + + Group + + // The default PanicHandler just returns a 500 code. + PanicHandler PanicHandler + + // The default NotFoundHandler is http.NotFound. + NotFoundHandler func(w http.ResponseWriter, r *http.Request) + + // Any OPTIONS request that matches a path without its own OPTIONS handler will use this handler, + // if set, instead of calling MethodNotAllowedHandler. + OptionsHandler HandlerFunc + + // MethodNotAllowedHandler is called when a pattern matches, but that + // pattern does not have a handler for the requested method. The default + // handler just writes the status code http.StatusMethodNotAllowed and adds + // the required Allowed header. + // The methods parameter contains the map of each method to the corresponding + // handler function. + MethodNotAllowedHandler func(w http.ResponseWriter, r *http.Request, + methods map[string]HandlerFunc) + + // HeadCanUseGet allows the router to use the GET handler to respond to + // HEAD requests if no explicit HEAD handler has been added for the + // matching pattern. This is true by default. + HeadCanUseGet bool + + // RedirectCleanPath allows the router to try clean the current request path, + // if no handler is registered for it, using CleanPath from github.com/dimfeld/httppath. + // This is true by default. + RedirectCleanPath bool + + // RedirectTrailingSlash enables automatic redirection in case router doesn't find a matching route + // for the current request path but a handler for the path with or without the trailing + // slash exists. This is true by default. + RedirectTrailingSlash bool + + // RemoveCatchAllTrailingSlash removes the trailing slash when a catch-all pattern + // is matched, if set to true. By default, catch-all paths are never redirected. + RemoveCatchAllTrailingSlash bool + + // RedirectBehavior sets the default redirect behavior when RedirectTrailingSlash or + // RedirectCleanPath are true. The default value is Redirect301. + RedirectBehavior RedirectBehavior + + // RedirectMethodBehavior overrides the default behavior for a particular HTTP method. + // The key is the method name, and the value is the behavior to use for that method. + RedirectMethodBehavior map[string]RedirectBehavior + + // PathSource determines from where the router gets its path to search. + // By default it pulls the data from the RequestURI member, but this can + // be overridden to use URL.Path instead. + // + // There is a small tradeoff here. Using RequestURI allows the router to handle + // encoded slashes (i.e. %2f) in the URL properly, while URL.Path provides + // better compatibility with some utility functions in the http + // library that modify the Request before passing it to the router. + PathSource PathSource + + // EscapeAddedRoutes controls URI escaping behavior when adding a route to the tree. + // If set to true, the router will add both the route as originally passed, and + // a version passed through URL.EscapedPath. This behavior is disabled by default. + EscapeAddedRoutes bool + + // If present, override the default context with this one. + DefaultContext context.Context + + // SafeAddRoutesWhileRunning tells the router to protect all accesses to the tree with an RWMutex. This is only needed + // if you are going to add routes after the router has already begun serving requests. There is a potential + // performance penalty at high load. + SafeAddRoutesWhileRunning bool +} + +func (t *TreeMux) setDefaultRequestContext(r *http.Request) *http.Request { + if t.DefaultContext != nil { + r = r.WithContext(t.DefaultContext) + } + + return r +} + +type ContextMux struct { + *TreeMux + *ContextGroup +} + +// NewContextMux returns a TreeMux preconfigured to work with standard http +// Handler functions and context objects. +func NewContextMux() *ContextMux { + mux := New() + cg := mux.UsingContext() + + return &ContextMux{ + TreeMux: mux, + ContextGroup: cg, + } +} + +func (cm *ContextMux) NewGroup(path string) *ContextGroup { + return cm.ContextGroup.NewGroup(path) +} + +// GET is convenience method for handling GET requests on a context group. +func (cm *ContextMux) GET(path string, handler http.HandlerFunc) { + cm.ContextGroup.Handle("GET", path, handler) +} + +// POST is convenience method for handling POST requests on a context group. +func (cm *ContextMux) POST(path string, handler http.HandlerFunc) { + cm.ContextGroup.Handle("POST", path, handler) +} + +// PUT is convenience method for handling PUT requests on a context group. +func (cm *ContextMux) PUT(path string, handler http.HandlerFunc) { + cm.ContextGroup.Handle("PUT", path, handler) +} + +// DELETE is convenience method for handling DELETE requests on a context group. +func (cm *ContextMux) DELETE(path string, handler http.HandlerFunc) { + cm.ContextGroup.Handle("DELETE", path, handler) +} + +// PATCH is convenience method for handling PATCH requests on a context group. +func (cm *ContextMux) PATCH(path string, handler http.HandlerFunc) { + cm.ContextGroup.Handle("PATCH", path, handler) +} + +// HEAD is convenience method for handling HEAD requests on a context group. +func (cm *ContextMux) HEAD(path string, handler http.HandlerFunc) { + cm.ContextGroup.Handle("HEAD", path, handler) +} + +// OPTIONS is convenience method for handling OPTIONS requests on a context group. +func (cm *ContextMux) OPTIONS(path string, handler http.HandlerFunc) { + cm.ContextGroup.Handle("OPTIONS", path, handler) +} diff --git a/vendor/github.com/dimfeld/httptreemux/v5/unescape_17.go b/vendor/github.com/dimfeld/httptreemux/v5/unescape_17.go new file mode 100644 index 0000000000..5d6d087875 --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/unescape_17.go @@ -0,0 +1,9 @@ +// +build !go1.8 + +package httptreemux + +import "net/url" + +func unescape(path string) (string, error) { + return url.QueryUnescape(path) +} diff --git a/vendor/github.com/dimfeld/httptreemux/v5/unescape_18.go b/vendor/github.com/dimfeld/httptreemux/v5/unescape_18.go new file mode 100644 index 0000000000..254dfcdd7f --- /dev/null +++ b/vendor/github.com/dimfeld/httptreemux/v5/unescape_18.go @@ -0,0 +1,9 @@ +// +build go1.8 + +package httptreemux + +import "net/url" + +func unescape(path string) (string, error) { + return url.PathUnescape(path) +} diff --git a/vendor/github.com/gorilla/websocket/.gitignore b/vendor/github.com/gorilla/websocket/.gitignore new file mode 100644 index 0000000000..cd3fcd1ef7 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/.gitignore @@ -0,0 +1,25 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +.idea/ +*.iml diff --git a/vendor/github.com/gorilla/websocket/AUTHORS b/vendor/github.com/gorilla/websocket/AUTHORS new file mode 100644 index 0000000000..1931f40068 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/AUTHORS @@ -0,0 +1,9 @@ +# This is the official list of Gorilla WebSocket authors for copyright +# purposes. +# +# Please keep the list sorted. + +Gary Burd +Google LLC (https://opensource.google.com/) +Joachim Bauch + diff --git a/vendor/github.com/gorilla/websocket/LICENSE b/vendor/github.com/gorilla/websocket/LICENSE new file mode 100644 index 0000000000..9171c97225 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/websocket/README.md b/vendor/github.com/gorilla/websocket/README.md new file mode 100644 index 0000000000..19aa2e75c8 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/README.md @@ -0,0 +1,64 @@ +# Gorilla WebSocket + +[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) +[![CircleCI](https://circleci.com/gh/gorilla/websocket.svg?style=svg)](https://circleci.com/gh/gorilla/websocket) + +Gorilla WebSocket is a [Go](http://golang.org/) implementation of the +[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. + +### Documentation + +* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc) +* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) +* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) +* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) +* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) + +### Status + +The Gorilla WebSocket package provides a complete and tested implementation of +the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The +package API is stable. + +### Installation + + go get github.com/gorilla/websocket + +### Protocol Compliance + +The Gorilla WebSocket package passes the server tests in the [Autobahn Test +Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn +subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). + +### Gorilla WebSocket compared with other packages + + + + + + + + + + + + + + + + + + +
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
+ +Notes: + +1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). +2. The application can get the type of a received data message by implementing + a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) + function. +3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. + Read returns when the input buffer is full or a frame boundary is + encountered. Each call to Write sends a single frame message. The Gorilla + io.Reader and io.WriteCloser operate on a single WebSocket message. + diff --git a/vendor/github.com/gorilla/websocket/client.go b/vendor/github.com/gorilla/websocket/client.go new file mode 100644 index 0000000000..962c06a391 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client.go @@ -0,0 +1,395 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bytes" + "context" + "crypto/tls" + "errors" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httptrace" + "net/url" + "strings" + "time" +) + +// ErrBadHandshake is returned when the server response to opening handshake is +// invalid. +var ErrBadHandshake = errors.New("websocket: bad handshake") + +var errInvalidCompression = errors.New("websocket: invalid compression negotiation") + +// NewClient creates a new client connection using the given net connection. +// The URL u specifies the host and request URI. Use requestHeader to specify +// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies +// (Cookie). Use the response.Header to get the selected subprotocol +// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). +// +// If the WebSocket handshake fails, ErrBadHandshake is returned along with a +// non-nil *http.Response so that callers can handle redirects, authentication, +// etc. +// +// Deprecated: Use Dialer instead. +func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { + d := Dialer{ + ReadBufferSize: readBufSize, + WriteBufferSize: writeBufSize, + NetDial: func(net, addr string) (net.Conn, error) { + return netConn, nil + }, + } + return d.Dial(u.String(), requestHeader) +} + +// A Dialer contains options for connecting to WebSocket server. +type Dialer struct { + // NetDial specifies the dial function for creating TCP connections. If + // NetDial is nil, net.Dial is used. + NetDial func(network, addr string) (net.Conn, error) + + // NetDialContext specifies the dial function for creating TCP connections. If + // NetDialContext is nil, net.DialContext is used. + NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error) + + // Proxy specifies a function to return a proxy for a given + // Request. If the function returns a non-nil error, the + // request is aborted with the provided error. + // If Proxy is nil or returns a nil *URL, no proxy is used. + Proxy func(*http.Request) (*url.URL, error) + + // TLSClientConfig specifies the TLS configuration to use with tls.Client. + // If nil, the default configuration is used. + TLSClientConfig *tls.Config + + // HandshakeTimeout specifies the duration for the handshake to complete. + HandshakeTimeout time.Duration + + // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer + // size is zero, then a useful default size is used. The I/O buffer sizes + // do not limit the size of the messages that can be sent or received. + ReadBufferSize, WriteBufferSize int + + // WriteBufferPool is a pool of buffers for write operations. If the value + // is not set, then write buffers are allocated to the connection for the + // lifetime of the connection. + // + // A pool is most useful when the application has a modest volume of writes + // across a large number of connections. + // + // Applications should use a single pool for each unique value of + // WriteBufferSize. + WriteBufferPool BufferPool + + // Subprotocols specifies the client's requested subprotocols. + Subprotocols []string + + // EnableCompression specifies if the client should attempt to negotiate + // per message compression (RFC 7692). Setting this value to true does not + // guarantee that compression will be supported. Currently only "no context + // takeover" modes are supported. + EnableCompression bool + + // Jar specifies the cookie jar. + // If Jar is nil, cookies are not sent in requests and ignored + // in responses. + Jar http.CookieJar +} + +// Dial creates a new client connection by calling DialContext with a background context. +func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { + return d.DialContext(context.Background(), urlStr, requestHeader) +} + +var errMalformedURL = errors.New("malformed ws or wss URL") + +func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { + hostPort = u.Host + hostNoPort = u.Host + if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { + hostNoPort = hostNoPort[:i] + } else { + switch u.Scheme { + case "wss": + hostPort += ":443" + case "https": + hostPort += ":443" + default: + hostPort += ":80" + } + } + return hostPort, hostNoPort +} + +// DefaultDialer is a dialer with all fields set to the default values. +var DefaultDialer = &Dialer{ + Proxy: http.ProxyFromEnvironment, + HandshakeTimeout: 45 * time.Second, +} + +// nilDialer is dialer to use when receiver is nil. +var nilDialer = *DefaultDialer + +// DialContext creates a new client connection. Use requestHeader to specify the +// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). +// Use the response.Header to get the selected subprotocol +// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). +// +// The context will be used in the request and in the Dialer. +// +// If the WebSocket handshake fails, ErrBadHandshake is returned along with a +// non-nil *http.Response so that callers can handle redirects, authentication, +// etcetera. The response body may not contain the entire response and does not +// need to be closed by the application. +func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { + if d == nil { + d = &nilDialer + } + + challengeKey, err := generateChallengeKey() + if err != nil { + return nil, nil, err + } + + u, err := url.Parse(urlStr) + if err != nil { + return nil, nil, err + } + + switch u.Scheme { + case "ws": + u.Scheme = "http" + case "wss": + u.Scheme = "https" + default: + return nil, nil, errMalformedURL + } + + if u.User != nil { + // User name and password are not allowed in websocket URIs. + return nil, nil, errMalformedURL + } + + req := &http.Request{ + Method: "GET", + URL: u, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + Host: u.Host, + } + req = req.WithContext(ctx) + + // Set the cookies present in the cookie jar of the dialer + if d.Jar != nil { + for _, cookie := range d.Jar.Cookies(u) { + req.AddCookie(cookie) + } + } + + // Set the request headers using the capitalization for names and values in + // RFC examples. Although the capitalization shouldn't matter, there are + // servers that depend on it. The Header.Set method is not used because the + // method canonicalizes the header names. + req.Header["Upgrade"] = []string{"websocket"} + req.Header["Connection"] = []string{"Upgrade"} + req.Header["Sec-WebSocket-Key"] = []string{challengeKey} + req.Header["Sec-WebSocket-Version"] = []string{"13"} + if len(d.Subprotocols) > 0 { + req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} + } + for k, vs := range requestHeader { + switch { + case k == "Host": + if len(vs) > 0 { + req.Host = vs[0] + } + case k == "Upgrade" || + k == "Connection" || + k == "Sec-Websocket-Key" || + k == "Sec-Websocket-Version" || + k == "Sec-Websocket-Extensions" || + (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): + return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) + case k == "Sec-Websocket-Protocol": + req.Header["Sec-WebSocket-Protocol"] = vs + default: + req.Header[k] = vs + } + } + + if d.EnableCompression { + req.Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"} + } + + if d.HandshakeTimeout != 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout) + defer cancel() + } + + // Get network dial function. + var netDial func(network, add string) (net.Conn, error) + + if d.NetDialContext != nil { + netDial = func(network, addr string) (net.Conn, error) { + return d.NetDialContext(ctx, network, addr) + } + } else if d.NetDial != nil { + netDial = d.NetDial + } else { + netDialer := &net.Dialer{} + netDial = func(network, addr string) (net.Conn, error) { + return netDialer.DialContext(ctx, network, addr) + } + } + + // If needed, wrap the dial function to set the connection deadline. + if deadline, ok := ctx.Deadline(); ok { + forwardDial := netDial + netDial = func(network, addr string) (net.Conn, error) { + c, err := forwardDial(network, addr) + if err != nil { + return nil, err + } + err = c.SetDeadline(deadline) + if err != nil { + c.Close() + return nil, err + } + return c, nil + } + } + + // If needed, wrap the dial function to connect through a proxy. + if d.Proxy != nil { + proxyURL, err := d.Proxy(req) + if err != nil { + return nil, nil, err + } + if proxyURL != nil { + dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial)) + if err != nil { + return nil, nil, err + } + netDial = dialer.Dial + } + } + + hostPort, hostNoPort := hostPortNoPort(u) + trace := httptrace.ContextClientTrace(ctx) + if trace != nil && trace.GetConn != nil { + trace.GetConn(hostPort) + } + + netConn, err := netDial("tcp", hostPort) + if trace != nil && trace.GotConn != nil { + trace.GotConn(httptrace.GotConnInfo{ + Conn: netConn, + }) + } + if err != nil { + return nil, nil, err + } + + defer func() { + if netConn != nil { + netConn.Close() + } + }() + + if u.Scheme == "https" { + cfg := cloneTLSConfig(d.TLSClientConfig) + if cfg.ServerName == "" { + cfg.ServerName = hostNoPort + } + tlsConn := tls.Client(netConn, cfg) + netConn = tlsConn + + var err error + if trace != nil { + err = doHandshakeWithTrace(trace, tlsConn, cfg) + } else { + err = doHandshake(tlsConn, cfg) + } + + if err != nil { + return nil, nil, err + } + } + + conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil) + + if err := req.Write(netConn); err != nil { + return nil, nil, err + } + + if trace != nil && trace.GotFirstResponseByte != nil { + if peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 { + trace.GotFirstResponseByte() + } + } + + resp, err := http.ReadResponse(conn.br, req) + if err != nil { + return nil, nil, err + } + + if d.Jar != nil { + if rc := resp.Cookies(); len(rc) > 0 { + d.Jar.SetCookies(u, rc) + } + } + + if resp.StatusCode != 101 || + !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || + !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || + resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { + // Before closing the network connection on return from this + // function, slurp up some of the response to aid application + // debugging. + buf := make([]byte, 1024) + n, _ := io.ReadFull(resp.Body, buf) + resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) + return nil, resp, ErrBadHandshake + } + + for _, ext := range parseExtensions(resp.Header) { + if ext[""] != "permessage-deflate" { + continue + } + _, snct := ext["server_no_context_takeover"] + _, cnct := ext["client_no_context_takeover"] + if !snct || !cnct { + return nil, resp, errInvalidCompression + } + conn.newCompressionWriter = compressNoContextTakeover + conn.newDecompressionReader = decompressNoContextTakeover + break + } + + resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) + conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") + + netConn.SetDeadline(time.Time{}) + netConn = nil // to avoid close in defer. + return conn, resp, nil +} + +func doHandshake(tlsConn *tls.Conn, cfg *tls.Config) error { + if err := tlsConn.Handshake(); err != nil { + return err + } + if !cfg.InsecureSkipVerify { + if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/gorilla/websocket/client_clone.go b/vendor/github.com/gorilla/websocket/client_clone.go new file mode 100644 index 0000000000..4f0d943723 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client_clone.go @@ -0,0 +1,16 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.8 + +package websocket + +import "crypto/tls" + +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + if cfg == nil { + return &tls.Config{} + } + return cfg.Clone() +} diff --git a/vendor/github.com/gorilla/websocket/client_clone_legacy.go b/vendor/github.com/gorilla/websocket/client_clone_legacy.go new file mode 100644 index 0000000000..babb007fb4 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client_clone_legacy.go @@ -0,0 +1,38 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.8 + +package websocket + +import "crypto/tls" + +// cloneTLSConfig clones all public fields except the fields +// SessionTicketsDisabled and SessionTicketKey. This avoids copying the +// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a +// config in active use. +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + if cfg == nil { + return &tls.Config{} + } + return &tls.Config{ + Rand: cfg.Rand, + Time: cfg.Time, + Certificates: cfg.Certificates, + NameToCertificate: cfg.NameToCertificate, + GetCertificate: cfg.GetCertificate, + RootCAs: cfg.RootCAs, + NextProtos: cfg.NextProtos, + ServerName: cfg.ServerName, + ClientAuth: cfg.ClientAuth, + ClientCAs: cfg.ClientCAs, + InsecureSkipVerify: cfg.InsecureSkipVerify, + CipherSuites: cfg.CipherSuites, + PreferServerCipherSuites: cfg.PreferServerCipherSuites, + ClientSessionCache: cfg.ClientSessionCache, + MinVersion: cfg.MinVersion, + MaxVersion: cfg.MaxVersion, + CurvePreferences: cfg.CurvePreferences, + } +} diff --git a/vendor/github.com/gorilla/websocket/compression.go b/vendor/github.com/gorilla/websocket/compression.go new file mode 100644 index 0000000000..813ffb1e84 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/compression.go @@ -0,0 +1,148 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "compress/flate" + "errors" + "io" + "strings" + "sync" +) + +const ( + minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 + maxCompressionLevel = flate.BestCompression + defaultCompressionLevel = 1 +) + +var ( + flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool + flateReaderPool = sync.Pool{New: func() interface{} { + return flate.NewReader(nil) + }} +) + +func decompressNoContextTakeover(r io.Reader) io.ReadCloser { + const tail = + // Add four bytes as specified in RFC + "\x00\x00\xff\xff" + + // Add final block to squelch unexpected EOF error from flate reader. + "\x01\x00\x00\xff\xff" + + fr, _ := flateReaderPool.Get().(io.ReadCloser) + fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) + return &flateReadWrapper{fr} +} + +func isValidCompressionLevel(level int) bool { + return minCompressionLevel <= level && level <= maxCompressionLevel +} + +func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { + p := &flateWriterPools[level-minCompressionLevel] + tw := &truncWriter{w: w} + fw, _ := p.Get().(*flate.Writer) + if fw == nil { + fw, _ = flate.NewWriter(tw, level) + } else { + fw.Reset(tw) + } + return &flateWriteWrapper{fw: fw, tw: tw, p: p} +} + +// truncWriter is an io.Writer that writes all but the last four bytes of the +// stream to another io.Writer. +type truncWriter struct { + w io.WriteCloser + n int + p [4]byte +} + +func (w *truncWriter) Write(p []byte) (int, error) { + n := 0 + + // fill buffer first for simplicity. + if w.n < len(w.p) { + n = copy(w.p[w.n:], p) + p = p[n:] + w.n += n + if len(p) == 0 { + return n, nil + } + } + + m := len(p) + if m > len(w.p) { + m = len(w.p) + } + + if nn, err := w.w.Write(w.p[:m]); err != nil { + return n + nn, err + } + + copy(w.p[:], w.p[m:]) + copy(w.p[len(w.p)-m:], p[len(p)-m:]) + nn, err := w.w.Write(p[:len(p)-m]) + return n + nn, err +} + +type flateWriteWrapper struct { + fw *flate.Writer + tw *truncWriter + p *sync.Pool +} + +func (w *flateWriteWrapper) Write(p []byte) (int, error) { + if w.fw == nil { + return 0, errWriteClosed + } + return w.fw.Write(p) +} + +func (w *flateWriteWrapper) Close() error { + if w.fw == nil { + return errWriteClosed + } + err1 := w.fw.Flush() + w.p.Put(w.fw) + w.fw = nil + if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { + return errors.New("websocket: internal error, unexpected bytes at end of flate stream") + } + err2 := w.tw.w.Close() + if err1 != nil { + return err1 + } + return err2 +} + +type flateReadWrapper struct { + fr io.ReadCloser +} + +func (r *flateReadWrapper) Read(p []byte) (int, error) { + if r.fr == nil { + return 0, io.ErrClosedPipe + } + n, err := r.fr.Read(p) + if err == io.EOF { + // Preemptively place the reader back in the pool. This helps with + // scenarios where the application does not call NextReader() soon after + // this final read. + r.Close() + } + return n, err +} + +func (r *flateReadWrapper) Close() error { + if r.fr == nil { + return io.ErrClosedPipe + } + err := r.fr.Close() + flateReaderPool.Put(r.fr) + r.fr = nil + return err +} diff --git a/vendor/github.com/gorilla/websocket/conn.go b/vendor/github.com/gorilla/websocket/conn.go new file mode 100644 index 0000000000..ca46d2f793 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn.go @@ -0,0 +1,1201 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "encoding/binary" + "errors" + "io" + "io/ioutil" + "math/rand" + "net" + "strconv" + "sync" + "time" + "unicode/utf8" +) + +const ( + // Frame header byte 0 bits from Section 5.2 of RFC 6455 + finalBit = 1 << 7 + rsv1Bit = 1 << 6 + rsv2Bit = 1 << 5 + rsv3Bit = 1 << 4 + + // Frame header byte 1 bits from Section 5.2 of RFC 6455 + maskBit = 1 << 7 + + maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask + maxControlFramePayloadSize = 125 + + writeWait = time.Second + + defaultReadBufferSize = 4096 + defaultWriteBufferSize = 4096 + + continuationFrame = 0 + noFrame = -1 +) + +// Close codes defined in RFC 6455, section 11.7. +const ( + CloseNormalClosure = 1000 + CloseGoingAway = 1001 + CloseProtocolError = 1002 + CloseUnsupportedData = 1003 + CloseNoStatusReceived = 1005 + CloseAbnormalClosure = 1006 + CloseInvalidFramePayloadData = 1007 + ClosePolicyViolation = 1008 + CloseMessageTooBig = 1009 + CloseMandatoryExtension = 1010 + CloseInternalServerErr = 1011 + CloseServiceRestart = 1012 + CloseTryAgainLater = 1013 + CloseTLSHandshake = 1015 +) + +// The message types are defined in RFC 6455, section 11.8. +const ( + // TextMessage denotes a text data message. The text message payload is + // interpreted as UTF-8 encoded text data. + TextMessage = 1 + + // BinaryMessage denotes a binary data message. + BinaryMessage = 2 + + // CloseMessage denotes a close control message. The optional message + // payload contains a numeric code and text. Use the FormatCloseMessage + // function to format a close message payload. + CloseMessage = 8 + + // PingMessage denotes a ping control message. The optional message payload + // is UTF-8 encoded text. + PingMessage = 9 + + // PongMessage denotes a pong control message. The optional message payload + // is UTF-8 encoded text. + PongMessage = 10 +) + +// ErrCloseSent is returned when the application writes a message to the +// connection after sending a close message. +var ErrCloseSent = errors.New("websocket: close sent") + +// ErrReadLimit is returned when reading a message that is larger than the +// read limit set for the connection. +var ErrReadLimit = errors.New("websocket: read limit exceeded") + +// netError satisfies the net Error interface. +type netError struct { + msg string + temporary bool + timeout bool +} + +func (e *netError) Error() string { return e.msg } +func (e *netError) Temporary() bool { return e.temporary } +func (e *netError) Timeout() bool { return e.timeout } + +// CloseError represents a close message. +type CloseError struct { + // Code is defined in RFC 6455, section 11.7. + Code int + + // Text is the optional text payload. + Text string +} + +func (e *CloseError) Error() string { + s := []byte("websocket: close ") + s = strconv.AppendInt(s, int64(e.Code), 10) + switch e.Code { + case CloseNormalClosure: + s = append(s, " (normal)"...) + case CloseGoingAway: + s = append(s, " (going away)"...) + case CloseProtocolError: + s = append(s, " (protocol error)"...) + case CloseUnsupportedData: + s = append(s, " (unsupported data)"...) + case CloseNoStatusReceived: + s = append(s, " (no status)"...) + case CloseAbnormalClosure: + s = append(s, " (abnormal closure)"...) + case CloseInvalidFramePayloadData: + s = append(s, " (invalid payload data)"...) + case ClosePolicyViolation: + s = append(s, " (policy violation)"...) + case CloseMessageTooBig: + s = append(s, " (message too big)"...) + case CloseMandatoryExtension: + s = append(s, " (mandatory extension missing)"...) + case CloseInternalServerErr: + s = append(s, " (internal server error)"...) + case CloseTLSHandshake: + s = append(s, " (TLS handshake error)"...) + } + if e.Text != "" { + s = append(s, ": "...) + s = append(s, e.Text...) + } + return string(s) +} + +// IsCloseError returns boolean indicating whether the error is a *CloseError +// with one of the specified codes. +func IsCloseError(err error, codes ...int) bool { + if e, ok := err.(*CloseError); ok { + for _, code := range codes { + if e.Code == code { + return true + } + } + } + return false +} + +// IsUnexpectedCloseError returns boolean indicating whether the error is a +// *CloseError with a code not in the list of expected codes. +func IsUnexpectedCloseError(err error, expectedCodes ...int) bool { + if e, ok := err.(*CloseError); ok { + for _, code := range expectedCodes { + if e.Code == code { + return false + } + } + return true + } + return false +} + +var ( + errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true} + errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()} + errBadWriteOpCode = errors.New("websocket: bad write message type") + errWriteClosed = errors.New("websocket: write closed") + errInvalidControlFrame = errors.New("websocket: invalid control frame") +) + +func newMaskKey() [4]byte { + n := rand.Uint32() + return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} +} + +func hideTempErr(err error) error { + if e, ok := err.(net.Error); ok && e.Temporary() { + err = &netError{msg: e.Error(), timeout: e.Timeout()} + } + return err +} + +func isControl(frameType int) bool { + return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage +} + +func isData(frameType int) bool { + return frameType == TextMessage || frameType == BinaryMessage +} + +var validReceivedCloseCodes = map[int]bool{ + // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number + + CloseNormalClosure: true, + CloseGoingAway: true, + CloseProtocolError: true, + CloseUnsupportedData: true, + CloseNoStatusReceived: false, + CloseAbnormalClosure: false, + CloseInvalidFramePayloadData: true, + ClosePolicyViolation: true, + CloseMessageTooBig: true, + CloseMandatoryExtension: true, + CloseInternalServerErr: true, + CloseServiceRestart: true, + CloseTryAgainLater: true, + CloseTLSHandshake: false, +} + +func isValidReceivedCloseCode(code int) bool { + return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) +} + +// BufferPool represents a pool of buffers. The *sync.Pool type satisfies this +// interface. The type of the value stored in a pool is not specified. +type BufferPool interface { + // Get gets a value from the pool or returns nil if the pool is empty. + Get() interface{} + // Put adds a value to the pool. + Put(interface{}) +} + +// writePoolData is the type added to the write buffer pool. This wrapper is +// used to prevent applications from peeking at and depending on the values +// added to the pool. +type writePoolData struct{ buf []byte } + +// The Conn type represents a WebSocket connection. +type Conn struct { + conn net.Conn + isServer bool + subprotocol string + + // Write fields + mu chan struct{} // used as mutex to protect write to conn + writeBuf []byte // frame is constructed in this buffer. + writePool BufferPool + writeBufSize int + writeDeadline time.Time + writer io.WriteCloser // the current writer returned to the application + isWriting bool // for best-effort concurrent write detection + + writeErrMu sync.Mutex + writeErr error + + enableWriteCompression bool + compressionLevel int + newCompressionWriter func(io.WriteCloser, int) io.WriteCloser + + // Read fields + reader io.ReadCloser // the current reader returned to the application + readErr error + br *bufio.Reader + // bytes remaining in current frame. + // set setReadRemaining to safely update this value and prevent overflow + readRemaining int64 + readFinal bool // true the current message has more frames. + readLength int64 // Message size. + readLimit int64 // Maximum message size. + readMaskPos int + readMaskKey [4]byte + handlePong func(string) error + handlePing func(string) error + handleClose func(int, string) error + readErrCount int + messageReader *messageReader // the current low-level reader + + readDecompress bool // whether last read frame had RSV1 set + newDecompressionReader func(io.Reader) io.ReadCloser +} + +func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, writeBufferPool BufferPool, br *bufio.Reader, writeBuf []byte) *Conn { + + if br == nil { + if readBufferSize == 0 { + readBufferSize = defaultReadBufferSize + } else if readBufferSize < maxControlFramePayloadSize { + // must be large enough for control frame + readBufferSize = maxControlFramePayloadSize + } + br = bufio.NewReaderSize(conn, readBufferSize) + } + + if writeBufferSize <= 0 { + writeBufferSize = defaultWriteBufferSize + } + writeBufferSize += maxFrameHeaderSize + + if writeBuf == nil && writeBufferPool == nil { + writeBuf = make([]byte, writeBufferSize) + } + + mu := make(chan struct{}, 1) + mu <- struct{}{} + c := &Conn{ + isServer: isServer, + br: br, + conn: conn, + mu: mu, + readFinal: true, + writeBuf: writeBuf, + writePool: writeBufferPool, + writeBufSize: writeBufferSize, + enableWriteCompression: true, + compressionLevel: defaultCompressionLevel, + } + c.SetCloseHandler(nil) + c.SetPingHandler(nil) + c.SetPongHandler(nil) + return c +} + +// setReadRemaining tracks the number of bytes remaining on the connection. If n +// overflows, an ErrReadLimit is returned. +func (c *Conn) setReadRemaining(n int64) error { + if n < 0 { + return ErrReadLimit + } + + c.readRemaining = n + return nil +} + +// Subprotocol returns the negotiated protocol for the connection. +func (c *Conn) Subprotocol() string { + return c.subprotocol +} + +// Close closes the underlying network connection without sending or waiting +// for a close message. +func (c *Conn) Close() error { + return c.conn.Close() +} + +// LocalAddr returns the local network address. +func (c *Conn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +// RemoteAddr returns the remote network address. +func (c *Conn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +// Write methods + +func (c *Conn) writeFatal(err error) error { + err = hideTempErr(err) + c.writeErrMu.Lock() + if c.writeErr == nil { + c.writeErr = err + } + c.writeErrMu.Unlock() + return err +} + +func (c *Conn) read(n int) ([]byte, error) { + p, err := c.br.Peek(n) + if err == io.EOF { + err = errUnexpectedEOF + } + c.br.Discard(len(p)) + return p, err +} + +func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error { + <-c.mu + defer func() { c.mu <- struct{}{} }() + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + if err != nil { + return err + } + + c.conn.SetWriteDeadline(deadline) + if len(buf1) == 0 { + _, err = c.conn.Write(buf0) + } else { + err = c.writeBufs(buf0, buf1) + } + if err != nil { + return c.writeFatal(err) + } + if frameType == CloseMessage { + c.writeFatal(ErrCloseSent) + } + return nil +} + +// WriteControl writes a control message with the given deadline. The allowed +// message types are CloseMessage, PingMessage and PongMessage. +func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error { + if !isControl(messageType) { + return errBadWriteOpCode + } + if len(data) > maxControlFramePayloadSize { + return errInvalidControlFrame + } + + b0 := byte(messageType) | finalBit + b1 := byte(len(data)) + if !c.isServer { + b1 |= maskBit + } + + buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize) + buf = append(buf, b0, b1) + + if c.isServer { + buf = append(buf, data...) + } else { + key := newMaskKey() + buf = append(buf, key[:]...) + buf = append(buf, data...) + maskBytes(key, 0, buf[6:]) + } + + d := 1000 * time.Hour + if !deadline.IsZero() { + d = deadline.Sub(time.Now()) + if d < 0 { + return errWriteTimeout + } + } + + timer := time.NewTimer(d) + select { + case <-c.mu: + timer.Stop() + case <-timer.C: + return errWriteTimeout + } + defer func() { c.mu <- struct{}{} }() + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + if err != nil { + return err + } + + c.conn.SetWriteDeadline(deadline) + _, err = c.conn.Write(buf) + if err != nil { + return c.writeFatal(err) + } + if messageType == CloseMessage { + c.writeFatal(ErrCloseSent) + } + return err +} + +// beginMessage prepares a connection and message writer for a new message. +func (c *Conn) beginMessage(mw *messageWriter, messageType int) error { + // Close previous writer if not already closed by the application. It's + // probably better to return an error in this situation, but we cannot + // change this without breaking existing applications. + if c.writer != nil { + c.writer.Close() + c.writer = nil + } + + if !isControl(messageType) && !isData(messageType) { + return errBadWriteOpCode + } + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + if err != nil { + return err + } + + mw.c = c + mw.frameType = messageType + mw.pos = maxFrameHeaderSize + + if c.writeBuf == nil { + wpd, ok := c.writePool.Get().(writePoolData) + if ok { + c.writeBuf = wpd.buf + } else { + c.writeBuf = make([]byte, c.writeBufSize) + } + } + return nil +} + +// NextWriter returns a writer for the next message to send. The writer's Close +// method flushes the complete message to the network. +// +// There can be at most one open writer on a connection. NextWriter closes the +// previous writer if the application has not already done so. +// +// All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and +// PongMessage) are supported. +func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { + var mw messageWriter + if err := c.beginMessage(&mw, messageType); err != nil { + return nil, err + } + c.writer = &mw + if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { + w := c.newCompressionWriter(c.writer, c.compressionLevel) + mw.compress = true + c.writer = w + } + return c.writer, nil +} + +type messageWriter struct { + c *Conn + compress bool // whether next call to flushFrame should set RSV1 + pos int // end of data in writeBuf. + frameType int // type of the current frame. + err error +} + +func (w *messageWriter) endMessage(err error) error { + if w.err != nil { + return err + } + c := w.c + w.err = err + c.writer = nil + if c.writePool != nil { + c.writePool.Put(writePoolData{buf: c.writeBuf}) + c.writeBuf = nil + } + return err +} + +// flushFrame writes buffered data and extra as a frame to the network. The +// final argument indicates that this is the last frame in the message. +func (w *messageWriter) flushFrame(final bool, extra []byte) error { + c := w.c + length := w.pos - maxFrameHeaderSize + len(extra) + + // Check for invalid control frames. + if isControl(w.frameType) && + (!final || length > maxControlFramePayloadSize) { + return w.endMessage(errInvalidControlFrame) + } + + b0 := byte(w.frameType) + if final { + b0 |= finalBit + } + if w.compress { + b0 |= rsv1Bit + } + w.compress = false + + b1 := byte(0) + if !c.isServer { + b1 |= maskBit + } + + // Assume that the frame starts at beginning of c.writeBuf. + framePos := 0 + if c.isServer { + // Adjust up if mask not included in the header. + framePos = 4 + } + + switch { + case length >= 65536: + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | 127 + binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length)) + case length > 125: + framePos += 6 + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | 126 + binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length)) + default: + framePos += 8 + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | byte(length) + } + + if !c.isServer { + key := newMaskKey() + copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) + maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) + if len(extra) > 0 { + return w.endMessage(c.writeFatal(errors.New("websocket: internal error, extra used in client mode"))) + } + } + + // Write the buffers to the connection with best-effort detection of + // concurrent writes. See the concurrency section in the package + // documentation for more info. + + if c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = true + + err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra) + + if !c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = false + + if err != nil { + return w.endMessage(err) + } + + if final { + w.endMessage(errWriteClosed) + return nil + } + + // Setup for next frame. + w.pos = maxFrameHeaderSize + w.frameType = continuationFrame + return nil +} + +func (w *messageWriter) ncopy(max int) (int, error) { + n := len(w.c.writeBuf) - w.pos + if n <= 0 { + if err := w.flushFrame(false, nil); err != nil { + return 0, err + } + n = len(w.c.writeBuf) - w.pos + } + if n > max { + n = max + } + return n, nil +} + +func (w *messageWriter) Write(p []byte) (int, error) { + if w.err != nil { + return 0, w.err + } + + if len(p) > 2*len(w.c.writeBuf) && w.c.isServer { + // Don't buffer large messages. + err := w.flushFrame(false, p) + if err != nil { + return 0, err + } + return len(p), nil + } + + nn := len(p) + for len(p) > 0 { + n, err := w.ncopy(len(p)) + if err != nil { + return 0, err + } + copy(w.c.writeBuf[w.pos:], p[:n]) + w.pos += n + p = p[n:] + } + return nn, nil +} + +func (w *messageWriter) WriteString(p string) (int, error) { + if w.err != nil { + return 0, w.err + } + + nn := len(p) + for len(p) > 0 { + n, err := w.ncopy(len(p)) + if err != nil { + return 0, err + } + copy(w.c.writeBuf[w.pos:], p[:n]) + w.pos += n + p = p[n:] + } + return nn, nil +} + +func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { + if w.err != nil { + return 0, w.err + } + for { + if w.pos == len(w.c.writeBuf) { + err = w.flushFrame(false, nil) + if err != nil { + break + } + } + var n int + n, err = r.Read(w.c.writeBuf[w.pos:]) + w.pos += n + nn += int64(n) + if err != nil { + if err == io.EOF { + err = nil + } + break + } + } + return nn, err +} + +func (w *messageWriter) Close() error { + if w.err != nil { + return w.err + } + return w.flushFrame(true, nil) +} + +// WritePreparedMessage writes prepared message into connection. +func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error { + frameType, frameData, err := pm.frame(prepareKey{ + isServer: c.isServer, + compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType), + compressionLevel: c.compressionLevel, + }) + if err != nil { + return err + } + if c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = true + err = c.write(frameType, c.writeDeadline, frameData, nil) + if !c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = false + return err +} + +// WriteMessage is a helper method for getting a writer using NextWriter, +// writing the message and closing the writer. +func (c *Conn) WriteMessage(messageType int, data []byte) error { + + if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { + // Fast path with no allocations and single frame. + + var mw messageWriter + if err := c.beginMessage(&mw, messageType); err != nil { + return err + } + n := copy(c.writeBuf[mw.pos:], data) + mw.pos += n + data = data[n:] + return mw.flushFrame(true, data) + } + + w, err := c.NextWriter(messageType) + if err != nil { + return err + } + if _, err = w.Write(data); err != nil { + return err + } + return w.Close() +} + +// SetWriteDeadline sets the write deadline on the underlying network +// connection. After a write has timed out, the websocket state is corrupt and +// all future writes will return an error. A zero value for t means writes will +// not time out. +func (c *Conn) SetWriteDeadline(t time.Time) error { + c.writeDeadline = t + return nil +} + +// Read methods + +func (c *Conn) advanceFrame() (int, error) { + // 1. Skip remainder of previous frame. + + if c.readRemaining > 0 { + if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil { + return noFrame, err + } + } + + // 2. Read and parse first two bytes of frame header. + + p, err := c.read(2) + if err != nil { + return noFrame, err + } + + final := p[0]&finalBit != 0 + frameType := int(p[0] & 0xf) + mask := p[1]&maskBit != 0 + c.setReadRemaining(int64(p[1] & 0x7f)) + + c.readDecompress = false + if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 { + c.readDecompress = true + p[0] &^= rsv1Bit + } + + if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 { + return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16)) + } + + switch frameType { + case CloseMessage, PingMessage, PongMessage: + if c.readRemaining > maxControlFramePayloadSize { + return noFrame, c.handleProtocolError("control frame length > 125") + } + if !final { + return noFrame, c.handleProtocolError("control frame not final") + } + case TextMessage, BinaryMessage: + if !c.readFinal { + return noFrame, c.handleProtocolError("message start before final message frame") + } + c.readFinal = final + case continuationFrame: + if c.readFinal { + return noFrame, c.handleProtocolError("continuation after final message frame") + } + c.readFinal = final + default: + return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) + } + + // 3. Read and parse frame length as per + // https://tools.ietf.org/html/rfc6455#section-5.2 + // + // The length of the "Payload data", in bytes: if 0-125, that is the payload + // length. + // - If 126, the following 2 bytes interpreted as a 16-bit unsigned + // integer are the payload length. + // - If 127, the following 8 bytes interpreted as + // a 64-bit unsigned integer (the most significant bit MUST be 0) are the + // payload length. Multibyte length quantities are expressed in network byte + // order. + + switch c.readRemaining { + case 126: + p, err := c.read(2) + if err != nil { + return noFrame, err + } + + if err := c.setReadRemaining(int64(binary.BigEndian.Uint16(p))); err != nil { + return noFrame, err + } + case 127: + p, err := c.read(8) + if err != nil { + return noFrame, err + } + + if err := c.setReadRemaining(int64(binary.BigEndian.Uint64(p))); err != nil { + return noFrame, err + } + } + + // 4. Handle frame masking. + + if mask != c.isServer { + return noFrame, c.handleProtocolError("incorrect mask flag") + } + + if mask { + c.readMaskPos = 0 + p, err := c.read(len(c.readMaskKey)) + if err != nil { + return noFrame, err + } + copy(c.readMaskKey[:], p) + } + + // 5. For text and binary messages, enforce read limit and return. + + if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { + + c.readLength += c.readRemaining + // Don't allow readLength to overflow in the presence of a large readRemaining + // counter. + if c.readLength < 0 { + return noFrame, ErrReadLimit + } + + if c.readLimit > 0 && c.readLength > c.readLimit { + c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) + return noFrame, ErrReadLimit + } + + return frameType, nil + } + + // 6. Read control frame payload. + + var payload []byte + if c.readRemaining > 0 { + payload, err = c.read(int(c.readRemaining)) + c.setReadRemaining(0) + if err != nil { + return noFrame, err + } + if c.isServer { + maskBytes(c.readMaskKey, 0, payload) + } + } + + // 7. Process control frame payload. + + switch frameType { + case PongMessage: + if err := c.handlePong(string(payload)); err != nil { + return noFrame, err + } + case PingMessage: + if err := c.handlePing(string(payload)); err != nil { + return noFrame, err + } + case CloseMessage: + closeCode := CloseNoStatusReceived + closeText := "" + if len(payload) >= 2 { + closeCode = int(binary.BigEndian.Uint16(payload)) + if !isValidReceivedCloseCode(closeCode) { + return noFrame, c.handleProtocolError("invalid close code") + } + closeText = string(payload[2:]) + if !utf8.ValidString(closeText) { + return noFrame, c.handleProtocolError("invalid utf8 payload in close frame") + } + } + if err := c.handleClose(closeCode, closeText); err != nil { + return noFrame, err + } + return noFrame, &CloseError{Code: closeCode, Text: closeText} + } + + return frameType, nil +} + +func (c *Conn) handleProtocolError(message string) error { + c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait)) + return errors.New("websocket: " + message) +} + +// NextReader returns the next data message received from the peer. The +// returned messageType is either TextMessage or BinaryMessage. +// +// There can be at most one open reader on a connection. NextReader discards +// the previous message if the application has not already consumed it. +// +// Applications must break out of the application's read loop when this method +// returns a non-nil error value. Errors returned from this method are +// permanent. Once this method returns a non-nil error, all subsequent calls to +// this method return the same error. +func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { + // Close previous reader, only relevant for decompression. + if c.reader != nil { + c.reader.Close() + c.reader = nil + } + + c.messageReader = nil + c.readLength = 0 + + for c.readErr == nil { + frameType, err := c.advanceFrame() + if err != nil { + c.readErr = hideTempErr(err) + break + } + + if frameType == TextMessage || frameType == BinaryMessage { + c.messageReader = &messageReader{c} + c.reader = c.messageReader + if c.readDecompress { + c.reader = c.newDecompressionReader(c.reader) + } + return frameType, c.reader, nil + } + } + + // Applications that do handle the error returned from this method spin in + // tight loop on connection failure. To help application developers detect + // this error, panic on repeated reads to the failed connection. + c.readErrCount++ + if c.readErrCount >= 1000 { + panic("repeated read on failed websocket connection") + } + + return noFrame, nil, c.readErr +} + +type messageReader struct{ c *Conn } + +func (r *messageReader) Read(b []byte) (int, error) { + c := r.c + if c.messageReader != r { + return 0, io.EOF + } + + for c.readErr == nil { + + if c.readRemaining > 0 { + if int64(len(b)) > c.readRemaining { + b = b[:c.readRemaining] + } + n, err := c.br.Read(b) + c.readErr = hideTempErr(err) + if c.isServer { + c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) + } + rem := c.readRemaining + rem -= int64(n) + c.setReadRemaining(rem) + if c.readRemaining > 0 && c.readErr == io.EOF { + c.readErr = errUnexpectedEOF + } + return n, c.readErr + } + + if c.readFinal { + c.messageReader = nil + return 0, io.EOF + } + + frameType, err := c.advanceFrame() + switch { + case err != nil: + c.readErr = hideTempErr(err) + case frameType == TextMessage || frameType == BinaryMessage: + c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") + } + } + + err := c.readErr + if err == io.EOF && c.messageReader == r { + err = errUnexpectedEOF + } + return 0, err +} + +func (r *messageReader) Close() error { + return nil +} + +// ReadMessage is a helper method for getting a reader using NextReader and +// reading from that reader to a buffer. +func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { + var r io.Reader + messageType, r, err = c.NextReader() + if err != nil { + return messageType, nil, err + } + p, err = ioutil.ReadAll(r) + return messageType, p, err +} + +// SetReadDeadline sets the read deadline on the underlying network connection. +// After a read has timed out, the websocket connection state is corrupt and +// all future reads will return an error. A zero value for t means reads will +// not time out. +func (c *Conn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +// SetReadLimit sets the maximum size in bytes for a message read from the peer. If a +// message exceeds the limit, the connection sends a close message to the peer +// and returns ErrReadLimit to the application. +func (c *Conn) SetReadLimit(limit int64) { + c.readLimit = limit +} + +// CloseHandler returns the current close handler +func (c *Conn) CloseHandler() func(code int, text string) error { + return c.handleClose +} + +// SetCloseHandler sets the handler for close messages received from the peer. +// The code argument to h is the received close code or CloseNoStatusReceived +// if the close message is empty. The default close handler sends a close +// message back to the peer. +// +// The handler function is called from the NextReader, ReadMessage and message +// reader Read methods. The application must read the connection to process +// close messages as described in the section on Control Messages above. +// +// The connection read methods return a CloseError when a close message is +// received. Most applications should handle close messages as part of their +// normal error handling. Applications should only set a close handler when the +// application must perform some action before sending a close message back to +// the peer. +func (c *Conn) SetCloseHandler(h func(code int, text string) error) { + if h == nil { + h = func(code int, text string) error { + message := FormatCloseMessage(code, "") + c.WriteControl(CloseMessage, message, time.Now().Add(writeWait)) + return nil + } + } + c.handleClose = h +} + +// PingHandler returns the current ping handler +func (c *Conn) PingHandler() func(appData string) error { + return c.handlePing +} + +// SetPingHandler sets the handler for ping messages received from the peer. +// The appData argument to h is the PING message application data. The default +// ping handler sends a pong to the peer. +// +// The handler function is called from the NextReader, ReadMessage and message +// reader Read methods. The application must read the connection to process +// ping messages as described in the section on Control Messages above. +func (c *Conn) SetPingHandler(h func(appData string) error) { + if h == nil { + h = func(message string) error { + err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) + if err == ErrCloseSent { + return nil + } else if e, ok := err.(net.Error); ok && e.Temporary() { + return nil + } + return err + } + } + c.handlePing = h +} + +// PongHandler returns the current pong handler +func (c *Conn) PongHandler() func(appData string) error { + return c.handlePong +} + +// SetPongHandler sets the handler for pong messages received from the peer. +// The appData argument to h is the PONG message application data. The default +// pong handler does nothing. +// +// The handler function is called from the NextReader, ReadMessage and message +// reader Read methods. The application must read the connection to process +// pong messages as described in the section on Control Messages above. +func (c *Conn) SetPongHandler(h func(appData string) error) { + if h == nil { + h = func(string) error { return nil } + } + c.handlePong = h +} + +// UnderlyingConn returns the internal net.Conn. This can be used to further +// modifications to connection specific flags. +func (c *Conn) UnderlyingConn() net.Conn { + return c.conn +} + +// EnableWriteCompression enables and disables write compression of +// subsequent text and binary messages. This function is a noop if +// compression was not negotiated with the peer. +func (c *Conn) EnableWriteCompression(enable bool) { + c.enableWriteCompression = enable +} + +// SetCompressionLevel sets the flate compression level for subsequent text and +// binary messages. This function is a noop if compression was not negotiated +// with the peer. See the compress/flate package for a description of +// compression levels. +func (c *Conn) SetCompressionLevel(level int) error { + if !isValidCompressionLevel(level) { + return errors.New("websocket: invalid compression level") + } + c.compressionLevel = level + return nil +} + +// FormatCloseMessage formats closeCode and text as a WebSocket close message. +// An empty message is returned for code CloseNoStatusReceived. +func FormatCloseMessage(closeCode int, text string) []byte { + if closeCode == CloseNoStatusReceived { + // Return empty message because it's illegal to send + // CloseNoStatusReceived. Return non-nil value in case application + // checks for nil. + return []byte{} + } + buf := make([]byte, 2+len(text)) + binary.BigEndian.PutUint16(buf, uint16(closeCode)) + copy(buf[2:], text) + return buf +} diff --git a/vendor/github.com/gorilla/websocket/conn_write.go b/vendor/github.com/gorilla/websocket/conn_write.go new file mode 100644 index 0000000000..a509a21f87 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn_write.go @@ -0,0 +1,15 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.8 + +package websocket + +import "net" + +func (c *Conn) writeBufs(bufs ...[]byte) error { + b := net.Buffers(bufs) + _, err := b.WriteTo(c.conn) + return err +} diff --git a/vendor/github.com/gorilla/websocket/conn_write_legacy.go b/vendor/github.com/gorilla/websocket/conn_write_legacy.go new file mode 100644 index 0000000000..37edaff5a5 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn_write_legacy.go @@ -0,0 +1,18 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.8 + +package websocket + +func (c *Conn) writeBufs(bufs ...[]byte) error { + for _, buf := range bufs { + if len(buf) > 0 { + if _, err := c.conn.Write(buf); err != nil { + return err + } + } + } + return nil +} diff --git a/vendor/github.com/gorilla/websocket/doc.go b/vendor/github.com/gorilla/websocket/doc.go new file mode 100644 index 0000000000..8db0cef95a --- /dev/null +++ b/vendor/github.com/gorilla/websocket/doc.go @@ -0,0 +1,227 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package websocket implements the WebSocket protocol defined in RFC 6455. +// +// Overview +// +// The Conn type represents a WebSocket connection. A server application calls +// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn: +// +// var upgrader = websocket.Upgrader{ +// ReadBufferSize: 1024, +// WriteBufferSize: 1024, +// } +// +// func handler(w http.ResponseWriter, r *http.Request) { +// conn, err := upgrader.Upgrade(w, r, nil) +// if err != nil { +// log.Println(err) +// return +// } +// ... Use conn to send and receive messages. +// } +// +// Call the connection's WriteMessage and ReadMessage methods to send and +// receive messages as a slice of bytes. This snippet of code shows how to echo +// messages using these methods: +// +// for { +// messageType, p, err := conn.ReadMessage() +// if err != nil { +// log.Println(err) +// return +// } +// if err := conn.WriteMessage(messageType, p); err != nil { +// log.Println(err) +// return +// } +// } +// +// In above snippet of code, p is a []byte and messageType is an int with value +// websocket.BinaryMessage or websocket.TextMessage. +// +// An application can also send and receive messages using the io.WriteCloser +// and io.Reader interfaces. To send a message, call the connection NextWriter +// method to get an io.WriteCloser, write the message to the writer and close +// the writer when done. To receive a message, call the connection NextReader +// method to get an io.Reader and read until io.EOF is returned. This snippet +// shows how to echo messages using the NextWriter and NextReader methods: +// +// for { +// messageType, r, err := conn.NextReader() +// if err != nil { +// return +// } +// w, err := conn.NextWriter(messageType) +// if err != nil { +// return err +// } +// if _, err := io.Copy(w, r); err != nil { +// return err +// } +// if err := w.Close(); err != nil { +// return err +// } +// } +// +// Data Messages +// +// The WebSocket protocol distinguishes between text and binary data messages. +// Text messages are interpreted as UTF-8 encoded text. The interpretation of +// binary messages is left to the application. +// +// This package uses the TextMessage and BinaryMessage integer constants to +// identify the two data message types. The ReadMessage and NextReader methods +// return the type of the received message. The messageType argument to the +// WriteMessage and NextWriter methods specifies the type of a sent message. +// +// It is the application's responsibility to ensure that text messages are +// valid UTF-8 encoded text. +// +// Control Messages +// +// The WebSocket protocol defines three types of control messages: close, ping +// and pong. Call the connection WriteControl, WriteMessage or NextWriter +// methods to send a control message to the peer. +// +// Connections handle received close messages by calling the handler function +// set with the SetCloseHandler method and by returning a *CloseError from the +// NextReader, ReadMessage or the message Read method. The default close +// handler sends a close message to the peer. +// +// Connections handle received ping messages by calling the handler function +// set with the SetPingHandler method. The default ping handler sends a pong +// message to the peer. +// +// Connections handle received pong messages by calling the handler function +// set with the SetPongHandler method. The default pong handler does nothing. +// If an application sends ping messages, then the application should set a +// pong handler to receive the corresponding pong. +// +// The control message handler functions are called from the NextReader, +// ReadMessage and message reader Read methods. The default close and ping +// handlers can block these methods for a short time when the handler writes to +// the connection. +// +// The application must read the connection to process close, ping and pong +// messages sent from the peer. If the application is not otherwise interested +// in messages from the peer, then the application should start a goroutine to +// read and discard messages from the peer. A simple example is: +// +// func readLoop(c *websocket.Conn) { +// for { +// if _, _, err := c.NextReader(); err != nil { +// c.Close() +// break +// } +// } +// } +// +// Concurrency +// +// Connections support one concurrent reader and one concurrent writer. +// +// Applications are responsible for ensuring that no more than one goroutine +// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, +// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and +// that no more than one goroutine calls the read methods (NextReader, +// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) +// concurrently. +// +// The Close and WriteControl methods can be called concurrently with all other +// methods. +// +// Origin Considerations +// +// Web browsers allow Javascript applications to open a WebSocket connection to +// any host. It's up to the server to enforce an origin policy using the Origin +// request header sent by the browser. +// +// The Upgrader calls the function specified in the CheckOrigin field to check +// the origin. If the CheckOrigin function returns false, then the Upgrade +// method fails the WebSocket handshake with HTTP status 403. +// +// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail +// the handshake if the Origin request header is present and the Origin host is +// not equal to the Host request header. +// +// The deprecated package-level Upgrade function does not perform origin +// checking. The application is responsible for checking the Origin header +// before calling the Upgrade function. +// +// Buffers +// +// Connections buffer network input and output to reduce the number +// of system calls when reading or writing messages. +// +// Write buffers are also used for constructing WebSocket frames. See RFC 6455, +// Section 5 for a discussion of message framing. A WebSocket frame header is +// written to the network each time a write buffer is flushed to the network. +// Decreasing the size of the write buffer can increase the amount of framing +// overhead on the connection. +// +// The buffer sizes in bytes are specified by the ReadBufferSize and +// WriteBufferSize fields in the Dialer and Upgrader. The Dialer uses a default +// size of 4096 when a buffer size field is set to zero. The Upgrader reuses +// buffers created by the HTTP server when a buffer size field is set to zero. +// The HTTP server buffers have a size of 4096 at the time of this writing. +// +// The buffer sizes do not limit the size of a message that can be read or +// written by a connection. +// +// Buffers are held for the lifetime of the connection by default. If the +// Dialer or Upgrader WriteBufferPool field is set, then a connection holds the +// write buffer only when writing a message. +// +// Applications should tune the buffer sizes to balance memory use and +// performance. Increasing the buffer size uses more memory, but can reduce the +// number of system calls to read or write the network. In the case of writing, +// increasing the buffer size can reduce the number of frame headers written to +// the network. +// +// Some guidelines for setting buffer parameters are: +// +// Limit the buffer sizes to the maximum expected message size. Buffers larger +// than the largest message do not provide any benefit. +// +// Depending on the distribution of message sizes, setting the buffer size to +// a value less than the maximum expected message size can greatly reduce memory +// use with a small impact on performance. Here's an example: If 99% of the +// messages are smaller than 256 bytes and the maximum message size is 512 +// bytes, then a buffer size of 256 bytes will result in 1.01 more system calls +// than a buffer size of 512 bytes. The memory savings is 50%. +// +// A write buffer pool is useful when the application has a modest number +// writes over a large number of connections. when buffers are pooled, a larger +// buffer size has a reduced impact on total memory use and has the benefit of +// reducing system calls and frame overhead. +// +// Compression EXPERIMENTAL +// +// Per message compression extensions (RFC 7692) are experimentally supported +// by this package in a limited capacity. Setting the EnableCompression option +// to true in Dialer or Upgrader will attempt to negotiate per message deflate +// support. +// +// var upgrader = websocket.Upgrader{ +// EnableCompression: true, +// } +// +// If compression was successfully negotiated with the connection's peer, any +// message received in compressed form will be automatically decompressed. +// All Read methods will return uncompressed bytes. +// +// Per message compression of messages written to a connection can be enabled +// or disabled by calling the corresponding Conn method: +// +// conn.EnableWriteCompression(false) +// +// Currently this package does not support compression with "context takeover". +// This means that messages must be compressed and decompressed in isolation, +// without retaining sliding window or dictionary state across messages. For +// more details refer to RFC 7692. +// +// Use of compression is experimental and may result in decreased performance. +package websocket diff --git a/vendor/github.com/gorilla/websocket/go.mod b/vendor/github.com/gorilla/websocket/go.mod new file mode 100644 index 0000000000..1a7afd5028 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/go.mod @@ -0,0 +1,3 @@ +module github.com/gorilla/websocket + +go 1.12 diff --git a/vendor/github.com/gorilla/websocket/go.sum b/vendor/github.com/gorilla/websocket/go.sum new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vendor/github.com/gorilla/websocket/join.go b/vendor/github.com/gorilla/websocket/join.go new file mode 100644 index 0000000000..c64f8c8290 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/join.go @@ -0,0 +1,42 @@ +// Copyright 2019 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "io" + "strings" +) + +// JoinMessages concatenates received messages to create a single io.Reader. +// The string term is appended to each message. The returned reader does not +// support concurrent calls to the Read method. +func JoinMessages(c *Conn, term string) io.Reader { + return &joinReader{c: c, term: term} +} + +type joinReader struct { + c *Conn + term string + r io.Reader +} + +func (r *joinReader) Read(p []byte) (int, error) { + if r.r == nil { + var err error + _, r.r, err = r.c.NextReader() + if err != nil { + return 0, err + } + if r.term != "" { + r.r = io.MultiReader(r.r, strings.NewReader(r.term)) + } + } + n, err := r.r.Read(p) + if err == io.EOF { + err = nil + r.r = nil + } + return n, err +} diff --git a/vendor/github.com/gorilla/websocket/json.go b/vendor/github.com/gorilla/websocket/json.go new file mode 100644 index 0000000000..dc2c1f6415 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/json.go @@ -0,0 +1,60 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "encoding/json" + "io" +) + +// WriteJSON writes the JSON encoding of v as a message. +// +// Deprecated: Use c.WriteJSON instead. +func WriteJSON(c *Conn, v interface{}) error { + return c.WriteJSON(v) +} + +// WriteJSON writes the JSON encoding of v as a message. +// +// See the documentation for encoding/json Marshal for details about the +// conversion of Go values to JSON. +func (c *Conn) WriteJSON(v interface{}) error { + w, err := c.NextWriter(TextMessage) + if err != nil { + return err + } + err1 := json.NewEncoder(w).Encode(v) + err2 := w.Close() + if err1 != nil { + return err1 + } + return err2 +} + +// ReadJSON reads the next JSON-encoded message from the connection and stores +// it in the value pointed to by v. +// +// Deprecated: Use c.ReadJSON instead. +func ReadJSON(c *Conn, v interface{}) error { + return c.ReadJSON(v) +} + +// ReadJSON reads the next JSON-encoded message from the connection and stores +// it in the value pointed to by v. +// +// See the documentation for the encoding/json Unmarshal function for details +// about the conversion of JSON to a Go value. +func (c *Conn) ReadJSON(v interface{}) error { + _, r, err := c.NextReader() + if err != nil { + return err + } + err = json.NewDecoder(r).Decode(v) + if err == io.EOF { + // One value is expected in the message. + err = io.ErrUnexpectedEOF + } + return err +} diff --git a/vendor/github.com/gorilla/websocket/mask.go b/vendor/github.com/gorilla/websocket/mask.go new file mode 100644 index 0000000000..577fce9efd --- /dev/null +++ b/vendor/github.com/gorilla/websocket/mask.go @@ -0,0 +1,54 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +// +build !appengine + +package websocket + +import "unsafe" + +const wordSize = int(unsafe.Sizeof(uintptr(0))) + +func maskBytes(key [4]byte, pos int, b []byte) int { + // Mask one byte at a time for small buffers. + if len(b) < 2*wordSize { + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + return pos & 3 + } + + // Mask one byte at a time to word boundary. + if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { + n = wordSize - n + for i := range b[:n] { + b[i] ^= key[pos&3] + pos++ + } + b = b[n:] + } + + // Create aligned word size key. + var k [wordSize]byte + for i := range k { + k[i] = key[(pos+i)&3] + } + kw := *(*uintptr)(unsafe.Pointer(&k)) + + // Mask one word at a time. + n := (len(b) / wordSize) * wordSize + for i := 0; i < n; i += wordSize { + *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw + } + + // Mask one byte at a time for remaining bytes. + b = b[n:] + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + + return pos & 3 +} diff --git a/vendor/github.com/gorilla/websocket/mask_safe.go b/vendor/github.com/gorilla/websocket/mask_safe.go new file mode 100644 index 0000000000..2aac060e52 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/mask_safe.go @@ -0,0 +1,15 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +// +build appengine + +package websocket + +func maskBytes(key [4]byte, pos int, b []byte) int { + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + return pos & 3 +} diff --git a/vendor/github.com/gorilla/websocket/prepared.go b/vendor/github.com/gorilla/websocket/prepared.go new file mode 100644 index 0000000000..c854225e96 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/prepared.go @@ -0,0 +1,102 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bytes" + "net" + "sync" + "time" +) + +// PreparedMessage caches on the wire representations of a message payload. +// Use PreparedMessage to efficiently send a message payload to multiple +// connections. PreparedMessage is especially useful when compression is used +// because the CPU and memory expensive compression operation can be executed +// once for a given set of compression options. +type PreparedMessage struct { + messageType int + data []byte + mu sync.Mutex + frames map[prepareKey]*preparedFrame +} + +// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. +type prepareKey struct { + isServer bool + compress bool + compressionLevel int +} + +// preparedFrame contains data in wire representation. +type preparedFrame struct { + once sync.Once + data []byte +} + +// NewPreparedMessage returns an initialized PreparedMessage. You can then send +// it to connection using WritePreparedMessage method. Valid wire +// representation will be calculated lazily only once for a set of current +// connection options. +func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { + pm := &PreparedMessage{ + messageType: messageType, + frames: make(map[prepareKey]*preparedFrame), + data: data, + } + + // Prepare a plain server frame. + _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) + if err != nil { + return nil, err + } + + // To protect against caller modifying the data argument, remember the data + // copied to the plain server frame. + pm.data = frameData[len(frameData)-len(data):] + return pm, nil +} + +func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { + pm.mu.Lock() + frame, ok := pm.frames[key] + if !ok { + frame = &preparedFrame{} + pm.frames[key] = frame + } + pm.mu.Unlock() + + var err error + frame.once.Do(func() { + // Prepare a frame using a 'fake' connection. + // TODO: Refactor code in conn.go to allow more direct construction of + // the frame. + mu := make(chan struct{}, 1) + mu <- struct{}{} + var nc prepareConn + c := &Conn{ + conn: &nc, + mu: mu, + isServer: key.isServer, + compressionLevel: key.compressionLevel, + enableWriteCompression: true, + writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), + } + if key.compress { + c.newCompressionWriter = compressNoContextTakeover + } + err = c.WriteMessage(pm.messageType, pm.data) + frame.data = nc.buf.Bytes() + }) + return pm.messageType, frame.data, err +} + +type prepareConn struct { + buf bytes.Buffer + net.Conn +} + +func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } +func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } diff --git a/vendor/github.com/gorilla/websocket/proxy.go b/vendor/github.com/gorilla/websocket/proxy.go new file mode 100644 index 0000000000..e87a8c9f0c --- /dev/null +++ b/vendor/github.com/gorilla/websocket/proxy.go @@ -0,0 +1,77 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "encoding/base64" + "errors" + "net" + "net/http" + "net/url" + "strings" +) + +type netDialerFunc func(network, addr string) (net.Conn, error) + +func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { + return fn(network, addr) +} + +func init() { + proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { + return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil + }) +} + +type httpProxyDialer struct { + proxyURL *url.URL + forwardDial func(network, addr string) (net.Conn, error) +} + +func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) { + hostPort, _ := hostPortNoPort(hpd.proxyURL) + conn, err := hpd.forwardDial(network, hostPort) + if err != nil { + return nil, err + } + + connectHeader := make(http.Header) + if user := hpd.proxyURL.User; user != nil { + proxyUser := user.Username() + if proxyPassword, passwordSet := user.Password(); passwordSet { + credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) + connectHeader.Set("Proxy-Authorization", "Basic "+credential) + } + } + + connectReq := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Opaque: addr}, + Host: addr, + Header: connectHeader, + } + + if err := connectReq.Write(conn); err != nil { + conn.Close() + return nil, err + } + + // Read response. It's OK to use and discard buffered reader here becaue + // the remote server does not speak until spoken to. + br := bufio.NewReader(conn) + resp, err := http.ReadResponse(br, connectReq) + if err != nil { + conn.Close() + return nil, err + } + + if resp.StatusCode != 200 { + conn.Close() + f := strings.SplitN(resp.Status, " ", 2) + return nil, errors.New(f[1]) + } + return conn, nil +} diff --git a/vendor/github.com/gorilla/websocket/server.go b/vendor/github.com/gorilla/websocket/server.go new file mode 100644 index 0000000000..887d558918 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/server.go @@ -0,0 +1,363 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "errors" + "io" + "net/http" + "net/url" + "strings" + "time" +) + +// HandshakeError describes an error with the handshake from the peer. +type HandshakeError struct { + message string +} + +func (e HandshakeError) Error() string { return e.message } + +// Upgrader specifies parameters for upgrading an HTTP connection to a +// WebSocket connection. +type Upgrader struct { + // HandshakeTimeout specifies the duration for the handshake to complete. + HandshakeTimeout time.Duration + + // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer + // size is zero, then buffers allocated by the HTTP server are used. The + // I/O buffer sizes do not limit the size of the messages that can be sent + // or received. + ReadBufferSize, WriteBufferSize int + + // WriteBufferPool is a pool of buffers for write operations. If the value + // is not set, then write buffers are allocated to the connection for the + // lifetime of the connection. + // + // A pool is most useful when the application has a modest volume of writes + // across a large number of connections. + // + // Applications should use a single pool for each unique value of + // WriteBufferSize. + WriteBufferPool BufferPool + + // Subprotocols specifies the server's supported protocols in order of + // preference. If this field is not nil, then the Upgrade method negotiates a + // subprotocol by selecting the first match in this list with a protocol + // requested by the client. If there's no match, then no protocol is + // negotiated (the Sec-Websocket-Protocol header is not included in the + // handshake response). + Subprotocols []string + + // Error specifies the function for generating HTTP error responses. If Error + // is nil, then http.Error is used to generate the HTTP response. + Error func(w http.ResponseWriter, r *http.Request, status int, reason error) + + // CheckOrigin returns true if the request Origin header is acceptable. If + // CheckOrigin is nil, then a safe default is used: return false if the + // Origin request header is present and the origin host is not equal to + // request Host header. + // + // A CheckOrigin function should carefully validate the request origin to + // prevent cross-site request forgery. + CheckOrigin func(r *http.Request) bool + + // EnableCompression specify if the server should attempt to negotiate per + // message compression (RFC 7692). Setting this value to true does not + // guarantee that compression will be supported. Currently only "no context + // takeover" modes are supported. + EnableCompression bool +} + +func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { + err := HandshakeError{reason} + if u.Error != nil { + u.Error(w, r, status, err) + } else { + w.Header().Set("Sec-Websocket-Version", "13") + http.Error(w, http.StatusText(status), status) + } + return nil, err +} + +// checkSameOrigin returns true if the origin is not set or is equal to the request host. +func checkSameOrigin(r *http.Request) bool { + origin := r.Header["Origin"] + if len(origin) == 0 { + return true + } + u, err := url.Parse(origin[0]) + if err != nil { + return false + } + return equalASCIIFold(u.Host, r.Host) +} + +func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { + if u.Subprotocols != nil { + clientProtocols := Subprotocols(r) + for _, serverProtocol := range u.Subprotocols { + for _, clientProtocol := range clientProtocols { + if clientProtocol == serverProtocol { + return clientProtocol + } + } + } + } else if responseHeader != nil { + return responseHeader.Get("Sec-Websocket-Protocol") + } + return "" +} + +// Upgrade upgrades the HTTP server connection to the WebSocket protocol. +// +// The responseHeader is included in the response to the client's upgrade +// request. Use the responseHeader to specify cookies (Set-Cookie) and the +// application negotiated subprotocol (Sec-WebSocket-Protocol). +// +// If the upgrade fails, then Upgrade replies to the client with an HTTP error +// response. +func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { + const badHandshake = "websocket: the client is not using the websocket protocol: " + + if !tokenListContainsValue(r.Header, "Connection", "upgrade") { + return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header") + } + + if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { + return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header") + } + + if r.Method != "GET" { + return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET") + } + + if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") { + return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header") + } + + if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok { + return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported") + } + + checkOrigin := u.CheckOrigin + if checkOrigin == nil { + checkOrigin = checkSameOrigin + } + if !checkOrigin(r) { + return u.returnError(w, r, http.StatusForbidden, "websocket: request origin not allowed by Upgrader.CheckOrigin") + } + + challengeKey := r.Header.Get("Sec-Websocket-Key") + if challengeKey == "" { + return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank") + } + + subprotocol := u.selectSubprotocol(r, responseHeader) + + // Negotiate PMCE + var compress bool + if u.EnableCompression { + for _, ext := range parseExtensions(r.Header) { + if ext[""] != "permessage-deflate" { + continue + } + compress = true + break + } + } + + h, ok := w.(http.Hijacker) + if !ok { + return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") + } + var brw *bufio.ReadWriter + netConn, brw, err := h.Hijack() + if err != nil { + return u.returnError(w, r, http.StatusInternalServerError, err.Error()) + } + + if brw.Reader.Buffered() > 0 { + netConn.Close() + return nil, errors.New("websocket: client sent data before handshake is complete") + } + + var br *bufio.Reader + if u.ReadBufferSize == 0 && bufioReaderSize(netConn, brw.Reader) > 256 { + // Reuse hijacked buffered reader as connection reader. + br = brw.Reader + } + + buf := bufioWriterBuffer(netConn, brw.Writer) + + var writeBuf []byte + if u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 { + // Reuse hijacked write buffer as connection buffer. + writeBuf = buf + } + + c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf) + c.subprotocol = subprotocol + + if compress { + c.newCompressionWriter = compressNoContextTakeover + c.newDecompressionReader = decompressNoContextTakeover + } + + // Use larger of hijacked buffer and connection write buffer for header. + p := buf + if len(c.writeBuf) > len(p) { + p = c.writeBuf + } + p = p[:0] + + p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) + p = append(p, computeAcceptKey(challengeKey)...) + p = append(p, "\r\n"...) + if c.subprotocol != "" { + p = append(p, "Sec-WebSocket-Protocol: "...) + p = append(p, c.subprotocol...) + p = append(p, "\r\n"...) + } + if compress { + p = append(p, "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...) + } + for k, vs := range responseHeader { + if k == "Sec-Websocket-Protocol" { + continue + } + for _, v := range vs { + p = append(p, k...) + p = append(p, ": "...) + for i := 0; i < len(v); i++ { + b := v[i] + if b <= 31 { + // prevent response splitting. + b = ' ' + } + p = append(p, b) + } + p = append(p, "\r\n"...) + } + } + p = append(p, "\r\n"...) + + // Clear deadlines set by HTTP server. + netConn.SetDeadline(time.Time{}) + + if u.HandshakeTimeout > 0 { + netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) + } + if _, err = netConn.Write(p); err != nil { + netConn.Close() + return nil, err + } + if u.HandshakeTimeout > 0 { + netConn.SetWriteDeadline(time.Time{}) + } + + return c, nil +} + +// Upgrade upgrades the HTTP server connection to the WebSocket protocol. +// +// Deprecated: Use websocket.Upgrader instead. +// +// Upgrade does not perform origin checking. The application is responsible for +// checking the Origin header before calling Upgrade. An example implementation +// of the same origin policy check is: +// +// if req.Header.Get("Origin") != "http://"+req.Host { +// http.Error(w, "Origin not allowed", http.StatusForbidden) +// return +// } +// +// If the endpoint supports subprotocols, then the application is responsible +// for negotiating the protocol used on the connection. Use the Subprotocols() +// function to get the subprotocols requested by the client. Use the +// Sec-Websocket-Protocol response header to specify the subprotocol selected +// by the application. +// +// The responseHeader is included in the response to the client's upgrade +// request. Use the responseHeader to specify cookies (Set-Cookie) and the +// negotiated subprotocol (Sec-Websocket-Protocol). +// +// The connection buffers IO to the underlying network connection. The +// readBufSize and writeBufSize parameters specify the size of the buffers to +// use. Messages can be larger than the buffers. +// +// If the request is not a valid WebSocket handshake, then Upgrade returns an +// error of type HandshakeError. Applications should handle this error by +// replying to the client with an HTTP error response. +func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { + u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} + u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { + // don't return errors to maintain backwards compatibility + } + u.CheckOrigin = func(r *http.Request) bool { + // allow all connections by default + return true + } + return u.Upgrade(w, r, responseHeader) +} + +// Subprotocols returns the subprotocols requested by the client in the +// Sec-Websocket-Protocol header. +func Subprotocols(r *http.Request) []string { + h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) + if h == "" { + return nil + } + protocols := strings.Split(h, ",") + for i := range protocols { + protocols[i] = strings.TrimSpace(protocols[i]) + } + return protocols +} + +// IsWebSocketUpgrade returns true if the client requested upgrade to the +// WebSocket protocol. +func IsWebSocketUpgrade(r *http.Request) bool { + return tokenListContainsValue(r.Header, "Connection", "upgrade") && + tokenListContainsValue(r.Header, "Upgrade", "websocket") +} + +// bufioReaderSize size returns the size of a bufio.Reader. +func bufioReaderSize(originalReader io.Reader, br *bufio.Reader) int { + // This code assumes that peek on a reset reader returns + // bufio.Reader.buf[:0]. + // TODO: Use bufio.Reader.Size() after Go 1.10 + br.Reset(originalReader) + if p, err := br.Peek(0); err == nil { + return cap(p) + } + return 0 +} + +// writeHook is an io.Writer that records the last slice passed to it vio +// io.Writer.Write. +type writeHook struct { + p []byte +} + +func (wh *writeHook) Write(p []byte) (int, error) { + wh.p = p + return len(p), nil +} + +// bufioWriterBuffer grabs the buffer from a bufio.Writer. +func bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte { + // This code assumes that bufio.Writer.buf[:1] is passed to the + // bufio.Writer's underlying writer. + var wh writeHook + bw.Reset(&wh) + bw.WriteByte(0) + bw.Flush() + + bw.Reset(originalWriter) + + return wh.p[:cap(wh.p)] +} diff --git a/vendor/github.com/gorilla/websocket/trace.go b/vendor/github.com/gorilla/websocket/trace.go new file mode 100644 index 0000000000..834f122a00 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/trace.go @@ -0,0 +1,19 @@ +// +build go1.8 + +package websocket + +import ( + "crypto/tls" + "net/http/httptrace" +) + +func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error { + if trace.TLSHandshakeStart != nil { + trace.TLSHandshakeStart() + } + err := doHandshake(tlsConn, cfg) + if trace.TLSHandshakeDone != nil { + trace.TLSHandshakeDone(tlsConn.ConnectionState(), err) + } + return err +} diff --git a/vendor/github.com/gorilla/websocket/trace_17.go b/vendor/github.com/gorilla/websocket/trace_17.go new file mode 100644 index 0000000000..77d05a0b57 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/trace_17.go @@ -0,0 +1,12 @@ +// +build !go1.8 + +package websocket + +import ( + "crypto/tls" + "net/http/httptrace" +) + +func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error { + return doHandshake(tlsConn, cfg) +} diff --git a/vendor/github.com/gorilla/websocket/util.go b/vendor/github.com/gorilla/websocket/util.go new file mode 100644 index 0000000000..7bf2f66c67 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/util.go @@ -0,0 +1,283 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "crypto/rand" + "crypto/sha1" + "encoding/base64" + "io" + "net/http" + "strings" + "unicode/utf8" +) + +var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") + +func computeAcceptKey(challengeKey string) string { + h := sha1.New() + h.Write([]byte(challengeKey)) + h.Write(keyGUID) + return base64.StdEncoding.EncodeToString(h.Sum(nil)) +} + +func generateChallengeKey() (string, error) { + p := make([]byte, 16) + if _, err := io.ReadFull(rand.Reader, p); err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(p), nil +} + +// Token octets per RFC 2616. +var isTokenOctet = [256]bool{ + '!': true, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '*': true, + '+': true, + '-': true, + '.': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'W': true, + 'V': true, + 'X': true, + 'Y': true, + 'Z': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '|': true, + '~': true, +} + +// skipSpace returns a slice of the string s with all leading RFC 2616 linear +// whitespace removed. +func skipSpace(s string) (rest string) { + i := 0 + for ; i < len(s); i++ { + if b := s[i]; b != ' ' && b != '\t' { + break + } + } + return s[i:] +} + +// nextToken returns the leading RFC 2616 token of s and the string following +// the token. +func nextToken(s string) (token, rest string) { + i := 0 + for ; i < len(s); i++ { + if !isTokenOctet[s[i]] { + break + } + } + return s[:i], s[i:] +} + +// nextTokenOrQuoted returns the leading token or quoted string per RFC 2616 +// and the string following the token or quoted string. +func nextTokenOrQuoted(s string) (value string, rest string) { + if !strings.HasPrefix(s, "\"") { + return nextToken(s) + } + s = s[1:] + for i := 0; i < len(s); i++ { + switch s[i] { + case '"': + return s[:i], s[i+1:] + case '\\': + p := make([]byte, len(s)-1) + j := copy(p, s[:i]) + escape := true + for i = i + 1; i < len(s); i++ { + b := s[i] + switch { + case escape: + escape = false + p[j] = b + j++ + case b == '\\': + escape = true + case b == '"': + return string(p[:j]), s[i+1:] + default: + p[j] = b + j++ + } + } + return "", "" + } + } + return "", "" +} + +// equalASCIIFold returns true if s is equal to t with ASCII case folding as +// defined in RFC 4790. +func equalASCIIFold(s, t string) bool { + for s != "" && t != "" { + sr, size := utf8.DecodeRuneInString(s) + s = s[size:] + tr, size := utf8.DecodeRuneInString(t) + t = t[size:] + if sr == tr { + continue + } + if 'A' <= sr && sr <= 'Z' { + sr = sr + 'a' - 'A' + } + if 'A' <= tr && tr <= 'Z' { + tr = tr + 'a' - 'A' + } + if sr != tr { + return false + } + } + return s == t +} + +// tokenListContainsValue returns true if the 1#token header with the given +// name contains a token equal to value with ASCII case folding. +func tokenListContainsValue(header http.Header, name string, value string) bool { +headers: + for _, s := range header[name] { + for { + var t string + t, s = nextToken(skipSpace(s)) + if t == "" { + continue headers + } + s = skipSpace(s) + if s != "" && s[0] != ',' { + continue headers + } + if equalASCIIFold(t, value) { + return true + } + if s == "" { + continue headers + } + s = s[1:] + } + } + return false +} + +// parseExtensions parses WebSocket extensions from a header. +func parseExtensions(header http.Header) []map[string]string { + // From RFC 6455: + // + // Sec-WebSocket-Extensions = extension-list + // extension-list = 1#extension + // extension = extension-token *( ";" extension-param ) + // extension-token = registered-token + // registered-token = token + // extension-param = token [ "=" (token | quoted-string) ] + // ;When using the quoted-string syntax variant, the value + // ;after quoted-string unescaping MUST conform to the + // ;'token' ABNF. + + var result []map[string]string +headers: + for _, s := range header["Sec-Websocket-Extensions"] { + for { + var t string + t, s = nextToken(skipSpace(s)) + if t == "" { + continue headers + } + ext := map[string]string{"": t} + for { + s = skipSpace(s) + if !strings.HasPrefix(s, ";") { + break + } + var k string + k, s = nextToken(skipSpace(s[1:])) + if k == "" { + continue headers + } + s = skipSpace(s) + var v string + if strings.HasPrefix(s, "=") { + v, s = nextTokenOrQuoted(skipSpace(s[1:])) + s = skipSpace(s) + } + if s != "" && s[0] != ',' && s[0] != ';' { + continue headers + } + ext[k] = v + } + if s != "" && s[0] != ',' { + continue headers + } + result = append(result, ext) + if s == "" { + continue headers + } + s = s[1:] + } + } + return result +} diff --git a/vendor/github.com/gorilla/websocket/x_net_proxy.go b/vendor/github.com/gorilla/websocket/x_net_proxy.go new file mode 100644 index 0000000000..2e668f6b88 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/x_net_proxy.go @@ -0,0 +1,473 @@ +// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. +//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy + +// Package proxy provides support for a variety of protocols to proxy network +// data. +// + +package websocket + +import ( + "errors" + "io" + "net" + "net/url" + "os" + "strconv" + "strings" + "sync" +) + +type proxy_direct struct{} + +// Direct is a direct proxy: one that makes network connections directly. +var proxy_Direct = proxy_direct{} + +func (proxy_direct) Dial(network, addr string) (net.Conn, error) { + return net.Dial(network, addr) +} + +// A PerHost directs connections to a default Dialer unless the host name +// requested matches one of a number of exceptions. +type proxy_PerHost struct { + def, bypass proxy_Dialer + + bypassNetworks []*net.IPNet + bypassIPs []net.IP + bypassZones []string + bypassHosts []string +} + +// NewPerHost returns a PerHost Dialer that directs connections to either +// defaultDialer or bypass, depending on whether the connection matches one of +// the configured rules. +func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost { + return &proxy_PerHost{ + def: defaultDialer, + bypass: bypass, + } +} + +// Dial connects to the address addr on the given network through either +// defaultDialer or bypass. +func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) { + host, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + + return p.dialerForRequest(host).Dial(network, addr) +} + +func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer { + if ip := net.ParseIP(host); ip != nil { + for _, net := range p.bypassNetworks { + if net.Contains(ip) { + return p.bypass + } + } + for _, bypassIP := range p.bypassIPs { + if bypassIP.Equal(ip) { + return p.bypass + } + } + return p.def + } + + for _, zone := range p.bypassZones { + if strings.HasSuffix(host, zone) { + return p.bypass + } + if host == zone[1:] { + // For a zone ".example.com", we match "example.com" + // too. + return p.bypass + } + } + for _, bypassHost := range p.bypassHosts { + if bypassHost == host { + return p.bypass + } + } + return p.def +} + +// AddFromString parses a string that contains comma-separated values +// specifying hosts that should use the bypass proxy. Each value is either an +// IP address, a CIDR range, a zone (*.example.com) or a host name +// (localhost). A best effort is made to parse the string and errors are +// ignored. +func (p *proxy_PerHost) AddFromString(s string) { + hosts := strings.Split(s, ",") + for _, host := range hosts { + host = strings.TrimSpace(host) + if len(host) == 0 { + continue + } + if strings.Contains(host, "/") { + // We assume that it's a CIDR address like 127.0.0.0/8 + if _, net, err := net.ParseCIDR(host); err == nil { + p.AddNetwork(net) + } + continue + } + if ip := net.ParseIP(host); ip != nil { + p.AddIP(ip) + continue + } + if strings.HasPrefix(host, "*.") { + p.AddZone(host[1:]) + continue + } + p.AddHost(host) + } +} + +// AddIP specifies an IP address that will use the bypass proxy. Note that +// this will only take effect if a literal IP address is dialed. A connection +// to a named host will never match an IP. +func (p *proxy_PerHost) AddIP(ip net.IP) { + p.bypassIPs = append(p.bypassIPs, ip) +} + +// AddNetwork specifies an IP range that will use the bypass proxy. Note that +// this will only take effect if a literal IP address is dialed. A connection +// to a named host will never match. +func (p *proxy_PerHost) AddNetwork(net *net.IPNet) { + p.bypassNetworks = append(p.bypassNetworks, net) +} + +// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of +// "example.com" matches "example.com" and all of its subdomains. +func (p *proxy_PerHost) AddZone(zone string) { + if strings.HasSuffix(zone, ".") { + zone = zone[:len(zone)-1] + } + if !strings.HasPrefix(zone, ".") { + zone = "." + zone + } + p.bypassZones = append(p.bypassZones, zone) +} + +// AddHost specifies a host name that will use the bypass proxy. +func (p *proxy_PerHost) AddHost(host string) { + if strings.HasSuffix(host, ".") { + host = host[:len(host)-1] + } + p.bypassHosts = append(p.bypassHosts, host) +} + +// A Dialer is a means to establish a connection. +type proxy_Dialer interface { + // Dial connects to the given address via the proxy. + Dial(network, addr string) (c net.Conn, err error) +} + +// Auth contains authentication parameters that specific Dialers may require. +type proxy_Auth struct { + User, Password string +} + +// FromEnvironment returns the dialer specified by the proxy related variables in +// the environment. +func proxy_FromEnvironment() proxy_Dialer { + allProxy := proxy_allProxyEnv.Get() + if len(allProxy) == 0 { + return proxy_Direct + } + + proxyURL, err := url.Parse(allProxy) + if err != nil { + return proxy_Direct + } + proxy, err := proxy_FromURL(proxyURL, proxy_Direct) + if err != nil { + return proxy_Direct + } + + noProxy := proxy_noProxyEnv.Get() + if len(noProxy) == 0 { + return proxy + } + + perHost := proxy_NewPerHost(proxy, proxy_Direct) + perHost.AddFromString(noProxy) + return perHost +} + +// proxySchemes is a map from URL schemes to a function that creates a Dialer +// from a URL with such a scheme. +var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error) + +// RegisterDialerType takes a URL scheme and a function to generate Dialers from +// a URL with that scheme and a forwarding Dialer. Registered schemes are used +// by FromURL. +func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) { + if proxy_proxySchemes == nil { + proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) + } + proxy_proxySchemes[scheme] = f +} + +// FromURL returns a Dialer given a URL specification and an underlying +// Dialer for it to make network requests. +func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) { + var auth *proxy_Auth + if u.User != nil { + auth = new(proxy_Auth) + auth.User = u.User.Username() + if p, ok := u.User.Password(); ok { + auth.Password = p + } + } + + switch u.Scheme { + case "socks5": + return proxy_SOCKS5("tcp", u.Host, auth, forward) + } + + // If the scheme doesn't match any of the built-in schemes, see if it + // was registered by another package. + if proxy_proxySchemes != nil { + if f, ok := proxy_proxySchemes[u.Scheme]; ok { + return f(u, forward) + } + } + + return nil, errors.New("proxy: unknown scheme: " + u.Scheme) +} + +var ( + proxy_allProxyEnv = &proxy_envOnce{ + names: []string{"ALL_PROXY", "all_proxy"}, + } + proxy_noProxyEnv = &proxy_envOnce{ + names: []string{"NO_PROXY", "no_proxy"}, + } +) + +// envOnce looks up an environment variable (optionally by multiple +// names) once. It mitigates expensive lookups on some platforms +// (e.g. Windows). +// (Borrowed from net/http/transport.go) +type proxy_envOnce struct { + names []string + once sync.Once + val string +} + +func (e *proxy_envOnce) Get() string { + e.once.Do(e.init) + return e.val +} + +func (e *proxy_envOnce) init() { + for _, n := range e.names { + e.val = os.Getenv(n) + if e.val != "" { + return + } + } +} + +// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address +// with an optional username and password. See RFC 1928 and RFC 1929. +func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) { + s := &proxy_socks5{ + network: network, + addr: addr, + forward: forward, + } + if auth != nil { + s.user = auth.User + s.password = auth.Password + } + + return s, nil +} + +type proxy_socks5 struct { + user, password string + network, addr string + forward proxy_Dialer +} + +const proxy_socks5Version = 5 + +const ( + proxy_socks5AuthNone = 0 + proxy_socks5AuthPassword = 2 +) + +const proxy_socks5Connect = 1 + +const ( + proxy_socks5IP4 = 1 + proxy_socks5Domain = 3 + proxy_socks5IP6 = 4 +) + +var proxy_socks5Errors = []string{ + "", + "general failure", + "connection forbidden", + "network unreachable", + "host unreachable", + "connection refused", + "TTL expired", + "command not supported", + "address type not supported", +} + +// Dial connects to the address addr on the given network via the SOCKS5 proxy. +func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) { + switch network { + case "tcp", "tcp6", "tcp4": + default: + return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network) + } + + conn, err := s.forward.Dial(s.network, s.addr) + if err != nil { + return nil, err + } + if err := s.connect(conn, addr); err != nil { + conn.Close() + return nil, err + } + return conn, nil +} + +// connect takes an existing connection to a socks5 proxy server, +// and commands the server to extend that connection to target, +// which must be a canonical address with a host and port. +func (s *proxy_socks5) connect(conn net.Conn, target string) error { + host, portStr, err := net.SplitHostPort(target) + if err != nil { + return err + } + + port, err := strconv.Atoi(portStr) + if err != nil { + return errors.New("proxy: failed to parse port number: " + portStr) + } + if port < 1 || port > 0xffff { + return errors.New("proxy: port number out of range: " + portStr) + } + + // the size here is just an estimate + buf := make([]byte, 0, 6+len(host)) + + buf = append(buf, proxy_socks5Version) + if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { + buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword) + } else { + buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone) + } + + if _, err := conn.Write(buf); err != nil { + return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + if buf[0] != 5 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) + } + if buf[1] == 0xff { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") + } + + // See RFC 1929 + if buf[1] == proxy_socks5AuthPassword { + buf = buf[:0] + buf = append(buf, 1 /* password protocol version */) + buf = append(buf, uint8(len(s.user))) + buf = append(buf, s.user...) + buf = append(buf, uint8(len(s.password))) + buf = append(buf, s.password...) + + if _, err := conn.Write(buf); err != nil { + return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if buf[1] != 0 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") + } + } + + buf = buf[:0] + buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */) + + if ip := net.ParseIP(host); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + buf = append(buf, proxy_socks5IP4) + ip = ip4 + } else { + buf = append(buf, proxy_socks5IP6) + } + buf = append(buf, ip...) + } else { + if len(host) > 255 { + return errors.New("proxy: destination host name too long: " + host) + } + buf = append(buf, proxy_socks5Domain) + buf = append(buf, byte(len(host))) + buf = append(buf, host...) + } + buf = append(buf, byte(port>>8), byte(port)) + + if _, err := conn.Write(buf); err != nil { + return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:4]); err != nil { + return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + failure := "unknown error" + if int(buf[1]) < len(proxy_socks5Errors) { + failure = proxy_socks5Errors[buf[1]] + } + + if len(failure) > 0 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) + } + + bytesToDiscard := 0 + switch buf[3] { + case proxy_socks5IP4: + bytesToDiscard = net.IPv4len + case proxy_socks5IP6: + bytesToDiscard = net.IPv6len + case proxy_socks5Domain: + _, err := io.ReadFull(conn, buf[:1]) + if err != nil { + return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + bytesToDiscard = int(buf[0]) + default: + return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) + } + + if cap(buf) < bytesToDiscard { + buf = make([]byte, bytesToDiscard) + } else { + buf = buf[:bytesToDiscard] + } + if _, err := io.ReadFull(conn, buf); err != nil { + return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + // Also need to discard the port number + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + return nil +} diff --git a/vendor/github.com/mitchellh/go-homedir/LICENSE b/vendor/github.com/mitchellh/go-homedir/LICENSE new file mode 100644 index 0000000000..f9c841a51e --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/go-homedir/README.md b/vendor/github.com/mitchellh/go-homedir/README.md new file mode 100644 index 0000000000..d70706d5b3 --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/README.md @@ -0,0 +1,14 @@ +# go-homedir + +This is a Go library for detecting the user's home directory without +the use of cgo, so the library can be used in cross-compilation environments. + +Usage is incredibly simple, just call `homedir.Dir()` to get the home directory +for a user, and `homedir.Expand()` to expand the `~` in a path to the home +directory. + +**Why not just use `os/user`?** The built-in `os/user` package requires +cgo on Darwin systems. This means that any Go code that uses that package +cannot cross compile. But 99% of the time the use for `os/user` is just to +retrieve the home directory, which we can do for the current user without +cgo. This library does that, enabling cross-compilation. diff --git a/vendor/github.com/mitchellh/go-homedir/go.mod b/vendor/github.com/mitchellh/go-homedir/go.mod new file mode 100644 index 0000000000..7efa09a043 --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/go.mod @@ -0,0 +1 @@ +module github.com/mitchellh/go-homedir diff --git a/vendor/github.com/mitchellh/go-homedir/homedir.go b/vendor/github.com/mitchellh/go-homedir/homedir.go new file mode 100644 index 0000000000..25378537ea --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/homedir.go @@ -0,0 +1,167 @@ +package homedir + +import ( + "bytes" + "errors" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" +) + +// DisableCache will disable caching of the home directory. Caching is enabled +// by default. +var DisableCache bool + +var homedirCache string +var cacheLock sync.RWMutex + +// Dir returns the home directory for the executing user. +// +// This uses an OS-specific method for discovering the home directory. +// An error is returned if a home directory cannot be detected. +func Dir() (string, error) { + if !DisableCache { + cacheLock.RLock() + cached := homedirCache + cacheLock.RUnlock() + if cached != "" { + return cached, nil + } + } + + cacheLock.Lock() + defer cacheLock.Unlock() + + var result string + var err error + if runtime.GOOS == "windows" { + result, err = dirWindows() + } else { + // Unix-like system, so just assume Unix + result, err = dirUnix() + } + + if err != nil { + return "", err + } + homedirCache = result + return result, nil +} + +// Expand expands the path to include the home directory if the path +// is prefixed with `~`. If it isn't prefixed with `~`, the path is +// returned as-is. +func Expand(path string) (string, error) { + if len(path) == 0 { + return path, nil + } + + if path[0] != '~' { + return path, nil + } + + if len(path) > 1 && path[1] != '/' && path[1] != '\\' { + return "", errors.New("cannot expand user-specific home dir") + } + + dir, err := Dir() + if err != nil { + return "", err + } + + return filepath.Join(dir, path[1:]), nil +} + +// Reset clears the cache, forcing the next call to Dir to re-detect +// the home directory. This generally never has to be called, but can be +// useful in tests if you're modifying the home directory via the HOME +// env var or something. +func Reset() { + cacheLock.Lock() + defer cacheLock.Unlock() + homedirCache = "" +} + +func dirUnix() (string, error) { + homeEnv := "HOME" + if runtime.GOOS == "plan9" { + // On plan9, env vars are lowercase. + homeEnv = "home" + } + + // First prefer the HOME environmental variable + if home := os.Getenv(homeEnv); home != "" { + return home, nil + } + + var stdout bytes.Buffer + + // If that fails, try OS specific commands + if runtime.GOOS == "darwin" { + cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`) + cmd.Stdout = &stdout + if err := cmd.Run(); err == nil { + result := strings.TrimSpace(stdout.String()) + if result != "" { + return result, nil + } + } + } else { + cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + // If the error is ErrNotFound, we ignore it. Otherwise, return it. + if err != exec.ErrNotFound { + return "", err + } + } else { + if passwd := strings.TrimSpace(stdout.String()); passwd != "" { + // username:password:uid:gid:gecos:home:shell + passwdParts := strings.SplitN(passwd, ":", 7) + if len(passwdParts) > 5 { + return passwdParts[5], nil + } + } + } + } + + // If all else fails, try the shell + stdout.Reset() + cmd := exec.Command("sh", "-c", "cd && pwd") + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + return "", err + } + + result := strings.TrimSpace(stdout.String()) + if result == "" { + return "", errors.New("blank output when reading home directory") + } + + return result, nil +} + +func dirWindows() (string, error) { + // First prefer the HOME environmental variable + if home := os.Getenv("HOME"); home != "" { + return home, nil + } + + // Prefer standard environment variable USERPROFILE + if home := os.Getenv("USERPROFILE"); home != "" { + return home, nil + } + + drive := os.Getenv("HOMEDRIVE") + path := os.Getenv("HOMEPATH") + home := drive + path + if drive == "" || path == "" { + return "", errors.New("HOMEDRIVE, HOMEPATH, or USERPROFILE are blank") + } + + return home, nil +} diff --git a/vendor/github.com/tektoncd/hub/api/LICENSE b/vendor/github.com/tektoncd/hub/api/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/cli.go b/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/cli.go new file mode 100644 index 0000000000..7d8675ca75 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/cli.go @@ -0,0 +1,221 @@ +// Code generated by goa v3.2.2, DO NOT EDIT. +// +// resource HTTP client CLI support package +// +// Command: +// $ goa gen github.com/tektoncd/hub/api/design + +package client + +import ( + "encoding/json" + "fmt" + "strconv" + + resource "github.com/tektoncd/hub/api/gen/resource" + goa "goa.design/goa/v3/pkg" +) + +// BuildQueryPayload builds the payload for the resource Query endpoint from +// CLI flags. +func BuildQueryPayload(resourceQueryName string, resourceQueryKinds string, resourceQueryTags string, resourceQueryLimit string, resourceQueryMatch string) (*resource.QueryPayload, error) { + var err error + var name string + { + if resourceQueryName != "" { + name = resourceQueryName + } + } + var kinds []string + { + if resourceQueryKinds != "" { + err = json.Unmarshal([]byte(resourceQueryKinds), &kinds) + if err != nil { + return nil, fmt.Errorf("invalid JSON for kinds, example of valid JSON:\n%s", "'[\n \"task\",\n \"pipelines\"\n ]'") + } + } + } + var tags []string + { + if resourceQueryTags != "" { + err = json.Unmarshal([]byte(resourceQueryTags), &tags) + if err != nil { + return nil, fmt.Errorf("invalid JSON for tags, example of valid JSON:\n%s", "'[\n \"image\",\n \"build\"\n ]'") + } + } + } + var limit uint + { + if resourceQueryLimit != "" { + var v uint64 + v, err = strconv.ParseUint(resourceQueryLimit, 10, 64) + limit = uint(v) + if err != nil { + return nil, fmt.Errorf("invalid value for limit, must be UINT") + } + } + } + var match string + { + if resourceQueryMatch != "" { + match = resourceQueryMatch + if !(match == "exact" || match == "contains") { + err = goa.MergeErrors(err, goa.InvalidEnumValueError("match", match, []interface{}{"exact", "contains"})) + } + if err != nil { + return nil, err + } + } + } + v := &resource.QueryPayload{} + v.Name = name + v.Kinds = kinds + v.Tags = tags + v.Limit = limit + v.Match = match + + return v, nil +} + +// BuildListPayload builds the payload for the resource List endpoint from CLI +// flags. +func BuildListPayload(resourceListLimit string) (*resource.ListPayload, error) { + var err error + var limit uint + { + if resourceListLimit != "" { + var v uint64 + v, err = strconv.ParseUint(resourceListLimit, 10, 64) + limit = uint(v) + if err != nil { + return nil, fmt.Errorf("invalid value for limit, must be UINT") + } + } + } + v := &resource.ListPayload{} + v.Limit = limit + + return v, nil +} + +// BuildVersionsByIDPayload builds the payload for the resource VersionsByID +// endpoint from CLI flags. +func BuildVersionsByIDPayload(resourceVersionsByIDID string) (*resource.VersionsByIDPayload, error) { + var err error + var id uint + { + var v uint64 + v, err = strconv.ParseUint(resourceVersionsByIDID, 10, 64) + id = uint(v) + if err != nil { + return nil, fmt.Errorf("invalid value for id, must be UINT") + } + } + v := &resource.VersionsByIDPayload{} + v.ID = id + + return v, nil +} + +// BuildByCatalogKindNameVersionPayload builds the payload for the resource +// ByCatalogKindNameVersion endpoint from CLI flags. +func BuildByCatalogKindNameVersionPayload(resourceByCatalogKindNameVersionCatalog string, resourceByCatalogKindNameVersionKind string, resourceByCatalogKindNameVersionName string, resourceByCatalogKindNameVersionVersion string) (*resource.ByCatalogKindNameVersionPayload, error) { + var err error + var catalog string + { + catalog = resourceByCatalogKindNameVersionCatalog + } + var kind string + { + kind = resourceByCatalogKindNameVersionKind + if !(kind == "task" || kind == "pipeline") { + err = goa.MergeErrors(err, goa.InvalidEnumValueError("kind", kind, []interface{}{"task", "pipeline"})) + } + if err != nil { + return nil, err + } + } + var name string + { + name = resourceByCatalogKindNameVersionName + } + var version string + { + version = resourceByCatalogKindNameVersionVersion + } + v := &resource.ByCatalogKindNameVersionPayload{} + v.Catalog = catalog + v.Kind = kind + v.Name = name + v.Version = version + + return v, nil +} + +// BuildByVersionIDPayload builds the payload for the resource ByVersionId +// endpoint from CLI flags. +func BuildByVersionIDPayload(resourceByVersionIDVersionID string) (*resource.ByVersionIDPayload, error) { + var err error + var versionID uint + { + var v uint64 + v, err = strconv.ParseUint(resourceByVersionIDVersionID, 10, 64) + versionID = uint(v) + if err != nil { + return nil, fmt.Errorf("invalid value for versionID, must be UINT") + } + } + v := &resource.ByVersionIDPayload{} + v.VersionID = versionID + + return v, nil +} + +// BuildByCatalogKindNamePayload builds the payload for the resource +// ByCatalogKindName endpoint from CLI flags. +func BuildByCatalogKindNamePayload(resourceByCatalogKindNameCatalog string, resourceByCatalogKindNameKind string, resourceByCatalogKindNameName string) (*resource.ByCatalogKindNamePayload, error) { + var err error + var catalog string + { + catalog = resourceByCatalogKindNameCatalog + } + var kind string + { + kind = resourceByCatalogKindNameKind + if !(kind == "task" || kind == "pipeline") { + err = goa.MergeErrors(err, goa.InvalidEnumValueError("kind", kind, []interface{}{"task", "pipeline"})) + } + if err != nil { + return nil, err + } + } + var name string + { + name = resourceByCatalogKindNameName + } + v := &resource.ByCatalogKindNamePayload{} + v.Catalog = catalog + v.Kind = kind + v.Name = name + + return v, nil +} + +// BuildByIDPayload builds the payload for the resource ById endpoint from CLI +// flags. +func BuildByIDPayload(resourceByIDID string) (*resource.ByIDPayload, error) { + var err error + var id uint + { + var v uint64 + v, err = strconv.ParseUint(resourceByIDID, 10, 64) + id = uint(v) + if err != nil { + return nil, fmt.Errorf("invalid value for id, must be UINT") + } + } + v := &resource.ByIDPayload{} + v.ID = id + + return v, nil +} diff --git a/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/client.go b/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/client.go new file mode 100644 index 0000000000..ac52baf27b --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/client.go @@ -0,0 +1,225 @@ +// Code generated by goa v3.2.2, DO NOT EDIT. +// +// resource client HTTP transport +// +// Command: +// $ goa gen github.com/tektoncd/hub/api/design + +package client + +import ( + "context" + "net/http" + + goahttp "goa.design/goa/v3/http" + goa "goa.design/goa/v3/pkg" +) + +// Client lists the resource service endpoint HTTP clients. +type Client struct { + // Query Doer is the HTTP client used to make requests to the Query endpoint. + QueryDoer goahttp.Doer + + // List Doer is the HTTP client used to make requests to the List endpoint. + ListDoer goahttp.Doer + + // VersionsByID Doer is the HTTP client used to make requests to the + // VersionsByID endpoint. + VersionsByIDDoer goahttp.Doer + + // ByCatalogKindNameVersion Doer is the HTTP client used to make requests to + // the ByCatalogKindNameVersion endpoint. + ByCatalogKindNameVersionDoer goahttp.Doer + + // ByVersionID Doer is the HTTP client used to make requests to the ByVersionId + // endpoint. + ByVersionIDDoer goahttp.Doer + + // ByCatalogKindName Doer is the HTTP client used to make requests to the + // ByCatalogKindName endpoint. + ByCatalogKindNameDoer goahttp.Doer + + // ByID Doer is the HTTP client used to make requests to the ById endpoint. + ByIDDoer goahttp.Doer + + // CORS Doer is the HTTP client used to make requests to the endpoint. + CORSDoer goahttp.Doer + + // RestoreResponseBody controls whether the response bodies are reset after + // decoding so they can be read again. + RestoreResponseBody bool + + scheme string + host string + encoder func(*http.Request) goahttp.Encoder + decoder func(*http.Response) goahttp.Decoder +} + +// NewClient instantiates HTTP clients for all the resource service servers. +func NewClient( + scheme string, + host string, + doer goahttp.Doer, + enc func(*http.Request) goahttp.Encoder, + dec func(*http.Response) goahttp.Decoder, + restoreBody bool, +) *Client { + return &Client{ + QueryDoer: doer, + ListDoer: doer, + VersionsByIDDoer: doer, + ByCatalogKindNameVersionDoer: doer, + ByVersionIDDoer: doer, + ByCatalogKindNameDoer: doer, + ByIDDoer: doer, + CORSDoer: doer, + RestoreResponseBody: restoreBody, + scheme: scheme, + host: host, + decoder: dec, + encoder: enc, + } +} + +// Query returns an endpoint that makes HTTP requests to the resource service +// Query server. +func (c *Client) Query() goa.Endpoint { + var ( + encodeRequest = EncodeQueryRequest(c.encoder) + decodeResponse = DecodeQueryResponse(c.decoder, c.RestoreResponseBody) + ) + return func(ctx context.Context, v interface{}) (interface{}, error) { + req, err := c.BuildQueryRequest(ctx, v) + if err != nil { + return nil, err + } + err = encodeRequest(req, v) + if err != nil { + return nil, err + } + resp, err := c.QueryDoer.Do(req) + if err != nil { + return nil, goahttp.ErrRequestError("resource", "Query", err) + } + return decodeResponse(resp) + } +} + +// List returns an endpoint that makes HTTP requests to the resource service +// List server. +func (c *Client) List() goa.Endpoint { + var ( + encodeRequest = EncodeListRequest(c.encoder) + decodeResponse = DecodeListResponse(c.decoder, c.RestoreResponseBody) + ) + return func(ctx context.Context, v interface{}) (interface{}, error) { + req, err := c.BuildListRequest(ctx, v) + if err != nil { + return nil, err + } + err = encodeRequest(req, v) + if err != nil { + return nil, err + } + resp, err := c.ListDoer.Do(req) + if err != nil { + return nil, goahttp.ErrRequestError("resource", "List", err) + } + return decodeResponse(resp) + } +} + +// VersionsByID returns an endpoint that makes HTTP requests to the resource +// service VersionsByID server. +func (c *Client) VersionsByID() goa.Endpoint { + var ( + decodeResponse = DecodeVersionsByIDResponse(c.decoder, c.RestoreResponseBody) + ) + return func(ctx context.Context, v interface{}) (interface{}, error) { + req, err := c.BuildVersionsByIDRequest(ctx, v) + if err != nil { + return nil, err + } + resp, err := c.VersionsByIDDoer.Do(req) + if err != nil { + return nil, goahttp.ErrRequestError("resource", "VersionsByID", err) + } + return decodeResponse(resp) + } +} + +// ByCatalogKindNameVersion returns an endpoint that makes HTTP requests to the +// resource service ByCatalogKindNameVersion server. +func (c *Client) ByCatalogKindNameVersion() goa.Endpoint { + var ( + decodeResponse = DecodeByCatalogKindNameVersionResponse(c.decoder, c.RestoreResponseBody) + ) + return func(ctx context.Context, v interface{}) (interface{}, error) { + req, err := c.BuildByCatalogKindNameVersionRequest(ctx, v) + if err != nil { + return nil, err + } + resp, err := c.ByCatalogKindNameVersionDoer.Do(req) + if err != nil { + return nil, goahttp.ErrRequestError("resource", "ByCatalogKindNameVersion", err) + } + return decodeResponse(resp) + } +} + +// ByVersionID returns an endpoint that makes HTTP requests to the resource +// service ByVersionId server. +func (c *Client) ByVersionID() goa.Endpoint { + var ( + decodeResponse = DecodeByVersionIDResponse(c.decoder, c.RestoreResponseBody) + ) + return func(ctx context.Context, v interface{}) (interface{}, error) { + req, err := c.BuildByVersionIDRequest(ctx, v) + if err != nil { + return nil, err + } + resp, err := c.ByVersionIDDoer.Do(req) + if err != nil { + return nil, goahttp.ErrRequestError("resource", "ByVersionId", err) + } + return decodeResponse(resp) + } +} + +// ByCatalogKindName returns an endpoint that makes HTTP requests to the +// resource service ByCatalogKindName server. +func (c *Client) ByCatalogKindName() goa.Endpoint { + var ( + decodeResponse = DecodeByCatalogKindNameResponse(c.decoder, c.RestoreResponseBody) + ) + return func(ctx context.Context, v interface{}) (interface{}, error) { + req, err := c.BuildByCatalogKindNameRequest(ctx, v) + if err != nil { + return nil, err + } + resp, err := c.ByCatalogKindNameDoer.Do(req) + if err != nil { + return nil, goahttp.ErrRequestError("resource", "ByCatalogKindName", err) + } + return decodeResponse(resp) + } +} + +// ByID returns an endpoint that makes HTTP requests to the resource service +// ById server. +func (c *Client) ByID() goa.Endpoint { + var ( + decodeResponse = DecodeByIDResponse(c.decoder, c.RestoreResponseBody) + ) + return func(ctx context.Context, v interface{}) (interface{}, error) { + req, err := c.BuildByIDRequest(ctx, v) + if err != nil { + return nil, err + } + resp, err := c.ByIDDoer.Do(req) + if err != nil { + return nil, goahttp.ErrRequestError("resource", "ById", err) + } + return decodeResponse(resp) + } +} diff --git a/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/encode_decode.go b/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/encode_decode.go new file mode 100644 index 0000000000..d54ca6d49e --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/encode_decode.go @@ -0,0 +1,956 @@ +// Code generated by goa v3.2.2, DO NOT EDIT. +// +// resource HTTP client encoders and decoders +// +// Command: +// $ goa gen github.com/tektoncd/hub/api/design + +package client + +import ( + "bytes" + "context" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + resource "github.com/tektoncd/hub/api/gen/resource" + resourceviews "github.com/tektoncd/hub/api/gen/resource/views" + goahttp "goa.design/goa/v3/http" +) + +// BuildQueryRequest instantiates a HTTP request object with method and path +// set to call the "resource" service "Query" endpoint +func (c *Client) BuildQueryRequest(ctx context.Context, v interface{}) (*http.Request, error) { + u := &url.URL{Scheme: c.scheme, Host: c.host, Path: QueryResourcePath()} + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, goahttp.ErrInvalidURL("resource", "Query", u.String(), err) + } + if ctx != nil { + req = req.WithContext(ctx) + } + + return req, nil +} + +// EncodeQueryRequest returns an encoder for requests sent to the resource +// Query server. +func EncodeQueryRequest(encoder func(*http.Request) goahttp.Encoder) func(*http.Request, interface{}) error { + return func(req *http.Request, v interface{}) error { + p, ok := v.(*resource.QueryPayload) + if !ok { + return goahttp.ErrInvalidType("resource", "Query", "*resource.QueryPayload", v) + } + values := req.URL.Query() + values.Add("name", p.Name) + for _, value := range p.Kinds { + values.Add("kinds", value) + } + for _, value := range p.Tags { + values.Add("tags", value) + } + values.Add("limit", fmt.Sprintf("%v", p.Limit)) + values.Add("match", p.Match) + req.URL.RawQuery = values.Encode() + return nil + } +} + +// DecodeQueryResponse returns a decoder for responses returned by the resource +// Query endpoint. restoreBody controls whether the response body should be +// restored after having been read. +// DecodeQueryResponse may return the following errors: +// - "internal-error" (type *goa.ServiceError): http.StatusInternalServerError +// - "invalid-kind" (type *goa.ServiceError): http.StatusBadRequest +// - "not-found" (type *goa.ServiceError): http.StatusNotFound +// - error: internal error +func DecodeQueryResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) { + return func(resp *http.Response) (interface{}, error) { + if restoreBody { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + defer func() { + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + }() + } else { + defer resp.Body.Close() + } + switch resp.StatusCode { + case http.StatusOK: + var ( + body QueryResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "Query", err) + } + p := NewQueryResourceCollectionOK(body) + view := "withoutVersion" + vres := resourceviews.ResourceCollection{Projected: p, View: view} + if err = resourceviews.ValidateResourceCollection(vres); err != nil { + return nil, goahttp.ErrValidationError("resource", "Query", err) + } + res := resource.NewResourceCollection(vres) + return res, nil + case http.StatusInternalServerError: + var ( + body QueryInternalErrorResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "Query", err) + } + err = ValidateQueryInternalErrorResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "Query", err) + } + return nil, NewQueryInternalError(&body) + case http.StatusBadRequest: + var ( + body QueryInvalidKindResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "Query", err) + } + err = ValidateQueryInvalidKindResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "Query", err) + } + return nil, NewQueryInvalidKind(&body) + case http.StatusNotFound: + var ( + body QueryNotFoundResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "Query", err) + } + err = ValidateQueryNotFoundResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "Query", err) + } + return nil, NewQueryNotFound(&body) + default: + body, _ := ioutil.ReadAll(resp.Body) + return nil, goahttp.ErrInvalidResponse("resource", "Query", resp.StatusCode, string(body)) + } + } +} + +// BuildListRequest instantiates a HTTP request object with method and path set +// to call the "resource" service "List" endpoint +func (c *Client) BuildListRequest(ctx context.Context, v interface{}) (*http.Request, error) { + u := &url.URL{Scheme: c.scheme, Host: c.host, Path: ListResourcePath()} + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, goahttp.ErrInvalidURL("resource", "List", u.String(), err) + } + if ctx != nil { + req = req.WithContext(ctx) + } + + return req, nil +} + +// EncodeListRequest returns an encoder for requests sent to the resource List +// server. +func EncodeListRequest(encoder func(*http.Request) goahttp.Encoder) func(*http.Request, interface{}) error { + return func(req *http.Request, v interface{}) error { + p, ok := v.(*resource.ListPayload) + if !ok { + return goahttp.ErrInvalidType("resource", "List", "*resource.ListPayload", v) + } + values := req.URL.Query() + values.Add("limit", fmt.Sprintf("%v", p.Limit)) + req.URL.RawQuery = values.Encode() + return nil + } +} + +// DecodeListResponse returns a decoder for responses returned by the resource +// List endpoint. restoreBody controls whether the response body should be +// restored after having been read. +// DecodeListResponse may return the following errors: +// - "internal-error" (type *goa.ServiceError): http.StatusInternalServerError +// - error: internal error +func DecodeListResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) { + return func(resp *http.Response) (interface{}, error) { + if restoreBody { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + defer func() { + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + }() + } else { + defer resp.Body.Close() + } + switch resp.StatusCode { + case http.StatusOK: + var ( + body ListResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "List", err) + } + p := NewListResourceCollectionOK(body) + view := "withoutVersion" + vres := resourceviews.ResourceCollection{Projected: p, View: view} + if err = resourceviews.ValidateResourceCollection(vres); err != nil { + return nil, goahttp.ErrValidationError("resource", "List", err) + } + res := resource.NewResourceCollection(vres) + return res, nil + case http.StatusInternalServerError: + var ( + body ListInternalErrorResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "List", err) + } + err = ValidateListInternalErrorResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "List", err) + } + return nil, NewListInternalError(&body) + default: + body, _ := ioutil.ReadAll(resp.Body) + return nil, goahttp.ErrInvalidResponse("resource", "List", resp.StatusCode, string(body)) + } + } +} + +// BuildVersionsByIDRequest instantiates a HTTP request object with method and +// path set to call the "resource" service "VersionsByID" endpoint +func (c *Client) BuildVersionsByIDRequest(ctx context.Context, v interface{}) (*http.Request, error) { + var ( + id uint + ) + { + p, ok := v.(*resource.VersionsByIDPayload) + if !ok { + return nil, goahttp.ErrInvalidType("resource", "VersionsByID", "*resource.VersionsByIDPayload", v) + } + id = p.ID + } + u := &url.URL{Scheme: c.scheme, Host: c.host, Path: VersionsByIDResourcePath(id)} + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, goahttp.ErrInvalidURL("resource", "VersionsByID", u.String(), err) + } + if ctx != nil { + req = req.WithContext(ctx) + } + + return req, nil +} + +// DecodeVersionsByIDResponse returns a decoder for responses returned by the +// resource VersionsByID endpoint. restoreBody controls whether the response +// body should be restored after having been read. +// DecodeVersionsByIDResponse may return the following errors: +// - "internal-error" (type *goa.ServiceError): http.StatusInternalServerError +// - "not-found" (type *goa.ServiceError): http.StatusNotFound +// - error: internal error +func DecodeVersionsByIDResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) { + return func(resp *http.Response) (interface{}, error) { + if restoreBody { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + defer func() { + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + }() + } else { + defer resp.Body.Close() + } + switch resp.StatusCode { + case http.StatusOK: + var ( + body VersionsByIDResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "VersionsByID", err) + } + p := NewVersionsByIDVersionsOK(&body) + view := "default" + vres := &resourceviews.Versions{Projected: p, View: view} + if err = resourceviews.ValidateVersions(vres); err != nil { + return nil, goahttp.ErrValidationError("resource", "VersionsByID", err) + } + res := resource.NewVersions(vres) + return res, nil + case http.StatusInternalServerError: + var ( + body VersionsByIDInternalErrorResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "VersionsByID", err) + } + err = ValidateVersionsByIDInternalErrorResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "VersionsByID", err) + } + return nil, NewVersionsByIDInternalError(&body) + case http.StatusNotFound: + var ( + body VersionsByIDNotFoundResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "VersionsByID", err) + } + err = ValidateVersionsByIDNotFoundResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "VersionsByID", err) + } + return nil, NewVersionsByIDNotFound(&body) + default: + body, _ := ioutil.ReadAll(resp.Body) + return nil, goahttp.ErrInvalidResponse("resource", "VersionsByID", resp.StatusCode, string(body)) + } + } +} + +// BuildByCatalogKindNameVersionRequest instantiates a HTTP request object with +// method and path set to call the "resource" service +// "ByCatalogKindNameVersion" endpoint +func (c *Client) BuildByCatalogKindNameVersionRequest(ctx context.Context, v interface{}) (*http.Request, error) { + var ( + catalog string + kind string + name string + version string + ) + { + p, ok := v.(*resource.ByCatalogKindNameVersionPayload) + if !ok { + return nil, goahttp.ErrInvalidType("resource", "ByCatalogKindNameVersion", "*resource.ByCatalogKindNameVersionPayload", v) + } + catalog = p.Catalog + kind = p.Kind + name = p.Name + version = p.Version + } + u := &url.URL{Scheme: c.scheme, Host: c.host, Path: ByCatalogKindNameVersionResourcePath(catalog, kind, name, version)} + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, goahttp.ErrInvalidURL("resource", "ByCatalogKindNameVersion", u.String(), err) + } + if ctx != nil { + req = req.WithContext(ctx) + } + + return req, nil +} + +// DecodeByCatalogKindNameVersionResponse returns a decoder for responses +// returned by the resource ByCatalogKindNameVersion endpoint. restoreBody +// controls whether the response body should be restored after having been read. +// DecodeByCatalogKindNameVersionResponse may return the following errors: +// - "internal-error" (type *goa.ServiceError): http.StatusInternalServerError +// - "not-found" (type *goa.ServiceError): http.StatusNotFound +// - error: internal error +func DecodeByCatalogKindNameVersionResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) { + return func(resp *http.Response) (interface{}, error) { + if restoreBody { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + defer func() { + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + }() + } else { + defer resp.Body.Close() + } + switch resp.StatusCode { + case http.StatusOK: + var ( + body ByCatalogKindNameVersionResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ByCatalogKindNameVersion", err) + } + p := NewByCatalogKindNameVersionVersionOK(&body) + view := "default" + vres := &resourceviews.Version{Projected: p, View: view} + if err = resourceviews.ValidateVersion(vres); err != nil { + return nil, goahttp.ErrValidationError("resource", "ByCatalogKindNameVersion", err) + } + res := resource.NewVersion(vres) + return res, nil + case http.StatusInternalServerError: + var ( + body ByCatalogKindNameVersionInternalErrorResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ByCatalogKindNameVersion", err) + } + err = ValidateByCatalogKindNameVersionInternalErrorResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "ByCatalogKindNameVersion", err) + } + return nil, NewByCatalogKindNameVersionInternalError(&body) + case http.StatusNotFound: + var ( + body ByCatalogKindNameVersionNotFoundResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ByCatalogKindNameVersion", err) + } + err = ValidateByCatalogKindNameVersionNotFoundResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "ByCatalogKindNameVersion", err) + } + return nil, NewByCatalogKindNameVersionNotFound(&body) + default: + body, _ := ioutil.ReadAll(resp.Body) + return nil, goahttp.ErrInvalidResponse("resource", "ByCatalogKindNameVersion", resp.StatusCode, string(body)) + } + } +} + +// BuildByVersionIDRequest instantiates a HTTP request object with method and +// path set to call the "resource" service "ByVersionId" endpoint +func (c *Client) BuildByVersionIDRequest(ctx context.Context, v interface{}) (*http.Request, error) { + var ( + versionID uint + ) + { + p, ok := v.(*resource.ByVersionIDPayload) + if !ok { + return nil, goahttp.ErrInvalidType("resource", "ByVersionId", "*resource.ByVersionIDPayload", v) + } + versionID = p.VersionID + } + u := &url.URL{Scheme: c.scheme, Host: c.host, Path: ByVersionIDResourcePath(versionID)} + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, goahttp.ErrInvalidURL("resource", "ByVersionId", u.String(), err) + } + if ctx != nil { + req = req.WithContext(ctx) + } + + return req, nil +} + +// DecodeByVersionIDResponse returns a decoder for responses returned by the +// resource ByVersionId endpoint. restoreBody controls whether the response +// body should be restored after having been read. +// DecodeByVersionIDResponse may return the following errors: +// - "internal-error" (type *goa.ServiceError): http.StatusInternalServerError +// - "not-found" (type *goa.ServiceError): http.StatusNotFound +// - error: internal error +func DecodeByVersionIDResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) { + return func(resp *http.Response) (interface{}, error) { + if restoreBody { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + defer func() { + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + }() + } else { + defer resp.Body.Close() + } + switch resp.StatusCode { + case http.StatusOK: + var ( + body ByVersionIDResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ByVersionId", err) + } + p := NewByVersionIDVersionOK(&body) + view := "default" + vres := &resourceviews.Version{Projected: p, View: view} + if err = resourceviews.ValidateVersion(vres); err != nil { + return nil, goahttp.ErrValidationError("resource", "ByVersionId", err) + } + res := resource.NewVersion(vres) + return res, nil + case http.StatusInternalServerError: + var ( + body ByVersionIDInternalErrorResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ByVersionId", err) + } + err = ValidateByVersionIDInternalErrorResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "ByVersionId", err) + } + return nil, NewByVersionIDInternalError(&body) + case http.StatusNotFound: + var ( + body ByVersionIDNotFoundResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ByVersionId", err) + } + err = ValidateByVersionIDNotFoundResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "ByVersionId", err) + } + return nil, NewByVersionIDNotFound(&body) + default: + body, _ := ioutil.ReadAll(resp.Body) + return nil, goahttp.ErrInvalidResponse("resource", "ByVersionId", resp.StatusCode, string(body)) + } + } +} + +// BuildByCatalogKindNameRequest instantiates a HTTP request object with method +// and path set to call the "resource" service "ByCatalogKindName" endpoint +func (c *Client) BuildByCatalogKindNameRequest(ctx context.Context, v interface{}) (*http.Request, error) { + var ( + catalog string + kind string + name string + ) + { + p, ok := v.(*resource.ByCatalogKindNamePayload) + if !ok { + return nil, goahttp.ErrInvalidType("resource", "ByCatalogKindName", "*resource.ByCatalogKindNamePayload", v) + } + catalog = p.Catalog + kind = p.Kind + name = p.Name + } + u := &url.URL{Scheme: c.scheme, Host: c.host, Path: ByCatalogKindNameResourcePath(catalog, kind, name)} + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, goahttp.ErrInvalidURL("resource", "ByCatalogKindName", u.String(), err) + } + if ctx != nil { + req = req.WithContext(ctx) + } + + return req, nil +} + +// DecodeByCatalogKindNameResponse returns a decoder for responses returned by +// the resource ByCatalogKindName endpoint. restoreBody controls whether the +// response body should be restored after having been read. +// DecodeByCatalogKindNameResponse may return the following errors: +// - "internal-error" (type *goa.ServiceError): http.StatusInternalServerError +// - "not-found" (type *goa.ServiceError): http.StatusNotFound +// - error: internal error +func DecodeByCatalogKindNameResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) { + return func(resp *http.Response) (interface{}, error) { + if restoreBody { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + defer func() { + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + }() + } else { + defer resp.Body.Close() + } + switch resp.StatusCode { + case http.StatusOK: + var ( + body ByCatalogKindNameResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ByCatalogKindName", err) + } + p := NewByCatalogKindNameResourceOK(&body) + view := "default" + vres := &resourceviews.Resource{Projected: p, View: view} + if err = resourceviews.ValidateResource(vres); err != nil { + return nil, goahttp.ErrValidationError("resource", "ByCatalogKindName", err) + } + res := resource.NewResource(vres) + return res, nil + case http.StatusInternalServerError: + var ( + body ByCatalogKindNameInternalErrorResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ByCatalogKindName", err) + } + err = ValidateByCatalogKindNameInternalErrorResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "ByCatalogKindName", err) + } + return nil, NewByCatalogKindNameInternalError(&body) + case http.StatusNotFound: + var ( + body ByCatalogKindNameNotFoundResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ByCatalogKindName", err) + } + err = ValidateByCatalogKindNameNotFoundResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "ByCatalogKindName", err) + } + return nil, NewByCatalogKindNameNotFound(&body) + default: + body, _ := ioutil.ReadAll(resp.Body) + return nil, goahttp.ErrInvalidResponse("resource", "ByCatalogKindName", resp.StatusCode, string(body)) + } + } +} + +// BuildByIDRequest instantiates a HTTP request object with method and path set +// to call the "resource" service "ById" endpoint +func (c *Client) BuildByIDRequest(ctx context.Context, v interface{}) (*http.Request, error) { + var ( + id uint + ) + { + p, ok := v.(*resource.ByIDPayload) + if !ok { + return nil, goahttp.ErrInvalidType("resource", "ById", "*resource.ByIDPayload", v) + } + id = p.ID + } + u := &url.URL{Scheme: c.scheme, Host: c.host, Path: ByIDResourcePath(id)} + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, goahttp.ErrInvalidURL("resource", "ById", u.String(), err) + } + if ctx != nil { + req = req.WithContext(ctx) + } + + return req, nil +} + +// DecodeByIDResponse returns a decoder for responses returned by the resource +// ById endpoint. restoreBody controls whether the response body should be +// restored after having been read. +// DecodeByIDResponse may return the following errors: +// - "internal-error" (type *goa.ServiceError): http.StatusInternalServerError +// - "not-found" (type *goa.ServiceError): http.StatusNotFound +// - error: internal error +func DecodeByIDResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) { + return func(resp *http.Response) (interface{}, error) { + if restoreBody { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + defer func() { + resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + }() + } else { + defer resp.Body.Close() + } + switch resp.StatusCode { + case http.StatusOK: + var ( + body ByIDResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ById", err) + } + p := NewByIDResourceOK(&body) + view := "default" + vres := &resourceviews.Resource{Projected: p, View: view} + if err = resourceviews.ValidateResource(vres); err != nil { + return nil, goahttp.ErrValidationError("resource", "ById", err) + } + res := resource.NewResource(vres) + return res, nil + case http.StatusInternalServerError: + var ( + body ByIDInternalErrorResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ById", err) + } + err = ValidateByIDInternalErrorResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "ById", err) + } + return nil, NewByIDInternalError(&body) + case http.StatusNotFound: + var ( + body ByIDNotFoundResponseBody + err error + ) + err = decoder(resp).Decode(&body) + if err != nil { + return nil, goahttp.ErrDecodingError("resource", "ById", err) + } + err = ValidateByIDNotFoundResponseBody(&body) + if err != nil { + return nil, goahttp.ErrValidationError("resource", "ById", err) + } + return nil, NewByIDNotFound(&body) + default: + body, _ := ioutil.ReadAll(resp.Body) + return nil, goahttp.ErrInvalidResponse("resource", "ById", resp.StatusCode, string(body)) + } + } +} + +// unmarshalResourceResponseToResourceviewsResourceView builds a value of type +// *resourceviews.ResourceView from a value of type *ResourceResponse. +func unmarshalResourceResponseToResourceviewsResourceView(v *ResourceResponse) *resourceviews.ResourceView { + res := &resourceviews.ResourceView{ + ID: v.ID, + Name: v.Name, + Kind: v.Kind, + Rating: v.Rating, + } + res.Catalog = unmarshalCatalogResponseToResourceviewsCatalogView(v.Catalog) + res.LatestVersion = unmarshalVersionResponseToResourceviewsVersionView(v.LatestVersion) + res.Tags = make([]*resourceviews.TagView, len(v.Tags)) + for i, val := range v.Tags { + res.Tags[i] = unmarshalTagResponseToResourceviewsTagView(val) + } + res.Versions = make([]*resourceviews.VersionView, len(v.Versions)) + for i, val := range v.Versions { + res.Versions[i] = unmarshalVersionResponseToResourceviewsVersionView(val) + } + + return res +} + +// unmarshalCatalogResponseToResourceviewsCatalogView builds a value of type +// *resourceviews.CatalogView from a value of type *CatalogResponse. +func unmarshalCatalogResponseToResourceviewsCatalogView(v *CatalogResponse) *resourceviews.CatalogView { + res := &resourceviews.CatalogView{ + ID: v.ID, + Name: v.Name, + Type: v.Type, + } + + return res +} + +// unmarshalVersionResponseToResourceviewsVersionView builds a value of type +// *resourceviews.VersionView from a value of type *VersionResponse. +func unmarshalVersionResponseToResourceviewsVersionView(v *VersionResponse) *resourceviews.VersionView { + res := &resourceviews.VersionView{ + ID: v.ID, + Version: v.Version, + DisplayName: v.DisplayName, + Description: v.Description, + MinPipelinesVersion: v.MinPipelinesVersion, + RawURL: v.RawURL, + WebURL: v.WebURL, + UpdatedAt: v.UpdatedAt, + } + res.Resource = unmarshalResourceResponseToResourceviewsResourceView(v.Resource) + + return res +} + +// unmarshalTagResponseToResourceviewsTagView builds a value of type +// *resourceviews.TagView from a value of type *TagResponse. +func unmarshalTagResponseToResourceviewsTagView(v *TagResponse) *resourceviews.TagView { + res := &resourceviews.TagView{ + ID: v.ID, + Name: v.Name, + } + + return res +} + +// unmarshalVersionResponseBodyToResourceviewsVersionView builds a value of +// type *resourceviews.VersionView from a value of type *VersionResponseBody. +func unmarshalVersionResponseBodyToResourceviewsVersionView(v *VersionResponseBody) *resourceviews.VersionView { + res := &resourceviews.VersionView{ + ID: v.ID, + Version: v.Version, + DisplayName: v.DisplayName, + Description: v.Description, + MinPipelinesVersion: v.MinPipelinesVersion, + RawURL: v.RawURL, + WebURL: v.WebURL, + UpdatedAt: v.UpdatedAt, + } + res.Resource = unmarshalResourceResponseBodyToResourceviewsResourceView(v.Resource) + + return res +} + +// unmarshalResourceResponseBodyToResourceviewsResourceView builds a value of +// type *resourceviews.ResourceView from a value of type *ResourceResponseBody. +func unmarshalResourceResponseBodyToResourceviewsResourceView(v *ResourceResponseBody) *resourceviews.ResourceView { + res := &resourceviews.ResourceView{ + ID: v.ID, + Name: v.Name, + Kind: v.Kind, + Rating: v.Rating, + } + res.Catalog = unmarshalCatalogResponseBodyToResourceviewsCatalogView(v.Catalog) + res.LatestVersion = unmarshalVersionResponseBodyToResourceviewsVersionView(v.LatestVersion) + res.Tags = make([]*resourceviews.TagView, len(v.Tags)) + for i, val := range v.Tags { + res.Tags[i] = unmarshalTagResponseBodyToResourceviewsTagView(val) + } + res.Versions = make([]*resourceviews.VersionView, len(v.Versions)) + for i, val := range v.Versions { + res.Versions[i] = unmarshalVersionResponseBodyToResourceviewsVersionView(val) + } + + return res +} + +// unmarshalCatalogResponseBodyToResourceviewsCatalogView builds a value of +// type *resourceviews.CatalogView from a value of type *CatalogResponseBody. +func unmarshalCatalogResponseBodyToResourceviewsCatalogView(v *CatalogResponseBody) *resourceviews.CatalogView { + res := &resourceviews.CatalogView{ + ID: v.ID, + Name: v.Name, + Type: v.Type, + } + + return res +} + +// unmarshalTagResponseBodyToResourceviewsTagView builds a value of type +// *resourceviews.TagView from a value of type *TagResponseBody. +func unmarshalTagResponseBodyToResourceviewsTagView(v *TagResponseBody) *resourceviews.TagView { + res := &resourceviews.TagView{ + ID: v.ID, + Name: v.Name, + } + + return res +} + +// unmarshalByCatalogKindNameVersionResponseBodyToResourceviewsVersionView +// builds a value of type *resourceviews.VersionView from a value of type +// *ByCatalogKindNameVersionResponseBody. +func unmarshalByCatalogKindNameVersionResponseBodyToResourceviewsVersionView(v *ByCatalogKindNameVersionResponseBody) *resourceviews.VersionView { + res := &resourceviews.VersionView{ + ID: v.ID, + Version: v.Version, + DisplayName: v.DisplayName, + Description: v.Description, + MinPipelinesVersion: v.MinPipelinesVersion, + RawURL: v.RawURL, + WebURL: v.WebURL, + UpdatedAt: v.UpdatedAt, + } + res.Resource = unmarshalResourceResponseBodyToResourceviewsResourceView(v.Resource) + + return res +} + +// unmarshalByVersionIDResponseBodyToResourceviewsVersionView builds a value of +// type *resourceviews.VersionView from a value of type +// *ByVersionIDResponseBody. +func unmarshalByVersionIDResponseBodyToResourceviewsVersionView(v *ByVersionIDResponseBody) *resourceviews.VersionView { + res := &resourceviews.VersionView{ + ID: v.ID, + Version: v.Version, + DisplayName: v.DisplayName, + Description: v.Description, + MinPipelinesVersion: v.MinPipelinesVersion, + RawURL: v.RawURL, + WebURL: v.WebURL, + UpdatedAt: v.UpdatedAt, + } + res.Resource = unmarshalResourceResponseBodyToResourceviewsResourceView(v.Resource) + + return res +} + +// unmarshalByCatalogKindNameResponseBodyToResourceviewsResourceView builds a +// value of type *resourceviews.ResourceView from a value of type +// *ByCatalogKindNameResponseBody. +func unmarshalByCatalogKindNameResponseBodyToResourceviewsResourceView(v *ByCatalogKindNameResponseBody) *resourceviews.ResourceView { + res := &resourceviews.ResourceView{ + ID: v.ID, + Name: v.Name, + Kind: v.Kind, + Rating: v.Rating, + } + res.Catalog = unmarshalCatalogResponseBodyToResourceviewsCatalogView(v.Catalog) + res.LatestVersion = unmarshalVersionResponseBodyToResourceviewsVersionView(v.LatestVersion) + res.Tags = make([]*resourceviews.TagView, len(v.Tags)) + for i, val := range v.Tags { + res.Tags[i] = unmarshalTagResponseBodyToResourceviewsTagView(val) + } + res.Versions = make([]*resourceviews.VersionView, len(v.Versions)) + for i, val := range v.Versions { + res.Versions[i] = unmarshalVersionResponseBodyToResourceviewsVersionView(val) + } + + return res +} + +// unmarshalByIDResponseBodyToResourceviewsResourceView builds a value of type +// *resourceviews.ResourceView from a value of type *ByIDResponseBody. +func unmarshalByIDResponseBodyToResourceviewsResourceView(v *ByIDResponseBody) *resourceviews.ResourceView { + res := &resourceviews.ResourceView{ + ID: v.ID, + Name: v.Name, + Kind: v.Kind, + Rating: v.Rating, + } + res.Catalog = unmarshalCatalogResponseBodyToResourceviewsCatalogView(v.Catalog) + res.LatestVersion = unmarshalVersionResponseBodyToResourceviewsVersionView(v.LatestVersion) + res.Tags = make([]*resourceviews.TagView, len(v.Tags)) + for i, val := range v.Tags { + res.Tags[i] = unmarshalTagResponseBodyToResourceviewsTagView(val) + } + res.Versions = make([]*resourceviews.VersionView, len(v.Versions)) + for i, val := range v.Versions { + res.Versions[i] = unmarshalVersionResponseBodyToResourceviewsVersionView(val) + } + + return res +} diff --git a/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/paths.go b/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/paths.go new file mode 100644 index 0000000000..b7a6151793 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/paths.go @@ -0,0 +1,47 @@ +// Code generated by goa v3.2.2, DO NOT EDIT. +// +// HTTP request path constructors for the resource service. +// +// Command: +// $ goa gen github.com/tektoncd/hub/api/design + +package client + +import ( + "fmt" +) + +// QueryResourcePath returns the URL path to the resource service Query HTTP endpoint. +func QueryResourcePath() string { + return "/query" +} + +// ListResourcePath returns the URL path to the resource service List HTTP endpoint. +func ListResourcePath() string { + return "/resources" +} + +// VersionsByIDResourcePath returns the URL path to the resource service VersionsByID HTTP endpoint. +func VersionsByIDResourcePath(id uint) string { + return fmt.Sprintf("/resource/%v/versions", id) +} + +// ByCatalogKindNameVersionResourcePath returns the URL path to the resource service ByCatalogKindNameVersion HTTP endpoint. +func ByCatalogKindNameVersionResourcePath(catalog string, kind string, name string, version string) string { + return fmt.Sprintf("/resource/%v/%v/%v/%v", catalog, kind, name, version) +} + +// ByVersionIDResourcePath returns the URL path to the resource service ByVersionId HTTP endpoint. +func ByVersionIDResourcePath(versionID uint) string { + return fmt.Sprintf("/resource/version/%v", versionID) +} + +// ByCatalogKindNameResourcePath returns the URL path to the resource service ByCatalogKindName HTTP endpoint. +func ByCatalogKindNameResourcePath(catalog string, kind string, name string) string { + return fmt.Sprintf("/resource/%v/%v/%v", catalog, kind, name) +} + +// ByIDResourcePath returns the URL path to the resource service ById HTTP endpoint. +func ByIDResourcePath(id uint) string { + return fmt.Sprintf("/resource/%v", id) +} diff --git a/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/types.go b/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/types.go new file mode 100644 index 0000000000..2a51dc60a9 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/gen/http/resource/client/types.go @@ -0,0 +1,1413 @@ +// Code generated by goa v3.2.2, DO NOT EDIT. +// +// resource HTTP client types +// +// Command: +// $ goa gen github.com/tektoncd/hub/api/design + +package client + +import ( + resourceviews "github.com/tektoncd/hub/api/gen/resource/views" + goa "goa.design/goa/v3/pkg" +) + +// QueryResponseBody is the type of the "resource" service "Query" endpoint +// HTTP response body. +type QueryResponseBody []*ResourceResponse + +// ListResponseBody is the type of the "resource" service "List" endpoint HTTP +// response body. +type ListResponseBody []*ResourceResponse + +// VersionsByIDResponseBody is the type of the "resource" service +// "VersionsByID" endpoint HTTP response body. +type VersionsByIDResponseBody struct { + // Latest Version of resource + Latest *VersionResponseBody `form:"latest,omitempty" json:"latest,omitempty" xml:"latest,omitempty"` + // List of all versions of resource + Versions []*VersionResponseBody `form:"versions,omitempty" json:"versions,omitempty" xml:"versions,omitempty"` +} + +// ByCatalogKindNameVersionResponseBody is the type of the "resource" service +// "ByCatalogKindNameVersion" endpoint HTTP response body. +type ByCatalogKindNameVersionResponseBody struct { + // ID is the unique id of resource's version + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Version of resource + Version *string `form:"version,omitempty" json:"version,omitempty" xml:"version,omitempty"` + // Display name of version + DisplayName *string `form:"displayName,omitempty" json:"displayName,omitempty" xml:"displayName,omitempty"` + // Description of version + Description *string `form:"description,omitempty" json:"description,omitempty" xml:"description,omitempty"` + // Minimum pipelines version the resource's version is compatible with + MinPipelinesVersion *string `form:"minPipelinesVersion,omitempty" json:"minPipelinesVersion,omitempty" xml:"minPipelinesVersion,omitempty"` + // Raw URL of resource's yaml file of the version + RawURL *string `form:"rawURL,omitempty" json:"rawURL,omitempty" xml:"rawURL,omitempty"` + // Web URL of resource's yaml file of the version + WebURL *string `form:"webURL,omitempty" json:"webURL,omitempty" xml:"webURL,omitempty"` + // Timestamp when version was last updated + UpdatedAt *string `form:"updatedAt,omitempty" json:"updatedAt,omitempty" xml:"updatedAt,omitempty"` + // Resource to which the version belongs + Resource *ResourceResponseBody `form:"resource,omitempty" json:"resource,omitempty" xml:"resource,omitempty"` +} + +// ByVersionIDResponseBody is the type of the "resource" service "ByVersionId" +// endpoint HTTP response body. +type ByVersionIDResponseBody struct { + // ID is the unique id of resource's version + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Version of resource + Version *string `form:"version,omitempty" json:"version,omitempty" xml:"version,omitempty"` + // Display name of version + DisplayName *string `form:"displayName,omitempty" json:"displayName,omitempty" xml:"displayName,omitempty"` + // Description of version + Description *string `form:"description,omitempty" json:"description,omitempty" xml:"description,omitempty"` + // Minimum pipelines version the resource's version is compatible with + MinPipelinesVersion *string `form:"minPipelinesVersion,omitempty" json:"minPipelinesVersion,omitempty" xml:"minPipelinesVersion,omitempty"` + // Raw URL of resource's yaml file of the version + RawURL *string `form:"rawURL,omitempty" json:"rawURL,omitempty" xml:"rawURL,omitempty"` + // Web URL of resource's yaml file of the version + WebURL *string `form:"webURL,omitempty" json:"webURL,omitempty" xml:"webURL,omitempty"` + // Timestamp when version was last updated + UpdatedAt *string `form:"updatedAt,omitempty" json:"updatedAt,omitempty" xml:"updatedAt,omitempty"` + // Resource to which the version belongs + Resource *ResourceResponseBody `form:"resource,omitempty" json:"resource,omitempty" xml:"resource,omitempty"` +} + +// ByCatalogKindNameResponseBody is the type of the "resource" service +// "ByCatalogKindName" endpoint HTTP response body. +type ByCatalogKindNameResponseBody struct { + // ID is the unique id of the resource + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Name of resource + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // Type of catalog to which resource belongs + Catalog *CatalogResponseBody `form:"catalog,omitempty" json:"catalog,omitempty" xml:"catalog,omitempty"` + // Kind of resource + Kind *string `form:"kind,omitempty" json:"kind,omitempty" xml:"kind,omitempty"` + // Latest version of resource + LatestVersion *VersionResponseBody `form:"latestVersion,omitempty" json:"latestVersion,omitempty" xml:"latestVersion,omitempty"` + // Tags related to resource + Tags []*TagResponseBody `form:"tags,omitempty" json:"tags,omitempty" xml:"tags,omitempty"` + // Rating of resource + Rating *float64 `form:"rating,omitempty" json:"rating,omitempty" xml:"rating,omitempty"` + // List of all versions of a resource + Versions []*VersionResponseBody `form:"versions,omitempty" json:"versions,omitempty" xml:"versions,omitempty"` +} + +// ByIDResponseBody is the type of the "resource" service "ById" endpoint HTTP +// response body. +type ByIDResponseBody struct { + // ID is the unique id of the resource + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Name of resource + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // Type of catalog to which resource belongs + Catalog *CatalogResponseBody `form:"catalog,omitempty" json:"catalog,omitempty" xml:"catalog,omitempty"` + // Kind of resource + Kind *string `form:"kind,omitempty" json:"kind,omitempty" xml:"kind,omitempty"` + // Latest version of resource + LatestVersion *VersionResponseBody `form:"latestVersion,omitempty" json:"latestVersion,omitempty" xml:"latestVersion,omitempty"` + // Tags related to resource + Tags []*TagResponseBody `form:"tags,omitempty" json:"tags,omitempty" xml:"tags,omitempty"` + // Rating of resource + Rating *float64 `form:"rating,omitempty" json:"rating,omitempty" xml:"rating,omitempty"` + // List of all versions of a resource + Versions []*VersionResponseBody `form:"versions,omitempty" json:"versions,omitempty" xml:"versions,omitempty"` +} + +// QueryInternalErrorResponseBody is the type of the "resource" service "Query" +// endpoint HTTP response body for the "internal-error" error. +type QueryInternalErrorResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// QueryInvalidKindResponseBody is the type of the "resource" service "Query" +// endpoint HTTP response body for the "invalid-kind" error. +type QueryInvalidKindResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// QueryNotFoundResponseBody is the type of the "resource" service "Query" +// endpoint HTTP response body for the "not-found" error. +type QueryNotFoundResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// ListInternalErrorResponseBody is the type of the "resource" service "List" +// endpoint HTTP response body for the "internal-error" error. +type ListInternalErrorResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// VersionsByIDInternalErrorResponseBody is the type of the "resource" service +// "VersionsByID" endpoint HTTP response body for the "internal-error" error. +type VersionsByIDInternalErrorResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// VersionsByIDNotFoundResponseBody is the type of the "resource" service +// "VersionsByID" endpoint HTTP response body for the "not-found" error. +type VersionsByIDNotFoundResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// ByCatalogKindNameVersionInternalErrorResponseBody is the type of the +// "resource" service "ByCatalogKindNameVersion" endpoint HTTP response body +// for the "internal-error" error. +type ByCatalogKindNameVersionInternalErrorResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// ByCatalogKindNameVersionNotFoundResponseBody is the type of the "resource" +// service "ByCatalogKindNameVersion" endpoint HTTP response body for the +// "not-found" error. +type ByCatalogKindNameVersionNotFoundResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// ByVersionIDInternalErrorResponseBody is the type of the "resource" service +// "ByVersionId" endpoint HTTP response body for the "internal-error" error. +type ByVersionIDInternalErrorResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// ByVersionIDNotFoundResponseBody is the type of the "resource" service +// "ByVersionId" endpoint HTTP response body for the "not-found" error. +type ByVersionIDNotFoundResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// ByCatalogKindNameInternalErrorResponseBody is the type of the "resource" +// service "ByCatalogKindName" endpoint HTTP response body for the +// "internal-error" error. +type ByCatalogKindNameInternalErrorResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// ByCatalogKindNameNotFoundResponseBody is the type of the "resource" service +// "ByCatalogKindName" endpoint HTTP response body for the "not-found" error. +type ByCatalogKindNameNotFoundResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// ByIDInternalErrorResponseBody is the type of the "resource" service "ById" +// endpoint HTTP response body for the "internal-error" error. +type ByIDInternalErrorResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// ByIDNotFoundResponseBody is the type of the "resource" service "ById" +// endpoint HTTP response body for the "not-found" error. +type ByIDNotFoundResponseBody struct { + // Name is the name of this class of errors. + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // ID is a unique identifier for this particular occurrence of the problem. + ID *string `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Message is a human-readable explanation specific to this occurrence of the + // problem. + Message *string `form:"message,omitempty" json:"message,omitempty" xml:"message,omitempty"` + // Is the error temporary? + Temporary *bool `form:"temporary,omitempty" json:"temporary,omitempty" xml:"temporary,omitempty"` + // Is the error a timeout? + Timeout *bool `form:"timeout,omitempty" json:"timeout,omitempty" xml:"timeout,omitempty"` + // Is the error a server-side fault? + Fault *bool `form:"fault,omitempty" json:"fault,omitempty" xml:"fault,omitempty"` +} + +// ResourceResponse is used to define fields on response body types. +type ResourceResponse struct { + // ID is the unique id of the resource + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Name of resource + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // Type of catalog to which resource belongs + Catalog *CatalogResponse `form:"catalog,omitempty" json:"catalog,omitempty" xml:"catalog,omitempty"` + // Kind of resource + Kind *string `form:"kind,omitempty" json:"kind,omitempty" xml:"kind,omitempty"` + // Latest version of resource + LatestVersion *VersionResponse `form:"latestVersion,omitempty" json:"latestVersion,omitempty" xml:"latestVersion,omitempty"` + // Tags related to resource + Tags []*TagResponse `form:"tags,omitempty" json:"tags,omitempty" xml:"tags,omitempty"` + // Rating of resource + Rating *float64 `form:"rating,omitempty" json:"rating,omitempty" xml:"rating,omitempty"` + // List of all versions of a resource + Versions []*VersionResponse `form:"versions,omitempty" json:"versions,omitempty" xml:"versions,omitempty"` +} + +// CatalogResponse is used to define fields on response body types. +type CatalogResponse struct { + // ID is the unique id of the catalog + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Name of catalog + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // Type of catalog + Type *string `form:"type,omitempty" json:"type,omitempty" xml:"type,omitempty"` +} + +// VersionResponse is used to define fields on response body types. +type VersionResponse struct { + // ID is the unique id of resource's version + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Version of resource + Version *string `form:"version,omitempty" json:"version,omitempty" xml:"version,omitempty"` + // Display name of version + DisplayName *string `form:"displayName,omitempty" json:"displayName,omitempty" xml:"displayName,omitempty"` + // Description of version + Description *string `form:"description,omitempty" json:"description,omitempty" xml:"description,omitempty"` + // Minimum pipelines version the resource's version is compatible with + MinPipelinesVersion *string `form:"minPipelinesVersion,omitempty" json:"minPipelinesVersion,omitempty" xml:"minPipelinesVersion,omitempty"` + // Raw URL of resource's yaml file of the version + RawURL *string `form:"rawURL,omitempty" json:"rawURL,omitempty" xml:"rawURL,omitempty"` + // Web URL of resource's yaml file of the version + WebURL *string `form:"webURL,omitempty" json:"webURL,omitempty" xml:"webURL,omitempty"` + // Timestamp when version was last updated + UpdatedAt *string `form:"updatedAt,omitempty" json:"updatedAt,omitempty" xml:"updatedAt,omitempty"` + // Resource to which the version belongs + Resource *ResourceResponse `form:"resource,omitempty" json:"resource,omitempty" xml:"resource,omitempty"` +} + +// TagResponse is used to define fields on response body types. +type TagResponse struct { + // ID is the unique id of tag + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Name of tag + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` +} + +// VersionResponseBody is used to define fields on response body types. +type VersionResponseBody struct { + // ID is the unique id of resource's version + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Version of resource + Version *string `form:"version,omitempty" json:"version,omitempty" xml:"version,omitempty"` + // Display name of version + DisplayName *string `form:"displayName,omitempty" json:"displayName,omitempty" xml:"displayName,omitempty"` + // Description of version + Description *string `form:"description,omitempty" json:"description,omitempty" xml:"description,omitempty"` + // Minimum pipelines version the resource's version is compatible with + MinPipelinesVersion *string `form:"minPipelinesVersion,omitempty" json:"minPipelinesVersion,omitempty" xml:"minPipelinesVersion,omitempty"` + // Raw URL of resource's yaml file of the version + RawURL *string `form:"rawURL,omitempty" json:"rawURL,omitempty" xml:"rawURL,omitempty"` + // Web URL of resource's yaml file of the version + WebURL *string `form:"webURL,omitempty" json:"webURL,omitempty" xml:"webURL,omitempty"` + // Timestamp when version was last updated + UpdatedAt *string `form:"updatedAt,omitempty" json:"updatedAt,omitempty" xml:"updatedAt,omitempty"` + // Resource to which the version belongs + Resource *ResourceResponseBody `form:"resource,omitempty" json:"resource,omitempty" xml:"resource,omitempty"` +} + +// ResourceResponseBody is used to define fields on response body types. +type ResourceResponseBody struct { + // ID is the unique id of the resource + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Name of resource + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // Type of catalog to which resource belongs + Catalog *CatalogResponseBody `form:"catalog,omitempty" json:"catalog,omitempty" xml:"catalog,omitempty"` + // Kind of resource + Kind *string `form:"kind,omitempty" json:"kind,omitempty" xml:"kind,omitempty"` + // Latest version of resource + LatestVersion *VersionResponseBody `form:"latestVersion,omitempty" json:"latestVersion,omitempty" xml:"latestVersion,omitempty"` + // Tags related to resource + Tags []*TagResponseBody `form:"tags,omitempty" json:"tags,omitempty" xml:"tags,omitempty"` + // Rating of resource + Rating *float64 `form:"rating,omitempty" json:"rating,omitempty" xml:"rating,omitempty"` + // List of all versions of a resource + Versions []*VersionResponseBody `form:"versions,omitempty" json:"versions,omitempty" xml:"versions,omitempty"` +} + +// CatalogResponseBody is used to define fields on response body types. +type CatalogResponseBody struct { + // ID is the unique id of the catalog + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Name of catalog + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` + // Type of catalog + Type *string `form:"type,omitempty" json:"type,omitempty" xml:"type,omitempty"` +} + +// TagResponseBody is used to define fields on response body types. +type TagResponseBody struct { + // ID is the unique id of tag + ID *uint `form:"id,omitempty" json:"id,omitempty" xml:"id,omitempty"` + // Name of tag + Name *string `form:"name,omitempty" json:"name,omitempty" xml:"name,omitempty"` +} + +// NewQueryResourceCollectionOK builds a "resource" service "Query" endpoint +// result from a HTTP "OK" response. +func NewQueryResourceCollectionOK(body QueryResponseBody) resourceviews.ResourceCollectionView { + v := make([]*resourceviews.ResourceView, len(body)) + for i, val := range body { + v[i] = unmarshalResourceResponseToResourceviewsResourceView(val) + } + return v +} + +// NewQueryInternalError builds a resource service Query endpoint +// internal-error error. +func NewQueryInternalError(body *QueryInternalErrorResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewQueryInvalidKind builds a resource service Query endpoint invalid-kind +// error. +func NewQueryInvalidKind(body *QueryInvalidKindResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewQueryNotFound builds a resource service Query endpoint not-found error. +func NewQueryNotFound(body *QueryNotFoundResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewListResourceCollectionOK builds a "resource" service "List" endpoint +// result from a HTTP "OK" response. +func NewListResourceCollectionOK(body ListResponseBody) resourceviews.ResourceCollectionView { + v := make([]*resourceviews.ResourceView, len(body)) + for i, val := range body { + v[i] = unmarshalResourceResponseToResourceviewsResourceView(val) + } + return v +} + +// NewListInternalError builds a resource service List endpoint internal-error +// error. +func NewListInternalError(body *ListInternalErrorResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewVersionsByIDVersionsOK builds a "resource" service "VersionsByID" +// endpoint result from a HTTP "OK" response. +func NewVersionsByIDVersionsOK(body *VersionsByIDResponseBody) *resourceviews.VersionsView { + v := &resourceviews.VersionsView{} + v.Latest = unmarshalVersionResponseBodyToResourceviewsVersionView(body.Latest) + v.Versions = make([]*resourceviews.VersionView, len(body.Versions)) + for i, val := range body.Versions { + v.Versions[i] = unmarshalVersionResponseBodyToResourceviewsVersionView(val) + } + + return v +} + +// NewVersionsByIDInternalError builds a resource service VersionsByID endpoint +// internal-error error. +func NewVersionsByIDInternalError(body *VersionsByIDInternalErrorResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewVersionsByIDNotFound builds a resource service VersionsByID endpoint +// not-found error. +func NewVersionsByIDNotFound(body *VersionsByIDNotFoundResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewByCatalogKindNameVersionVersionOK builds a "resource" service +// "ByCatalogKindNameVersion" endpoint result from a HTTP "OK" response. +func NewByCatalogKindNameVersionVersionOK(body *ByCatalogKindNameVersionResponseBody) *resourceviews.VersionView { + v := &resourceviews.VersionView{ + ID: body.ID, + Version: body.Version, + DisplayName: body.DisplayName, + Description: body.Description, + MinPipelinesVersion: body.MinPipelinesVersion, + RawURL: body.RawURL, + WebURL: body.WebURL, + UpdatedAt: body.UpdatedAt, + } + v.Resource = unmarshalResourceResponseBodyToResourceviewsResourceView(body.Resource) + + return v +} + +// NewByCatalogKindNameVersionInternalError builds a resource service +// ByCatalogKindNameVersion endpoint internal-error error. +func NewByCatalogKindNameVersionInternalError(body *ByCatalogKindNameVersionInternalErrorResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewByCatalogKindNameVersionNotFound builds a resource service +// ByCatalogKindNameVersion endpoint not-found error. +func NewByCatalogKindNameVersionNotFound(body *ByCatalogKindNameVersionNotFoundResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewByVersionIDVersionOK builds a "resource" service "ByVersionId" endpoint +// result from a HTTP "OK" response. +func NewByVersionIDVersionOK(body *ByVersionIDResponseBody) *resourceviews.VersionView { + v := &resourceviews.VersionView{ + ID: body.ID, + Version: body.Version, + DisplayName: body.DisplayName, + Description: body.Description, + MinPipelinesVersion: body.MinPipelinesVersion, + RawURL: body.RawURL, + WebURL: body.WebURL, + UpdatedAt: body.UpdatedAt, + } + v.Resource = unmarshalResourceResponseBodyToResourceviewsResourceView(body.Resource) + + return v +} + +// NewByVersionIDInternalError builds a resource service ByVersionId endpoint +// internal-error error. +func NewByVersionIDInternalError(body *ByVersionIDInternalErrorResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewByVersionIDNotFound builds a resource service ByVersionId endpoint +// not-found error. +func NewByVersionIDNotFound(body *ByVersionIDNotFoundResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewByCatalogKindNameResourceOK builds a "resource" service +// "ByCatalogKindName" endpoint result from a HTTP "OK" response. +func NewByCatalogKindNameResourceOK(body *ByCatalogKindNameResponseBody) *resourceviews.ResourceView { + v := &resourceviews.ResourceView{ + ID: body.ID, + Name: body.Name, + Kind: body.Kind, + Rating: body.Rating, + } + v.Catalog = unmarshalCatalogResponseBodyToResourceviewsCatalogView(body.Catalog) + v.LatestVersion = unmarshalVersionResponseBodyToResourceviewsVersionView(body.LatestVersion) + v.Tags = make([]*resourceviews.TagView, len(body.Tags)) + for i, val := range body.Tags { + v.Tags[i] = unmarshalTagResponseBodyToResourceviewsTagView(val) + } + v.Versions = make([]*resourceviews.VersionView, len(body.Versions)) + for i, val := range body.Versions { + v.Versions[i] = unmarshalVersionResponseBodyToResourceviewsVersionView(val) + } + + return v +} + +// NewByCatalogKindNameInternalError builds a resource service +// ByCatalogKindName endpoint internal-error error. +func NewByCatalogKindNameInternalError(body *ByCatalogKindNameInternalErrorResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewByCatalogKindNameNotFound builds a resource service ByCatalogKindName +// endpoint not-found error. +func NewByCatalogKindNameNotFound(body *ByCatalogKindNameNotFoundResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewByIDResourceOK builds a "resource" service "ById" endpoint result from a +// HTTP "OK" response. +func NewByIDResourceOK(body *ByIDResponseBody) *resourceviews.ResourceView { + v := &resourceviews.ResourceView{ + ID: body.ID, + Name: body.Name, + Kind: body.Kind, + Rating: body.Rating, + } + v.Catalog = unmarshalCatalogResponseBodyToResourceviewsCatalogView(body.Catalog) + v.LatestVersion = unmarshalVersionResponseBodyToResourceviewsVersionView(body.LatestVersion) + v.Tags = make([]*resourceviews.TagView, len(body.Tags)) + for i, val := range body.Tags { + v.Tags[i] = unmarshalTagResponseBodyToResourceviewsTagView(val) + } + v.Versions = make([]*resourceviews.VersionView, len(body.Versions)) + for i, val := range body.Versions { + v.Versions[i] = unmarshalVersionResponseBodyToResourceviewsVersionView(val) + } + + return v +} + +// NewByIDInternalError builds a resource service ById endpoint internal-error +// error. +func NewByIDInternalError(body *ByIDInternalErrorResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// NewByIDNotFound builds a resource service ById endpoint not-found error. +func NewByIDNotFound(body *ByIDNotFoundResponseBody) *goa.ServiceError { + v := &goa.ServiceError{ + Name: *body.Name, + ID: *body.ID, + Message: *body.Message, + Temporary: *body.Temporary, + Timeout: *body.Timeout, + Fault: *body.Fault, + } + + return v +} + +// ValidateQueryInternalErrorResponseBody runs the validations defined on +// Query_internal-error_Response_Body +func ValidateQueryInternalErrorResponseBody(body *QueryInternalErrorResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateQueryInvalidKindResponseBody runs the validations defined on +// Query_invalid-kind_Response_Body +func ValidateQueryInvalidKindResponseBody(body *QueryInvalidKindResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateQueryNotFoundResponseBody runs the validations defined on +// Query_not-found_Response_Body +func ValidateQueryNotFoundResponseBody(body *QueryNotFoundResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateListInternalErrorResponseBody runs the validations defined on +// List_internal-error_Response_Body +func ValidateListInternalErrorResponseBody(body *ListInternalErrorResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateVersionsByIDInternalErrorResponseBody runs the validations defined +// on VersionsByID_internal-error_Response_Body +func ValidateVersionsByIDInternalErrorResponseBody(body *VersionsByIDInternalErrorResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateVersionsByIDNotFoundResponseBody runs the validations defined on +// VersionsByID_not-found_Response_Body +func ValidateVersionsByIDNotFoundResponseBody(body *VersionsByIDNotFoundResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateByCatalogKindNameVersionInternalErrorResponseBody runs the +// validations defined on ByCatalogKindNameVersion_internal-error_Response_Body +func ValidateByCatalogKindNameVersionInternalErrorResponseBody(body *ByCatalogKindNameVersionInternalErrorResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateByCatalogKindNameVersionNotFoundResponseBody runs the validations +// defined on ByCatalogKindNameVersion_not-found_Response_Body +func ValidateByCatalogKindNameVersionNotFoundResponseBody(body *ByCatalogKindNameVersionNotFoundResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateByVersionIDInternalErrorResponseBody runs the validations defined on +// ByVersionId_internal-error_Response_Body +func ValidateByVersionIDInternalErrorResponseBody(body *ByVersionIDInternalErrorResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateByVersionIDNotFoundResponseBody runs the validations defined on +// ByVersionId_not-found_Response_Body +func ValidateByVersionIDNotFoundResponseBody(body *ByVersionIDNotFoundResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateByCatalogKindNameInternalErrorResponseBody runs the validations +// defined on ByCatalogKindName_internal-error_Response_Body +func ValidateByCatalogKindNameInternalErrorResponseBody(body *ByCatalogKindNameInternalErrorResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateByCatalogKindNameNotFoundResponseBody runs the validations defined +// on ByCatalogKindName_not-found_Response_Body +func ValidateByCatalogKindNameNotFoundResponseBody(body *ByCatalogKindNameNotFoundResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateByIDInternalErrorResponseBody runs the validations defined on +// ById_internal-error_Response_Body +func ValidateByIDInternalErrorResponseBody(body *ByIDInternalErrorResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateByIDNotFoundResponseBody runs the validations defined on +// ById_not-found_Response_Body +func ValidateByIDNotFoundResponseBody(body *ByIDNotFoundResponseBody) (err error) { + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Message == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("message", "body")) + } + if body.Temporary == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("temporary", "body")) + } + if body.Timeout == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("timeout", "body")) + } + if body.Fault == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("fault", "body")) + } + return +} + +// ValidateResourceResponse runs the validations defined on ResourceResponse +func ValidateResourceResponse(body *ResourceResponse) (err error) { + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.Catalog == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("catalog", "body")) + } + if body.Kind == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("kind", "body")) + } + if body.LatestVersion == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("latestVersion", "body")) + } + if body.Tags == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("tags", "body")) + } + if body.Rating == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("rating", "body")) + } + if body.Versions == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("versions", "body")) + } + if body.Catalog != nil { + if err2 := ValidateCatalogResponse(body.Catalog); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + if body.LatestVersion != nil { + if err2 := ValidateVersionResponse(body.LatestVersion); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + for _, e := range body.Tags { + if e != nil { + if err2 := ValidateTagResponse(e); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + } + for _, e := range body.Versions { + if e != nil { + if err2 := ValidateVersionResponse(e); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + } + return +} + +// ValidateCatalogResponse runs the validations defined on CatalogResponse +func ValidateCatalogResponse(body *CatalogResponse) (err error) { + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.Type == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("type", "body")) + } + if body.Type != nil { + if !(*body.Type == "official" || *body.Type == "community") { + err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.type", *body.Type, []interface{}{"official", "community"})) + } + } + return +} + +// ValidateVersionResponse runs the validations defined on VersionResponse +func ValidateVersionResponse(body *VersionResponse) (err error) { + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Version == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("version", "body")) + } + if body.DisplayName == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("displayName", "body")) + } + if body.Description == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("description", "body")) + } + if body.MinPipelinesVersion == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("minPipelinesVersion", "body")) + } + if body.RawURL == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("rawURL", "body")) + } + if body.WebURL == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("webURL", "body")) + } + if body.UpdatedAt == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("updatedAt", "body")) + } + if body.Resource == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("resource", "body")) + } + if body.RawURL != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("body.rawURL", *body.RawURL, goa.FormatURI)) + } + if body.WebURL != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("body.webURL", *body.WebURL, goa.FormatURI)) + } + if body.UpdatedAt != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("body.updatedAt", *body.UpdatedAt, goa.FormatDateTime)) + } + if body.Resource != nil { + if err2 := ValidateResourceResponse(body.Resource); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + return +} + +// ValidateTagResponse runs the validations defined on TagResponse +func ValidateTagResponse(body *TagResponse) (err error) { + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + return +} + +// ValidateVersionResponseBody runs the validations defined on +// VersionResponseBody +func ValidateVersionResponseBody(body *VersionResponseBody) (err error) { + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Version == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("version", "body")) + } + if body.DisplayName == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("displayName", "body")) + } + if body.Description == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("description", "body")) + } + if body.MinPipelinesVersion == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("minPipelinesVersion", "body")) + } + if body.RawURL == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("rawURL", "body")) + } + if body.WebURL == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("webURL", "body")) + } + if body.UpdatedAt == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("updatedAt", "body")) + } + if body.Resource == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("resource", "body")) + } + if body.RawURL != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("body.rawURL", *body.RawURL, goa.FormatURI)) + } + if body.WebURL != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("body.webURL", *body.WebURL, goa.FormatURI)) + } + if body.UpdatedAt != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("body.updatedAt", *body.UpdatedAt, goa.FormatDateTime)) + } + if body.Resource != nil { + if err2 := ValidateResourceResponseBody(body.Resource); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + return +} + +// ValidateResourceResponseBody runs the validations defined on +// ResourceResponseBody +func ValidateResourceResponseBody(body *ResourceResponseBody) (err error) { + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.Catalog == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("catalog", "body")) + } + if body.Kind == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("kind", "body")) + } + if body.LatestVersion == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("latestVersion", "body")) + } + if body.Tags == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("tags", "body")) + } + if body.Rating == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("rating", "body")) + } + if body.Versions == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("versions", "body")) + } + if body.Catalog != nil { + if err2 := ValidateCatalogResponseBody(body.Catalog); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + if body.LatestVersion != nil { + if err2 := ValidateVersionResponseBody(body.LatestVersion); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + for _, e := range body.Tags { + if e != nil { + if err2 := ValidateTagResponseBody(e); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + } + for _, e := range body.Versions { + if e != nil { + if err2 := ValidateVersionResponseBody(e); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + } + return +} + +// ValidateCatalogResponseBody runs the validations defined on +// CatalogResponseBody +func ValidateCatalogResponseBody(body *CatalogResponseBody) (err error) { + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + if body.Type == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("type", "body")) + } + if body.Type != nil { + if !(*body.Type == "official" || *body.Type == "community") { + err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.type", *body.Type, []interface{}{"official", "community"})) + } + } + return +} + +// ValidateTagResponseBody runs the validations defined on TagResponseBody +func ValidateTagResponseBody(body *TagResponseBody) (err error) { + if body.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "body")) + } + if body.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "body")) + } + return +} diff --git a/vendor/github.com/tektoncd/hub/api/gen/resource/client.go b/vendor/github.com/tektoncd/hub/api/gen/resource/client.go new file mode 100644 index 0000000000..351565318e --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/gen/resource/client.go @@ -0,0 +1,110 @@ +// Code generated by goa v3.2.2, DO NOT EDIT. +// +// resource client +// +// Command: +// $ goa gen github.com/tektoncd/hub/api/design + +package resource + +import ( + "context" + + goa "goa.design/goa/v3/pkg" +) + +// Client is the "resource" service client. +type Client struct { + QueryEndpoint goa.Endpoint + ListEndpoint goa.Endpoint + VersionsByIDEndpoint goa.Endpoint + ByCatalogKindNameVersionEndpoint goa.Endpoint + ByVersionIDEndpoint goa.Endpoint + ByCatalogKindNameEndpoint goa.Endpoint + ByIDEndpoint goa.Endpoint +} + +// NewClient initializes a "resource" service client given the endpoints. +func NewClient(query, list, versionsByID, byCatalogKindNameVersion, byVersionID, byCatalogKindName, byID goa.Endpoint) *Client { + return &Client{ + QueryEndpoint: query, + ListEndpoint: list, + VersionsByIDEndpoint: versionsByID, + ByCatalogKindNameVersionEndpoint: byCatalogKindNameVersion, + ByVersionIDEndpoint: byVersionID, + ByCatalogKindNameEndpoint: byCatalogKindName, + ByIDEndpoint: byID, + } +} + +// Query calls the "Query" endpoint of the "resource" service. +func (c *Client) Query(ctx context.Context, p *QueryPayload) (res ResourceCollection, err error) { + var ires interface{} + ires, err = c.QueryEndpoint(ctx, p) + if err != nil { + return + } + return ires.(ResourceCollection), nil +} + +// List calls the "List" endpoint of the "resource" service. +func (c *Client) List(ctx context.Context, p *ListPayload) (res ResourceCollection, err error) { + var ires interface{} + ires, err = c.ListEndpoint(ctx, p) + if err != nil { + return + } + return ires.(ResourceCollection), nil +} + +// VersionsByID calls the "VersionsByID" endpoint of the "resource" service. +func (c *Client) VersionsByID(ctx context.Context, p *VersionsByIDPayload) (res *Versions, err error) { + var ires interface{} + ires, err = c.VersionsByIDEndpoint(ctx, p) + if err != nil { + return + } + return ires.(*Versions), nil +} + +// ByCatalogKindNameVersion calls the "ByCatalogKindNameVersion" endpoint of +// the "resource" service. +func (c *Client) ByCatalogKindNameVersion(ctx context.Context, p *ByCatalogKindNameVersionPayload) (res *Version, err error) { + var ires interface{} + ires, err = c.ByCatalogKindNameVersionEndpoint(ctx, p) + if err != nil { + return + } + return ires.(*Version), nil +} + +// ByVersionID calls the "ByVersionId" endpoint of the "resource" service. +func (c *Client) ByVersionID(ctx context.Context, p *ByVersionIDPayload) (res *Version, err error) { + var ires interface{} + ires, err = c.ByVersionIDEndpoint(ctx, p) + if err != nil { + return + } + return ires.(*Version), nil +} + +// ByCatalogKindName calls the "ByCatalogKindName" endpoint of the "resource" +// service. +func (c *Client) ByCatalogKindName(ctx context.Context, p *ByCatalogKindNamePayload) (res *Resource, err error) { + var ires interface{} + ires, err = c.ByCatalogKindNameEndpoint(ctx, p) + if err != nil { + return + } + return ires.(*Resource), nil +} + +// ByID calls the "ById" endpoint of the "resource" service. +func (c *Client) ByID(ctx context.Context, p *ByIDPayload) (res *Resource, err error) { + var ires interface{} + ires, err = c.ByIDEndpoint(ctx, p) + if err != nil { + return + } + return ires.(*Resource), nil +} diff --git a/vendor/github.com/tektoncd/hub/api/gen/resource/endpoints.go b/vendor/github.com/tektoncd/hub/api/gen/resource/endpoints.go new file mode 100644 index 0000000000..f2554d6f10 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/gen/resource/endpoints.go @@ -0,0 +1,147 @@ +// Code generated by goa v3.2.2, DO NOT EDIT. +// +// resource endpoints +// +// Command: +// $ goa gen github.com/tektoncd/hub/api/design + +package resource + +import ( + "context" + + goa "goa.design/goa/v3/pkg" +) + +// Endpoints wraps the "resource" service endpoints. +type Endpoints struct { + Query goa.Endpoint + List goa.Endpoint + VersionsByID goa.Endpoint + ByCatalogKindNameVersion goa.Endpoint + ByVersionID goa.Endpoint + ByCatalogKindName goa.Endpoint + ByID goa.Endpoint +} + +// NewEndpoints wraps the methods of the "resource" service with endpoints. +func NewEndpoints(s Service) *Endpoints { + return &Endpoints{ + Query: NewQueryEndpoint(s), + List: NewListEndpoint(s), + VersionsByID: NewVersionsByIDEndpoint(s), + ByCatalogKindNameVersion: NewByCatalogKindNameVersionEndpoint(s), + ByVersionID: NewByVersionIDEndpoint(s), + ByCatalogKindName: NewByCatalogKindNameEndpoint(s), + ByID: NewByIDEndpoint(s), + } +} + +// Use applies the given middleware to all the "resource" service endpoints. +func (e *Endpoints) Use(m func(goa.Endpoint) goa.Endpoint) { + e.Query = m(e.Query) + e.List = m(e.List) + e.VersionsByID = m(e.VersionsByID) + e.ByCatalogKindNameVersion = m(e.ByCatalogKindNameVersion) + e.ByVersionID = m(e.ByVersionID) + e.ByCatalogKindName = m(e.ByCatalogKindName) + e.ByID = m(e.ByID) +} + +// NewQueryEndpoint returns an endpoint function that calls the method "Query" +// of service "resource". +func NewQueryEndpoint(s Service) goa.Endpoint { + return func(ctx context.Context, req interface{}) (interface{}, error) { + p := req.(*QueryPayload) + res, err := s.Query(ctx, p) + if err != nil { + return nil, err + } + vres := NewViewedResourceCollection(res, "withoutVersion") + return vres, nil + } +} + +// NewListEndpoint returns an endpoint function that calls the method "List" of +// service "resource". +func NewListEndpoint(s Service) goa.Endpoint { + return func(ctx context.Context, req interface{}) (interface{}, error) { + p := req.(*ListPayload) + res, err := s.List(ctx, p) + if err != nil { + return nil, err + } + vres := NewViewedResourceCollection(res, "withoutVersion") + return vres, nil + } +} + +// NewVersionsByIDEndpoint returns an endpoint function that calls the method +// "VersionsByID" of service "resource". +func NewVersionsByIDEndpoint(s Service) goa.Endpoint { + return func(ctx context.Context, req interface{}) (interface{}, error) { + p := req.(*VersionsByIDPayload) + res, err := s.VersionsByID(ctx, p) + if err != nil { + return nil, err + } + vres := NewViewedVersions(res, "default") + return vres, nil + } +} + +// NewByCatalogKindNameVersionEndpoint returns an endpoint function that calls +// the method "ByCatalogKindNameVersion" of service "resource". +func NewByCatalogKindNameVersionEndpoint(s Service) goa.Endpoint { + return func(ctx context.Context, req interface{}) (interface{}, error) { + p := req.(*ByCatalogKindNameVersionPayload) + res, err := s.ByCatalogKindNameVersion(ctx, p) + if err != nil { + return nil, err + } + vres := NewViewedVersion(res, "default") + return vres, nil + } +} + +// NewByVersionIDEndpoint returns an endpoint function that calls the method +// "ByVersionId" of service "resource". +func NewByVersionIDEndpoint(s Service) goa.Endpoint { + return func(ctx context.Context, req interface{}) (interface{}, error) { + p := req.(*ByVersionIDPayload) + res, err := s.ByVersionID(ctx, p) + if err != nil { + return nil, err + } + vres := NewViewedVersion(res, "default") + return vres, nil + } +} + +// NewByCatalogKindNameEndpoint returns an endpoint function that calls the +// method "ByCatalogKindName" of service "resource". +func NewByCatalogKindNameEndpoint(s Service) goa.Endpoint { + return func(ctx context.Context, req interface{}) (interface{}, error) { + p := req.(*ByCatalogKindNamePayload) + res, err := s.ByCatalogKindName(ctx, p) + if err != nil { + return nil, err + } + vres := NewViewedResource(res, "default") + return vres, nil + } +} + +// NewByIDEndpoint returns an endpoint function that calls the method "ById" of +// service "resource". +func NewByIDEndpoint(s Service) goa.Endpoint { + return func(ctx context.Context, req interface{}) (interface{}, error) { + p := req.(*ByIDPayload) + res, err := s.ByID(ctx, p) + if err != nil { + return nil, err + } + vres := NewViewedResource(res, "default") + return vres, nil + } +} diff --git a/vendor/github.com/tektoncd/hub/api/gen/resource/service.go b/vendor/github.com/tektoncd/hub/api/gen/resource/service.go new file mode 100644 index 0000000000..c92b2e977e --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/gen/resource/service.go @@ -0,0 +1,902 @@ +// Code generated by goa v3.2.2, DO NOT EDIT. +// +// resource service +// +// Command: +// $ goa gen github.com/tektoncd/hub/api/design + +package resource + +import ( + "context" + + resourceviews "github.com/tektoncd/hub/api/gen/resource/views" + goa "goa.design/goa/v3/pkg" +) + +// The resource service provides details about all kind of resources +type Service interface { + // Find resources by a combination of name, kind and tags + Query(context.Context, *QueryPayload) (res ResourceCollection, err error) + // List all resources sorted by rating and name + List(context.Context, *ListPayload) (res ResourceCollection, err error) + // Find all versions of a resource by its id + VersionsByID(context.Context, *VersionsByIDPayload) (res *Versions, err error) + // Find resource using name of catalog & name, kind and version of resource + ByCatalogKindNameVersion(context.Context, *ByCatalogKindNameVersionPayload) (res *Version, err error) + // Find a resource using its version's id + ByVersionID(context.Context, *ByVersionIDPayload) (res *Version, err error) + // Find resources using name of catalog, resource name and kind of resource + ByCatalogKindName(context.Context, *ByCatalogKindNamePayload) (res *Resource, err error) + // Find a resource using it's id + ByID(context.Context, *ByIDPayload) (res *Resource, err error) +} + +// ServiceName is the name of the service as defined in the design. This is the +// same value that is set in the endpoint request contexts under the ServiceKey +// key. +const ServiceName = "resource" + +// MethodNames lists the service method names as defined in the design. These +// are the same values that are set in the endpoint request contexts under the +// MethodKey key. +var MethodNames = [7]string{"Query", "List", "VersionsByID", "ByCatalogKindNameVersion", "ByVersionId", "ByCatalogKindName", "ById"} + +// QueryPayload is the payload type of the resource service Query method. +type QueryPayload struct { + // Name of resource + Name string + // Kinds of resource to filter by + Kinds []string + // Tags associated with a resource to filter by + Tags []string + // Maximum number of resources to be returned + Limit uint + // Strategy used to find matching resources + Match string +} + +// ResourceCollection is the result type of the resource service Query method. +type ResourceCollection []*Resource + +// ListPayload is the payload type of the resource service List method. +type ListPayload struct { + // Maximum number of resources to be returned + Limit uint +} + +// VersionsByIDPayload is the payload type of the resource service VersionsByID +// method. +type VersionsByIDPayload struct { + // ID of a resource + ID uint +} + +// Versions is the result type of the resource service VersionsByID method. +type Versions struct { + // Latest Version of resource + Latest *Version + // List of all versions of resource + Versions []*Version +} + +// ByCatalogKindNameVersionPayload is the payload type of the resource service +// ByCatalogKindNameVersion method. +type ByCatalogKindNameVersionPayload struct { + // name of catalog + Catalog string + // kind of resource + Kind string + // name of resource + Name string + // version of resource + Version string +} + +// Version is the result type of the resource service ByCatalogKindNameVersion +// method. +type Version struct { + // ID is the unique id of resource's version + ID uint + // Version of resource + Version string + // Display name of version + DisplayName string + // Description of version + Description string + // Minimum pipelines version the resource's version is compatible with + MinPipelinesVersion string + // Raw URL of resource's yaml file of the version + RawURL string + // Web URL of resource's yaml file of the version + WebURL string + // Timestamp when version was last updated + UpdatedAt string + // Resource to which the version belongs + Resource *Resource +} + +// ByVersionIDPayload is the payload type of the resource service ByVersionId +// method. +type ByVersionIDPayload struct { + // Version ID of a resource's version + VersionID uint +} + +// ByCatalogKindNamePayload is the payload type of the resource service +// ByCatalogKindName method. +type ByCatalogKindNamePayload struct { + // name of catalog + Catalog string + // kind of resource + Kind string + // Name of resource + Name string +} + +// Resource is the result type of the resource service ByCatalogKindName method. +type Resource struct { + // ID is the unique id of the resource + ID uint + // Name of resource + Name string + // Type of catalog to which resource belongs + Catalog *Catalog + // Kind of resource + Kind string + // Latest version of resource + LatestVersion *Version + // Tags related to resource + Tags []*Tag + // Rating of resource + Rating float64 + // List of all versions of a resource + Versions []*Version +} + +// ByIDPayload is the payload type of the resource service ById method. +type ByIDPayload struct { + // ID of a resource + ID uint +} + +type Catalog struct { + // ID is the unique id of the catalog + ID uint + // Name of catalog + Name string + // Type of catalog + Type string +} + +type Tag struct { + // ID is the unique id of tag + ID uint + // Name of tag + Name string +} + +// MakeInternalError builds a goa.ServiceError from an error. +func MakeInternalError(err error) *goa.ServiceError { + return &goa.ServiceError{ + Name: "internal-error", + ID: goa.NewErrorID(), + Message: err.Error(), + } +} + +// MakeNotFound builds a goa.ServiceError from an error. +func MakeNotFound(err error) *goa.ServiceError { + return &goa.ServiceError{ + Name: "not-found", + ID: goa.NewErrorID(), + Message: err.Error(), + } +} + +// MakeInvalidKind builds a goa.ServiceError from an error. +func MakeInvalidKind(err error) *goa.ServiceError { + return &goa.ServiceError{ + Name: "invalid-kind", + ID: goa.NewErrorID(), + Message: err.Error(), + } +} + +// NewResourceCollection initializes result type ResourceCollection from viewed +// result type ResourceCollection. +func NewResourceCollection(vres resourceviews.ResourceCollection) ResourceCollection { + var res ResourceCollection + switch vres.View { + case "info": + res = newResourceCollectionInfo(vres.Projected) + case "withoutVersion": + res = newResourceCollectionWithoutVersion(vres.Projected) + case "default", "": + res = newResourceCollection(vres.Projected) + } + return res +} + +// NewViewedResourceCollection initializes viewed result type +// ResourceCollection from result type ResourceCollection using the given view. +func NewViewedResourceCollection(res ResourceCollection, view string) resourceviews.ResourceCollection { + var vres resourceviews.ResourceCollection + switch view { + case "info": + p := newResourceCollectionViewInfo(res) + vres = resourceviews.ResourceCollection{Projected: p, View: "info"} + case "withoutVersion": + p := newResourceCollectionViewWithoutVersion(res) + vres = resourceviews.ResourceCollection{Projected: p, View: "withoutVersion"} + case "default", "": + p := newResourceCollectionView(res) + vres = resourceviews.ResourceCollection{Projected: p, View: "default"} + } + return vres +} + +// NewVersions initializes result type Versions from viewed result type +// Versions. +func NewVersions(vres *resourceviews.Versions) *Versions { + return newVersions(vres.Projected) +} + +// NewViewedVersions initializes viewed result type Versions from result type +// Versions using the given view. +func NewViewedVersions(res *Versions, view string) *resourceviews.Versions { + p := newVersionsView(res) + return &resourceviews.Versions{Projected: p, View: "default"} +} + +// NewVersion initializes result type Version from viewed result type Version. +func NewVersion(vres *resourceviews.Version) *Version { + var res *Version + switch vres.View { + case "tiny": + res = newVersionTiny(vres.Projected) + case "min": + res = newVersionMin(vres.Projected) + case "withoutResource": + res = newVersionWithoutResource(vres.Projected) + case "default", "": + res = newVersion(vres.Projected) + } + return res +} + +// NewViewedVersion initializes viewed result type Version from result type +// Version using the given view. +func NewViewedVersion(res *Version, view string) *resourceviews.Version { + var vres *resourceviews.Version + switch view { + case "tiny": + p := newVersionViewTiny(res) + vres = &resourceviews.Version{Projected: p, View: "tiny"} + case "min": + p := newVersionViewMin(res) + vres = &resourceviews.Version{Projected: p, View: "min"} + case "withoutResource": + p := newVersionViewWithoutResource(res) + vres = &resourceviews.Version{Projected: p, View: "withoutResource"} + case "default", "": + p := newVersionView(res) + vres = &resourceviews.Version{Projected: p, View: "default"} + } + return vres +} + +// NewResource initializes result type Resource from viewed result type +// Resource. +func NewResource(vres *resourceviews.Resource) *Resource { + var res *Resource + switch vres.View { + case "info": + res = newResourceInfo(vres.Projected) + case "withoutVersion": + res = newResourceWithoutVersion(vres.Projected) + case "default", "": + res = newResource(vres.Projected) + } + return res +} + +// NewViewedResource initializes viewed result type Resource from result type +// Resource using the given view. +func NewViewedResource(res *Resource, view string) *resourceviews.Resource { + var vres *resourceviews.Resource + switch view { + case "info": + p := newResourceViewInfo(res) + vres = &resourceviews.Resource{Projected: p, View: "info"} + case "withoutVersion": + p := newResourceViewWithoutVersion(res) + vres = &resourceviews.Resource{Projected: p, View: "withoutVersion"} + case "default", "": + p := newResourceView(res) + vres = &resourceviews.Resource{Projected: p, View: "default"} + } + return vres +} + +// newResourceCollectionInfo converts projected type ResourceCollection to +// service type ResourceCollection. +func newResourceCollectionInfo(vres resourceviews.ResourceCollectionView) ResourceCollection { + res := make(ResourceCollection, len(vres)) + for i, n := range vres { + res[i] = newResourceInfo(n) + } + return res +} + +// newResourceCollectionWithoutVersion converts projected type +// ResourceCollection to service type ResourceCollection. +func newResourceCollectionWithoutVersion(vres resourceviews.ResourceCollectionView) ResourceCollection { + res := make(ResourceCollection, len(vres)) + for i, n := range vres { + res[i] = newResourceWithoutVersion(n) + } + return res +} + +// newResourceCollection converts projected type ResourceCollection to service +// type ResourceCollection. +func newResourceCollection(vres resourceviews.ResourceCollectionView) ResourceCollection { + res := make(ResourceCollection, len(vres)) + for i, n := range vres { + res[i] = newResource(n) + } + return res +} + +// newResourceCollectionViewInfo projects result type ResourceCollection to +// projected type ResourceCollectionView using the "info" view. +func newResourceCollectionViewInfo(res ResourceCollection) resourceviews.ResourceCollectionView { + vres := make(resourceviews.ResourceCollectionView, len(res)) + for i, n := range res { + vres[i] = newResourceViewInfo(n) + } + return vres +} + +// newResourceCollectionViewWithoutVersion projects result type +// ResourceCollection to projected type ResourceCollectionView using the +// "withoutVersion" view. +func newResourceCollectionViewWithoutVersion(res ResourceCollection) resourceviews.ResourceCollectionView { + vres := make(resourceviews.ResourceCollectionView, len(res)) + for i, n := range res { + vres[i] = newResourceViewWithoutVersion(n) + } + return vres +} + +// newResourceCollectionView projects result type ResourceCollection to +// projected type ResourceCollectionView using the "default" view. +func newResourceCollectionView(res ResourceCollection) resourceviews.ResourceCollectionView { + vres := make(resourceviews.ResourceCollectionView, len(res)) + for i, n := range res { + vres[i] = newResourceView(n) + } + return vres +} + +// newResourceInfo converts projected type Resource to service type Resource. +func newResourceInfo(vres *resourceviews.ResourceView) *Resource { + res := &Resource{} + if vres.ID != nil { + res.ID = *vres.ID + } + if vres.Name != nil { + res.Name = *vres.Name + } + if vres.Kind != nil { + res.Kind = *vres.Kind + } + if vres.Rating != nil { + res.Rating = *vres.Rating + } + if vres.Catalog != nil { + res.Catalog = transformResourceviewsCatalogViewToCatalog(vres.Catalog) + } + if vres.Tags != nil { + res.Tags = make([]*Tag, len(vres.Tags)) + for i, val := range vres.Tags { + res.Tags[i] = transformResourceviewsTagViewToTag(val) + } + } + if vres.LatestVersion != nil { + res.LatestVersion = newVersion(vres.LatestVersion) + } + return res +} + +// newResourceWithoutVersion converts projected type Resource to service type +// Resource. +func newResourceWithoutVersion(vres *resourceviews.ResourceView) *Resource { + res := &Resource{} + if vres.ID != nil { + res.ID = *vres.ID + } + if vres.Name != nil { + res.Name = *vres.Name + } + if vres.Kind != nil { + res.Kind = *vres.Kind + } + if vres.Rating != nil { + res.Rating = *vres.Rating + } + if vres.Catalog != nil { + res.Catalog = transformResourceviewsCatalogViewToCatalog(vres.Catalog) + } + if vres.Tags != nil { + res.Tags = make([]*Tag, len(vres.Tags)) + for i, val := range vres.Tags { + res.Tags[i] = transformResourceviewsTagViewToTag(val) + } + } + if vres.LatestVersion != nil { + res.LatestVersion = newVersionWithoutResource(vres.LatestVersion) + } + return res +} + +// newResource converts projected type Resource to service type Resource. +func newResource(vres *resourceviews.ResourceView) *Resource { + res := &Resource{} + if vres.ID != nil { + res.ID = *vres.ID + } + if vres.Name != nil { + res.Name = *vres.Name + } + if vres.Kind != nil { + res.Kind = *vres.Kind + } + if vres.Rating != nil { + res.Rating = *vres.Rating + } + if vres.Catalog != nil { + res.Catalog = transformResourceviewsCatalogViewToCatalog(vres.Catalog) + } + if vres.Tags != nil { + res.Tags = make([]*Tag, len(vres.Tags)) + for i, val := range vres.Tags { + res.Tags[i] = transformResourceviewsTagViewToTag(val) + } + } + if vres.Versions != nil { + res.Versions = make([]*Version, len(vres.Versions)) + for i, val := range vres.Versions { + res.Versions[i] = transformResourceviewsVersionViewToVersion(val) + } + } + if vres.LatestVersion != nil { + res.LatestVersion = newVersionWithoutResource(vres.LatestVersion) + } + return res +} + +// newResourceViewInfo projects result type Resource to projected type +// ResourceView using the "info" view. +func newResourceViewInfo(res *Resource) *resourceviews.ResourceView { + vres := &resourceviews.ResourceView{ + ID: &res.ID, + Name: &res.Name, + Kind: &res.Kind, + Rating: &res.Rating, + } + if res.Catalog != nil { + vres.Catalog = transformCatalogToResourceviewsCatalogView(res.Catalog) + } + if res.Tags != nil { + vres.Tags = make([]*resourceviews.TagView, len(res.Tags)) + for i, val := range res.Tags { + vres.Tags[i] = transformTagToResourceviewsTagView(val) + } + } + return vres +} + +// newResourceViewWithoutVersion projects result type Resource to projected +// type ResourceView using the "withoutVersion" view. +func newResourceViewWithoutVersion(res *Resource) *resourceviews.ResourceView { + vres := &resourceviews.ResourceView{ + ID: &res.ID, + Name: &res.Name, + Kind: &res.Kind, + Rating: &res.Rating, + } + if res.Catalog != nil { + vres.Catalog = transformCatalogToResourceviewsCatalogView(res.Catalog) + } + if res.Tags != nil { + vres.Tags = make([]*resourceviews.TagView, len(res.Tags)) + for i, val := range res.Tags { + vres.Tags[i] = transformTagToResourceviewsTagView(val) + } + } + if res.LatestVersion != nil { + vres.LatestVersion = newVersionViewWithoutResource(res.LatestVersion) + } + return vres +} + +// newResourceView projects result type Resource to projected type ResourceView +// using the "default" view. +func newResourceView(res *Resource) *resourceviews.ResourceView { + vres := &resourceviews.ResourceView{ + ID: &res.ID, + Name: &res.Name, + Kind: &res.Kind, + Rating: &res.Rating, + } + if res.Catalog != nil { + vres.Catalog = transformCatalogToResourceviewsCatalogView(res.Catalog) + } + if res.Tags != nil { + vres.Tags = make([]*resourceviews.TagView, len(res.Tags)) + for i, val := range res.Tags { + vres.Tags[i] = transformTagToResourceviewsTagView(val) + } + } + if res.Versions != nil { + vres.Versions = make([]*resourceviews.VersionView, len(res.Versions)) + for i, val := range res.Versions { + vres.Versions[i] = transformVersionToResourceviewsVersionView(val) + } + } + if res.LatestVersion != nil { + vres.LatestVersion = newVersionViewWithoutResource(res.LatestVersion) + } + return vres +} + +// newVersionTiny converts projected type Version to service type Version. +func newVersionTiny(vres *resourceviews.VersionView) *Version { + res := &Version{} + if vres.ID != nil { + res.ID = *vres.ID + } + if vres.Version != nil { + res.Version = *vres.Version + } + if vres.Resource != nil { + res.Resource = newResource(vres.Resource) + } + return res +} + +// newVersionMin converts projected type Version to service type Version. +func newVersionMin(vres *resourceviews.VersionView) *Version { + res := &Version{} + if vres.ID != nil { + res.ID = *vres.ID + } + if vres.Version != nil { + res.Version = *vres.Version + } + if vres.RawURL != nil { + res.RawURL = *vres.RawURL + } + if vres.WebURL != nil { + res.WebURL = *vres.WebURL + } + if vres.Resource != nil { + res.Resource = newResource(vres.Resource) + } + return res +} + +// newVersionWithoutResource converts projected type Version to service type +// Version. +func newVersionWithoutResource(vres *resourceviews.VersionView) *Version { + res := &Version{} + if vres.ID != nil { + res.ID = *vres.ID + } + if vres.Version != nil { + res.Version = *vres.Version + } + if vres.DisplayName != nil { + res.DisplayName = *vres.DisplayName + } + if vres.Description != nil { + res.Description = *vres.Description + } + if vres.MinPipelinesVersion != nil { + res.MinPipelinesVersion = *vres.MinPipelinesVersion + } + if vres.RawURL != nil { + res.RawURL = *vres.RawURL + } + if vres.WebURL != nil { + res.WebURL = *vres.WebURL + } + if vres.UpdatedAt != nil { + res.UpdatedAt = *vres.UpdatedAt + } + if vres.Resource != nil { + res.Resource = newResource(vres.Resource) + } + return res +} + +// newVersion converts projected type Version to service type Version. +func newVersion(vres *resourceviews.VersionView) *Version { + res := &Version{} + if vres.ID != nil { + res.ID = *vres.ID + } + if vres.Version != nil { + res.Version = *vres.Version + } + if vres.DisplayName != nil { + res.DisplayName = *vres.DisplayName + } + if vres.Description != nil { + res.Description = *vres.Description + } + if vres.MinPipelinesVersion != nil { + res.MinPipelinesVersion = *vres.MinPipelinesVersion + } + if vres.RawURL != nil { + res.RawURL = *vres.RawURL + } + if vres.WebURL != nil { + res.WebURL = *vres.WebURL + } + if vres.UpdatedAt != nil { + res.UpdatedAt = *vres.UpdatedAt + } + if vres.Resource != nil { + res.Resource = newResourceInfo(vres.Resource) + } + return res +} + +// newVersionViewTiny projects result type Version to projected type +// VersionView using the "tiny" view. +func newVersionViewTiny(res *Version) *resourceviews.VersionView { + vres := &resourceviews.VersionView{ + ID: &res.ID, + Version: &res.Version, + } + return vres +} + +// newVersionViewMin projects result type Version to projected type VersionView +// using the "min" view. +func newVersionViewMin(res *Version) *resourceviews.VersionView { + vres := &resourceviews.VersionView{ + ID: &res.ID, + Version: &res.Version, + RawURL: &res.RawURL, + WebURL: &res.WebURL, + } + return vres +} + +// newVersionViewWithoutResource projects result type Version to projected type +// VersionView using the "withoutResource" view. +func newVersionViewWithoutResource(res *Version) *resourceviews.VersionView { + vres := &resourceviews.VersionView{ + ID: &res.ID, + Version: &res.Version, + DisplayName: &res.DisplayName, + Description: &res.Description, + MinPipelinesVersion: &res.MinPipelinesVersion, + RawURL: &res.RawURL, + WebURL: &res.WebURL, + UpdatedAt: &res.UpdatedAt, + } + return vres +} + +// newVersionView projects result type Version to projected type VersionView +// using the "default" view. +func newVersionView(res *Version) *resourceviews.VersionView { + vres := &resourceviews.VersionView{ + ID: &res.ID, + Version: &res.Version, + DisplayName: &res.DisplayName, + Description: &res.Description, + MinPipelinesVersion: &res.MinPipelinesVersion, + RawURL: &res.RawURL, + WebURL: &res.WebURL, + UpdatedAt: &res.UpdatedAt, + } + if res.Resource != nil { + vres.Resource = newResourceViewInfo(res.Resource) + } + return vres +} + +// newVersions converts projected type Versions to service type Versions. +func newVersions(vres *resourceviews.VersionsView) *Versions { + res := &Versions{} + if vres.Versions != nil { + res.Versions = make([]*Version, len(vres.Versions)) + for i, val := range vres.Versions { + res.Versions[i] = transformResourceviewsVersionViewToVersion(val) + } + } + if vres.Latest != nil { + res.Latest = newVersionMin(vres.Latest) + } + return res +} + +// newVersionsView projects result type Versions to projected type VersionsView +// using the "default" view. +func newVersionsView(res *Versions) *resourceviews.VersionsView { + vres := &resourceviews.VersionsView{} + if res.Versions != nil { + vres.Versions = make([]*resourceviews.VersionView, len(res.Versions)) + for i, val := range res.Versions { + vres.Versions[i] = transformVersionToResourceviewsVersionView(val) + } + } + if res.Latest != nil { + vres.Latest = newVersionViewMin(res.Latest) + } + return vres +} + +// transformResourceviewsCatalogViewToCatalog builds a value of type *Catalog +// from a value of type *resourceviews.CatalogView. +func transformResourceviewsCatalogViewToCatalog(v *resourceviews.CatalogView) *Catalog { + if v == nil { + return nil + } + res := &Catalog{ + ID: *v.ID, + Name: *v.Name, + Type: *v.Type, + } + + return res +} + +// transformResourceviewsTagViewToTag builds a value of type *Tag from a value +// of type *resourceviews.TagView. +func transformResourceviewsTagViewToTag(v *resourceviews.TagView) *Tag { + if v == nil { + return nil + } + res := &Tag{ + ID: *v.ID, + Name: *v.Name, + } + + return res +} + +// transformResourceviewsVersionViewToVersion builds a value of type *Version +// from a value of type *resourceviews.VersionView. +func transformResourceviewsVersionViewToVersion(v *resourceviews.VersionView) *Version { + if v == nil { + return nil + } + res := &Version{ + ID: *v.ID, + Version: *v.Version, + DisplayName: *v.DisplayName, + Description: *v.Description, + MinPipelinesVersion: *v.MinPipelinesVersion, + RawURL: *v.RawURL, + WebURL: *v.WebURL, + UpdatedAt: *v.UpdatedAt, + } + if v.Resource != nil { + res.Resource = transformResourceviewsResourceViewToResource(v.Resource) + } + + return res +} + +// transformResourceviewsResourceViewToResource builds a value of type +// *Resource from a value of type *resourceviews.ResourceView. +func transformResourceviewsResourceViewToResource(v *resourceviews.ResourceView) *Resource { + res := &Resource{} + if v.ID != nil { + res.ID = *v.ID + } + if v.Name != nil { + res.Name = *v.Name + } + if v.Kind != nil { + res.Kind = *v.Kind + } + if v.Rating != nil { + res.Rating = *v.Rating + } + if v.Catalog != nil { + res.Catalog = transformResourceviewsCatalogViewToCatalog(v.Catalog) + } + if v.Tags != nil { + res.Tags = make([]*Tag, len(v.Tags)) + for i, val := range v.Tags { + res.Tags[i] = transformResourceviewsTagViewToTag(val) + } + } + if v.Versions != nil { + res.Versions = make([]*Version, len(v.Versions)) + for i, val := range v.Versions { + res.Versions[i] = transformResourceviewsVersionViewToVersion(val) + } + } + + return res +} + +// transformCatalogToResourceviewsCatalogView builds a value of type +// *resourceviews.CatalogView from a value of type *Catalog. +func transformCatalogToResourceviewsCatalogView(v *Catalog) *resourceviews.CatalogView { + res := &resourceviews.CatalogView{ + ID: &v.ID, + Name: &v.Name, + Type: &v.Type, + } + + return res +} + +// transformTagToResourceviewsTagView builds a value of type +// *resourceviews.TagView from a value of type *Tag. +func transformTagToResourceviewsTagView(v *Tag) *resourceviews.TagView { + res := &resourceviews.TagView{ + ID: &v.ID, + Name: &v.Name, + } + + return res +} + +// transformVersionToResourceviewsVersionView builds a value of type +// *resourceviews.VersionView from a value of type *Version. +func transformVersionToResourceviewsVersionView(v *Version) *resourceviews.VersionView { + res := &resourceviews.VersionView{ + ID: &v.ID, + Version: &v.Version, + DisplayName: &v.DisplayName, + Description: &v.Description, + MinPipelinesVersion: &v.MinPipelinesVersion, + RawURL: &v.RawURL, + WebURL: &v.WebURL, + UpdatedAt: &v.UpdatedAt, + } + if v.Resource != nil { + res.Resource = transformResourceToResourceviewsResourceView(v.Resource) + } + + return res +} + +// transformResourceToResourceviewsResourceView builds a value of type +// *resourceviews.ResourceView from a value of type *Resource. +func transformResourceToResourceviewsResourceView(v *Resource) *resourceviews.ResourceView { + res := &resourceviews.ResourceView{ + ID: &v.ID, + Name: &v.Name, + Kind: &v.Kind, + Rating: &v.Rating, + } + if v.Catalog != nil { + res.Catalog = transformCatalogToResourceviewsCatalogView(v.Catalog) + } + if v.Tags != nil { + res.Tags = make([]*resourceviews.TagView, len(v.Tags)) + for i, val := range v.Tags { + res.Tags[i] = transformTagToResourceviewsTagView(val) + } + } + if v.Versions != nil { + res.Versions = make([]*resourceviews.VersionView, len(v.Versions)) + for i, val := range v.Versions { + res.Versions[i] = transformVersionToResourceviewsVersionView(val) + } + } + + return res +} diff --git a/vendor/github.com/tektoncd/hub/api/gen/resource/views/view.go b/vendor/github.com/tektoncd/hub/api/gen/resource/views/view.go new file mode 100644 index 0000000000..da74d8b3fb --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/gen/resource/views/view.go @@ -0,0 +1,617 @@ +// Code generated by goa v3.2.2, DO NOT EDIT. +// +// resource views +// +// Command: +// $ goa gen github.com/tektoncd/hub/api/design + +package views + +import ( + goa "goa.design/goa/v3/pkg" +) + +// ResourceCollection is the viewed result type that is projected based on a +// view. +type ResourceCollection struct { + // Type to project + Projected ResourceCollectionView + // View to render + View string +} + +// Versions is the viewed result type that is projected based on a view. +type Versions struct { + // Type to project + Projected *VersionsView + // View to render + View string +} + +// Version is the viewed result type that is projected based on a view. +type Version struct { + // Type to project + Projected *VersionView + // View to render + View string +} + +// Resource is the viewed result type that is projected based on a view. +type Resource struct { + // Type to project + Projected *ResourceView + // View to render + View string +} + +// ResourceCollectionView is a type that runs validations on a projected type. +type ResourceCollectionView []*ResourceView + +// ResourceView is a type that runs validations on a projected type. +type ResourceView struct { + // ID is the unique id of the resource + ID *uint + // Name of resource + Name *string + // Type of catalog to which resource belongs + Catalog *CatalogView + // Kind of resource + Kind *string + // Latest version of resource + LatestVersion *VersionView + // Tags related to resource + Tags []*TagView + // Rating of resource + Rating *float64 + // List of all versions of a resource + Versions []*VersionView +} + +// CatalogView is a type that runs validations on a projected type. +type CatalogView struct { + // ID is the unique id of the catalog + ID *uint + // Name of catalog + Name *string + // Type of catalog + Type *string +} + +// VersionView is a type that runs validations on a projected type. +type VersionView struct { + // ID is the unique id of resource's version + ID *uint + // Version of resource + Version *string + // Display name of version + DisplayName *string + // Description of version + Description *string + // Minimum pipelines version the resource's version is compatible with + MinPipelinesVersion *string + // Raw URL of resource's yaml file of the version + RawURL *string + // Web URL of resource's yaml file of the version + WebURL *string + // Timestamp when version was last updated + UpdatedAt *string + // Resource to which the version belongs + Resource *ResourceView +} + +// TagView is a type that runs validations on a projected type. +type TagView struct { + // ID is the unique id of tag + ID *uint + // Name of tag + Name *string +} + +// VersionsView is a type that runs validations on a projected type. +type VersionsView struct { + // Latest Version of resource + Latest *VersionView + // List of all versions of resource + Versions []*VersionView +} + +var ( + // ResourceCollectionMap is a map of attribute names in result type + // ResourceCollection indexed by view name. + ResourceCollectionMap = map[string][]string{ + "info": []string{ + "id", + "name", + "catalog", + "kind", + "tags", + "rating", + }, + "withoutVersion": []string{ + "id", + "name", + "catalog", + "kind", + "latestVersion", + "tags", + "rating", + }, + "default": []string{ + "id", + "name", + "catalog", + "kind", + "latestVersion", + "tags", + "rating", + "versions", + }, + } + // VersionsMap is a map of attribute names in result type Versions indexed by + // view name. + VersionsMap = map[string][]string{ + "default": []string{ + "latest", + "versions", + }, + } + // VersionMap is a map of attribute names in result type Version indexed by + // view name. + VersionMap = map[string][]string{ + "tiny": []string{ + "id", + "version", + }, + "min": []string{ + "id", + "version", + "rawURL", + "webURL", + }, + "withoutResource": []string{ + "id", + "version", + "displayName", + "description", + "minPipelinesVersion", + "rawURL", + "webURL", + "updatedAt", + }, + "default": []string{ + "id", + "version", + "displayName", + "description", + "minPipelinesVersion", + "rawURL", + "webURL", + "updatedAt", + "resource", + }, + } + // ResourceMap is a map of attribute names in result type Resource indexed by + // view name. + ResourceMap = map[string][]string{ + "info": []string{ + "id", + "name", + "catalog", + "kind", + "tags", + "rating", + }, + "withoutVersion": []string{ + "id", + "name", + "catalog", + "kind", + "latestVersion", + "tags", + "rating", + }, + "default": []string{ + "id", + "name", + "catalog", + "kind", + "latestVersion", + "tags", + "rating", + "versions", + }, + } +) + +// ValidateResourceCollection runs the validations defined on the viewed result +// type ResourceCollection. +func ValidateResourceCollection(result ResourceCollection) (err error) { + switch result.View { + case "info": + err = ValidateResourceCollectionViewInfo(result.Projected) + case "withoutVersion": + err = ValidateResourceCollectionViewWithoutVersion(result.Projected) + case "default", "": + err = ValidateResourceCollectionView(result.Projected) + default: + err = goa.InvalidEnumValueError("view", result.View, []interface{}{"info", "withoutVersion", "default"}) + } + return +} + +// ValidateVersions runs the validations defined on the viewed result type +// Versions. +func ValidateVersions(result *Versions) (err error) { + switch result.View { + case "default", "": + err = ValidateVersionsView(result.Projected) + default: + err = goa.InvalidEnumValueError("view", result.View, []interface{}{"default"}) + } + return +} + +// ValidateVersion runs the validations defined on the viewed result type +// Version. +func ValidateVersion(result *Version) (err error) { + switch result.View { + case "tiny": + err = ValidateVersionViewTiny(result.Projected) + case "min": + err = ValidateVersionViewMin(result.Projected) + case "withoutResource": + err = ValidateVersionViewWithoutResource(result.Projected) + case "default", "": + err = ValidateVersionView(result.Projected) + default: + err = goa.InvalidEnumValueError("view", result.View, []interface{}{"tiny", "min", "withoutResource", "default"}) + } + return +} + +// ValidateResource runs the validations defined on the viewed result type +// Resource. +func ValidateResource(result *Resource) (err error) { + switch result.View { + case "info": + err = ValidateResourceViewInfo(result.Projected) + case "withoutVersion": + err = ValidateResourceViewWithoutVersion(result.Projected) + case "default", "": + err = ValidateResourceView(result.Projected) + default: + err = goa.InvalidEnumValueError("view", result.View, []interface{}{"info", "withoutVersion", "default"}) + } + return +} + +// ValidateResourceCollectionViewInfo runs the validations defined on +// ResourceCollectionView using the "info" view. +func ValidateResourceCollectionViewInfo(result ResourceCollectionView) (err error) { + for _, item := range result { + if err2 := ValidateResourceViewInfo(item); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + return +} + +// ValidateResourceCollectionViewWithoutVersion runs the validations defined on +// ResourceCollectionView using the "withoutVersion" view. +func ValidateResourceCollectionViewWithoutVersion(result ResourceCollectionView) (err error) { + for _, item := range result { + if err2 := ValidateResourceViewWithoutVersion(item); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + return +} + +// ValidateResourceCollectionView runs the validations defined on +// ResourceCollectionView using the "default" view. +func ValidateResourceCollectionView(result ResourceCollectionView) (err error) { + for _, item := range result { + if err2 := ValidateResourceView(item); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + return +} + +// ValidateResourceViewInfo runs the validations defined on ResourceView using +// the "info" view. +func ValidateResourceViewInfo(result *ResourceView) (err error) { + if result.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "result")) + } + if result.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "result")) + } + if result.Catalog == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("catalog", "result")) + } + if result.Kind == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("kind", "result")) + } + if result.Tags == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("tags", "result")) + } + if result.Rating == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("rating", "result")) + } + if result.Catalog != nil { + if err2 := ValidateCatalogView(result.Catalog); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + for _, e := range result.Tags { + if e != nil { + if err2 := ValidateTagView(e); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + } + return +} + +// ValidateResourceViewWithoutVersion runs the validations defined on +// ResourceView using the "withoutVersion" view. +func ValidateResourceViewWithoutVersion(result *ResourceView) (err error) { + if result.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "result")) + } + if result.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "result")) + } + if result.Catalog == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("catalog", "result")) + } + if result.Kind == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("kind", "result")) + } + if result.Tags == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("tags", "result")) + } + if result.Rating == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("rating", "result")) + } + if result.Catalog != nil { + if err2 := ValidateCatalogView(result.Catalog); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + for _, e := range result.Tags { + if e != nil { + if err2 := ValidateTagView(e); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + } + if result.LatestVersion != nil { + if err2 := ValidateVersionViewWithoutResource(result.LatestVersion); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + return +} + +// ValidateResourceView runs the validations defined on ResourceView using the +// "default" view. +func ValidateResourceView(result *ResourceView) (err error) { + if result.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "result")) + } + if result.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "result")) + } + if result.Catalog == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("catalog", "result")) + } + if result.Kind == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("kind", "result")) + } + if result.Tags == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("tags", "result")) + } + if result.Rating == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("rating", "result")) + } + if result.Versions == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("versions", "result")) + } + if result.Catalog != nil { + if err2 := ValidateCatalogView(result.Catalog); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + for _, e := range result.Tags { + if e != nil { + if err2 := ValidateTagView(e); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + } + for _, e := range result.Versions { + if e != nil { + if err2 := ValidateVersionView(e); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + } + if result.LatestVersion != nil { + if err2 := ValidateVersionViewWithoutResource(result.LatestVersion); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + return +} + +// ValidateCatalogView runs the validations defined on CatalogView. +func ValidateCatalogView(result *CatalogView) (err error) { + if result.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "result")) + } + if result.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "result")) + } + if result.Type == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("type", "result")) + } + if result.Type != nil { + if !(*result.Type == "official" || *result.Type == "community") { + err = goa.MergeErrors(err, goa.InvalidEnumValueError("result.type", *result.Type, []interface{}{"official", "community"})) + } + } + return +} + +// ValidateVersionViewTiny runs the validations defined on VersionView using +// the "tiny" view. +func ValidateVersionViewTiny(result *VersionView) (err error) { + if result.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "result")) + } + if result.Version == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("version", "result")) + } + return +} + +// ValidateVersionViewMin runs the validations defined on VersionView using the +// "min" view. +func ValidateVersionViewMin(result *VersionView) (err error) { + if result.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "result")) + } + if result.Version == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("version", "result")) + } + if result.RawURL == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("rawURL", "result")) + } + if result.WebURL == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("webURL", "result")) + } + if result.RawURL != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("result.rawURL", *result.RawURL, goa.FormatURI)) + } + if result.WebURL != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("result.webURL", *result.WebURL, goa.FormatURI)) + } + return +} + +// ValidateVersionViewWithoutResource runs the validations defined on +// VersionView using the "withoutResource" view. +func ValidateVersionViewWithoutResource(result *VersionView) (err error) { + if result.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "result")) + } + if result.Version == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("version", "result")) + } + if result.DisplayName == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("displayName", "result")) + } + if result.Description == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("description", "result")) + } + if result.MinPipelinesVersion == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("minPipelinesVersion", "result")) + } + if result.RawURL == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("rawURL", "result")) + } + if result.WebURL == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("webURL", "result")) + } + if result.UpdatedAt == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("updatedAt", "result")) + } + if result.RawURL != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("result.rawURL", *result.RawURL, goa.FormatURI)) + } + if result.WebURL != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("result.webURL", *result.WebURL, goa.FormatURI)) + } + if result.UpdatedAt != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("result.updatedAt", *result.UpdatedAt, goa.FormatDateTime)) + } + return +} + +// ValidateVersionView runs the validations defined on VersionView using the +// "default" view. +func ValidateVersionView(result *VersionView) (err error) { + if result.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "result")) + } + if result.Version == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("version", "result")) + } + if result.DisplayName == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("displayName", "result")) + } + if result.Description == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("description", "result")) + } + if result.MinPipelinesVersion == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("minPipelinesVersion", "result")) + } + if result.RawURL == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("rawURL", "result")) + } + if result.WebURL == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("webURL", "result")) + } + if result.UpdatedAt == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("updatedAt", "result")) + } + if result.RawURL != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("result.rawURL", *result.RawURL, goa.FormatURI)) + } + if result.WebURL != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("result.webURL", *result.WebURL, goa.FormatURI)) + } + if result.UpdatedAt != nil { + err = goa.MergeErrors(err, goa.ValidateFormat("result.updatedAt", *result.UpdatedAt, goa.FormatDateTime)) + } + if result.Resource != nil { + if err2 := ValidateResourceViewInfo(result.Resource); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + return +} + +// ValidateTagView runs the validations defined on TagView. +func ValidateTagView(result *TagView) (err error) { + if result.ID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("id", "result")) + } + if result.Name == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("name", "result")) + } + return +} + +// ValidateVersionsView runs the validations defined on VersionsView using the +// "default" view. +func ValidateVersionsView(result *VersionsView) (err error) { + if result.Versions == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("versions", "result")) + } + for _, e := range result.Versions { + if e != nil { + if err2 := ValidateVersionView(e); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + } + if result.Latest != nil { + if err2 := ValidateVersionViewMin(result.Latest); err2 != nil { + err = goa.MergeErrors(err, err2) + } + } + return +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/cli/app/app.go b/vendor/github.com/tektoncd/hub/api/pkg/cli/app/app.go new file mode 100644 index 0000000000..1fc70a70d0 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/cli/app/app.go @@ -0,0 +1,55 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "io" + + "github.com/tektoncd/hub/api/pkg/cli/hub" +) + +type Stream struct { + Out io.Writer + Err io.Writer +} + +type CLI interface { + Hub() hub.Client + Stream() Stream + SetStream(out, err io.Writer) +} + +type cli struct { + hub hub.Client + stream Stream +} + +var _ CLI = (*cli)(nil) + +func New() *cli { + return &cli{hub: hub.NewClient()} +} + +func (c *cli) Stream() Stream { + return c.stream +} + +func (c *cli) SetStream(out, err io.Writer) { + c.stream = Stream{Out: out, Err: err} +} + +func (c *cli) Hub() hub.Client { + return c.hub +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/get/get.go b/vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/get/get.go new file mode 100644 index 0000000000..b0bbff5e28 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/get/get.go @@ -0,0 +1,123 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package get + +import ( + "strings" + + "github.com/spf13/cobra" + "github.com/tektoncd/hub/api/pkg/cli/app" + "github.com/tektoncd/hub/api/pkg/cli/flag" + "github.com/tektoncd/hub/api/pkg/cli/hub" + "github.com/tektoncd/hub/api/pkg/cli/printer" +) + +type options struct { + cli app.CLI + from string + version string + kind string + args []string +} + +var cmdExamples string = ` +Get a %s of name 'foo': + + tkn hub get %s foo + +or + +Get a %s of name 'foo' of version '0.3': + + tkn hub get %s foo --version 0.3 +` + +func Command(cli app.CLI) *cobra.Command { + + opts := &options{cli: cli} + + cmd := &cobra.Command{ + Use: "get", + Short: "Get resource manifest by its name, kind, catalog, and version", + Long: ``, + Annotations: map[string]string{ + "commandType": "main", + }, + SilenceUsage: true, + } + + cmd.AddCommand( + commandForKind("task", opts), + commandForKind("pipeline", opts), + ) + + cmd.PersistentFlags().StringVar(&opts.from, "from", "tekton", "Name of Catalog to which resource belongs to.") + cmd.PersistentFlags().StringVar(&opts.version, "version", "", "Version of Resource") + + return cmd +} + +// commandForKind creates a cobra.Command that when run sets +// opts.Kind and opts.Args and invokes opts.run +func commandForKind(kind string, opts *options) *cobra.Command { + + return &cobra.Command{ + Use: kind, + Short: "Get " + kind + " by name, catalog and version", + Long: ``, + SilenceUsage: true, + Example: examples(kind), + Annotations: map[string]string{ + "commandType": "main", + }, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + opts.kind = kind + opts.args = args + return opts.run() + }, + } +} + +func (opts *options) run() error { + + if err := opts.validate(); err != nil { + return err + } + + hubClient := opts.cli.Hub() + + resource := hubClient.GetResource(hub.ResourceOption{ + Name: opts.name(), + Catalog: opts.from, + Kind: opts.kind, + Version: opts.version, + }) + + out := opts.cli.Stream().Out + return printer.New(out).Raw(resource.Manifest()) +} + +func (opts *options) validate() error { + return flag.ValidateVersion(opts.version) +} + +func (opts *options) name() string { + return strings.TrimSpace(opts.args[0]) +} + +func examples(kind string) string { + return strings.ReplaceAll(cmdExamples, "%s", kind) +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/root.go b/vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/root.go new file mode 100644 index 0000000000..14c1e011ba --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/root.go @@ -0,0 +1,53 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "github.com/spf13/cobra" + "github.com/tektoncd/hub/api/pkg/cli/app" + "github.com/tektoncd/hub/api/pkg/cli/cmd/get" + "github.com/tektoncd/hub/api/pkg/cli/cmd/search" + "github.com/tektoncd/hub/api/pkg/cli/hub" +) + +// Root represents the base command when called without any subcommands +func Root(cli app.CLI) *cobra.Command { + + apiURL := "" + + cmd := &cobra.Command{ + Use: "hub", + Annotations: map[string]string{ + "commandType": "main", + }, + Short: "Interact with tekton hub", + Long: ``, + SilenceUsage: true, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + return cli.Hub().SetURL(apiURL) + }, + } + + cli.SetStream(cmd.OutOrStdout(), cmd.OutOrStderr()) + + cmd.AddCommand( + search.Command(cli), + get.Command(cli), + ) + + cmd.PersistentFlags().StringVar(&apiURL, "api-server", hub.URL(), "Hub API Server URL") + + return cmd +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/search/search.go b/vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/search/search.go new file mode 100644 index 0000000000..cf3cfd5306 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/cli/cmd/search/search.go @@ -0,0 +1,168 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package search + +import ( + "fmt" + "strings" + "text/template" + + "github.com/spf13/cobra" + "github.com/tektoncd/hub/api/pkg/cli/app" + "github.com/tektoncd/hub/api/pkg/cli/flag" + "github.com/tektoncd/hub/api/pkg/cli/formatter" + "github.com/tektoncd/hub/api/pkg/cli/hub" + "github.com/tektoncd/hub/api/pkg/cli/printer" + "github.com/tektoncd/hub/api/pkg/parser" +) + +const resTemplate = `{{- $rl := len .Resources }}{{ if eq $rl 0 -}} +No Resources found +{{ else -}} +NAME KIND DESCRIPTION TAGS +{{ range $_, $r := .Resources -}} +{{ formatName $r.Name $r.LatestVersion.Version }} {{ $r.Kind }} {{ formatDesc $r.LatestVersion.Description }} {{ formatTags $r.Tags }} +{{ end }} +{{- end -}} +` + +var ( + funcMap = template.FuncMap{ + "formatName": formatter.FormatName, + "formatDesc": formatter.FormatDesc, + "formatTags": formatter.FormatTags, + } + tmpl = template.Must(template.New("List Resources").Funcs(funcMap).Parse(resTemplate)) +) + +type options struct { + cli app.CLI + limit uint + match string + output string + tags []string + kinds []string + args []string +} + +var examples string = ` +Search a resource of name 'foo': + + tkn hub search foo + +or + +Search resources using tag 'cli': + + tkn hub search --tags cli +` + +func Command(cli app.CLI) *cobra.Command { + + opts := &options{cli: cli} + + cmd := &cobra.Command{ + Use: "search", + Short: "Search resource by a combination of name, kind, and tags", + Long: ``, + Example: examples, + Annotations: map[string]string{ + "commandType": "main", + }, + SilenceUsage: true, + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + opts.args = args + return opts.run() + }, + } + + cmd.Flags().UintVarP(&opts.limit, "limit", "l", 0, "Max number of resources to fetch") + cmd.Flags().StringVar(&opts.match, "match", "contains", "Accept type of search. 'exact' or 'contains'.") + cmd.Flags().StringArrayVar(&opts.kinds, "kinds", nil, "Accepts a comma separated list of kinds") + cmd.Flags().StringArrayVar(&opts.tags, "tags", nil, "Accepts a comma separated list of tags") + cmd.Flags().StringVarP(&opts.output, "output", "o", "table", "Accepts output format: [table, json]") + + return cmd +} + +func (opts *options) run() error { + + if err := opts.validate(); err != nil { + return err + } + + hubClient := opts.cli.Hub() + + result := hubClient.Search(hub.SearchOption{ + Name: opts.name(), + Kinds: opts.kinds, + Tags: opts.tags, + Match: opts.match, + Limit: opts.limit, + }) + + out := opts.cli.Stream().Out + + if opts.output == "json" { + return printer.New(out).JSON(result.Raw()) + } + + typed, err := result.Typed() + if err != nil { + return err + } + + var templateData = struct { + Resources hub.SearchResponse + }{ + Resources: typed, + } + + return printer.New(out).Tabbed(tmpl, templateData) +} + +func (opts *options) validate() error { + + if flag.AllEmpty(opts.args, opts.kinds, opts.tags) { + return fmt.Errorf("please specify a name, tag or a kind to search") + } + + if err := flag.InList("match", opts.match, []string{"contains", "exact"}); err != nil { + return err + } + + if err := flag.InList("output", opts.output, []string{"table", "json"}); err != nil { + return err + } + + opts.kinds = flag.TrimArray(opts.kinds) + opts.tags = flag.TrimArray(opts.tags) + + for _, k := range opts.kinds { + if !parser.IsSupportedKind(k) { + return fmt.Errorf("invalid value %q set for option kinds. supported kinds: [%s]", + k, strings.ToLower(strings.Join(parser.SupportedKinds(), ", "))) + } + } + return nil +} + +func (opts *options) name() string { + if len(opts.args) == 0 { + return "" + } + return strings.TrimSpace(opts.args[0]) +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/cli/flag/validate.go b/vendor/github.com/tektoncd/hub/api/pkg/cli/flag/validate.go new file mode 100644 index 0000000000..ede922704d --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/cli/flag/validate.go @@ -0,0 +1,63 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package flag + +import ( + "fmt" + "regexp" + "strings" +) + +// InList validates if a value of a flag is in the array passed to it. +func InList(option, val string, list []string) error { + val = strings.ToLower(val) + + for _, v := range list { + if v == val { + return nil + } + } + return fmt.Errorf("invalid value %q set for option %s. Valid options: [%s]", + val, option, strings.Join(list, ", ")) +} + +// TrimArray Splits the array by `,` & ' '(space) and returns an array +// eg. [abc,def mno xyz] -> [abc def mno xyz] +func TrimArray(arr []string) []string { + input := strings.Trim(fmt.Sprint(arr), "[]") + return strings.FieldsFunc(input, func(r rune) bool { return r == ' ' || r == ',' }) +} + +// AllEmpty checks if all the passed arrays are empty +func AllEmpty(arr ...[]string) bool { + for _, a := range arr { + if len(a) != 0 { + return false + } + } + return true +} + +// ValidateVersion validates version format +func ValidateVersion(version string) error { + if version == "" { + return nil + } + var re = regexp.MustCompile(`^(\d+\.)?(\d+\.)?(\*|\d+)$`) + if !re.MatchString(version) { + return fmt.Errorf("invalid value %q set for option version. valid eg. 0.1, 1.2.1", version) + } + return nil +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/cli/formatter/field.go b/vendor/github.com/tektoncd/hub/api/pkg/cli/formatter/field.go new file mode 100644 index 0000000000..0ee14869e4 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/cli/formatter/field.go @@ -0,0 +1,54 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package formatter + +import ( + "fmt" + "strings" + + "github.com/tektoncd/hub/api/gen/http/resource/client" +) + +// FormatName returns name of resource with its latest version +func FormatName(name, latestVersion string) string { + return fmt.Sprintf("%s (%s)", name, latestVersion) +} + +// FormatDesc returns first 40 char of resource description +func FormatDesc(desc string) string { + + if desc == "" { + return "---" + } else if len(desc) > 40 { + return desc[0:39] + "..." + } + return desc +} + +// FormatTags returns list of tags seperated by comma +func FormatTags(tags []*client.TagResponse) string { + var sb strings.Builder + if len(tags) == 0 { + return "---" + } + for i, t := range tags { + if i != len(tags)-1 { + sb.WriteString(strings.Trim(*t.Name, " ") + ", ") + continue + } + sb.WriteString(strings.Trim(*t.Name, " ")) + } + return sb.String() +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/cli/formatter/json.go b/vendor/github.com/tektoncd/hub/api/pkg/cli/formatter/json.go new file mode 100644 index 0000000000..d9e03399a9 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/cli/formatter/json.go @@ -0,0 +1,30 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package formatter + +import ( + "bytes" + "encoding/json" +) + +// FormatJSON returns formatted json string +func FormatJSON(b []byte) ([]byte, error) { + var formatted bytes.Buffer + err := json.Indent(&formatted, b, "", "\t") + if err != nil { + return nil, err + } + return formatted.Bytes(), nil +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/cli/hub/get_resource.go b/vendor/github.com/tektoncd/hub/api/pkg/cli/hub/get_resource.go new file mode 100644 index 0000000000..f3aa0eda25 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/cli/hub/get_resource.go @@ -0,0 +1,107 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "encoding/json" + "fmt" + "net/http" + + rclient "github.com/tektoncd/hub/api/gen/http/resource/client" +) + +// ResourceOption defines option associated with API to fetch a +// particular resource +type ResourceOption struct { + Name string + Catalog string + Version string + Kind string +} + +// ResourceResult defines API response +type ResourceResult struct { + data []byte + status int + err error + version string +} + +// GetResource queries the data using Hub Endpoint +func (h *client) GetResource(opt ResourceOption) ResourceResult { + data, status, err := h.Get(opt.Endpoint()) + + return ResourceResult{ + data: data, + version: opt.Version, + status: status, + err: err, + } +} + +// Endpoint computes the endpoint url using input provided +func (opt ResourceOption) Endpoint() string { + if opt.Version != "" { + // API: /resource//// + return fmt.Sprintf("/resource/%s/%s/%s/%s", opt.Catalog, opt.Kind, opt.Name, opt.Version) + } + // API: /resource/// + return fmt.Sprintf("/resource/%s/%s/%s", opt.Catalog, opt.Kind, opt.Name) +} + +// RawURL returns the raw url of the resource yaml file +func (gr *ResourceResult) RawURL() (string, error) { + if gr.err != nil { + return "", gr.err + } + + if gr.status == http.StatusNotFound { + return "", fmt.Errorf("No Resource Found") + } + + if gr.version != "" { + resVersion := rclient.VersionResponse{} + if err := json.Unmarshal(gr.data, &resVersion); err != nil { + return "", err + } + return *resVersion.RawURL, nil + } + + res := rclient.ResourceResponse{} + if err := json.Unmarshal(gr.data, &res); err != nil { + return "", err + } + return *res.LatestVersion.RawURL, nil +} + +// Manifest gets the resource from catalog +func (gr *ResourceResult) Manifest() ([]byte, error) { + rawURL, err := gr.RawURL() + if err != nil { + return nil, err + } + + data, status, err := httpGet(rawURL) + + if err != nil { + return nil, err + } + + if status != http.StatusOK { + return nil, fmt.Errorf("failed to fetch resource from catalog") + } + + return data, nil +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/cli/hub/hub.go b/vendor/github.com/tektoncd/hub/api/pkg/cli/hub/hub.go new file mode 100644 index 0000000000..f490e53cde --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/cli/hub/hub.go @@ -0,0 +1,98 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "fmt" + "io/ioutil" + "net/http" + "net/url" +) + +const ( + // hubURL - Hub API Server URL + hubURL = "https://api.hub.tekton.dev" +) + +type Client interface { + SetURL(u string) error + Get(endpoint string) ([]byte, int, error) + Search(opt SearchOption) SearchResult + GetResource(opt ResourceOption) ResourceResult +} + +type client struct { + apiURL string +} + +var _ Client = (*client)(nil) + +func NewClient() *client { + return &client{apiURL: hubURL} +} + +// URL returns the Hub API Server URL +func URL() string { + return hubURL +} + +// SetURL validates and sets the hub apiURL server URL +func (h *client) SetURL(apiURL string) error { + + _, err := url.ParseRequestURI(apiURL) + if err != nil { + return err + } + + h.apiURL = apiURL + return nil +} + +// Get gets data from Hub +func (h *client) Get(endpoint string) ([]byte, int, error) { + data, status, err := httpGet(h.apiURL + endpoint) + if err != nil { + return nil, 0, err + } + + switch status { + case http.StatusOK: + err = nil + case http.StatusNotFound: + err = fmt.Errorf("No Resource Found") + case http.StatusInternalServerError: + err = fmt.Errorf("Internal server Error: consider filing a bug report") + default: + err = fmt.Errorf("Invalid Response from server") + } + + return data, status, err +} + +// httpGet gets raw data given the url +func httpGet(url string) ([]byte, int, error) { + resp, err := http.Get(url) + if err != nil { + return nil, 0, err + } + defer resp.Body.Close() + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, 0, err + } + + return data, resp.StatusCode, err +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/cli/hub/search.go b/vendor/github.com/tektoncd/hub/api/pkg/cli/hub/search.go new file mode 100644 index 0000000000..01e0972148 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/cli/hub/search.go @@ -0,0 +1,101 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "encoding/json" + "net/http" + "net/url" + "strconv" + + rclient "github.com/tektoncd/hub/api/gen/http/resource/client" +) + +// SearchOption defines option associated with query API +type SearchOption struct { + Name string + Kinds []string + Tags []string + Match string + Limit uint +} + +// SearchResponse is the array of ResourceResponse +type SearchResponse = []rclient.ResourceResponse + +// SearchResult defines API raw response, unmarshalled reponse, and error +type SearchResult struct { + data []byte + status int + resources SearchResponse + err error +} + +// Search queries the data using Hub Endpoint +func (h *client) Search(so SearchOption) SearchResult { + data, status, err := h.Get(so.Endpoint()) + if status == http.StatusNotFound { + err = nil + } + return SearchResult{data: data, status: status, err: err} +} + +// Raw returns API response as byte array +func (sr *SearchResult) Raw() ([]byte, error) { + return sr.data, sr.err +} + +// Typed returns unmarshalled API response as SearchResponse +func (sr *SearchResult) Typed() (SearchResponse, error) { + if sr.resources != nil || sr.err != nil { + return sr.resources, sr.err + } + sr.resources = SearchResponse{} + if sr.status == http.StatusNotFound { + return sr.resources, sr.err + } + + sr.err = json.Unmarshal(sr.data, &sr.resources) + return sr.resources, sr.err +} + +// Endpoint computes the endpoint url using input provided +func (so SearchOption) Endpoint() string { + + v := url.Values{} + + if so.Name != "" { + v.Set("name", so.Name) + } + if len(so.Kinds) != 0 { + addArraytoURL("kinds", so.Kinds, v) + } + if len(so.Tags) != 0 { + addArraytoURL("tags", so.Tags, v) + } + if so.Limit != 0 { + v.Set("limit", strconv.FormatUint(uint64(so.Limit), 10)) + } + + v.Set("match", so.Match) + + return "/query?" + v.Encode() +} + +func addArraytoURL(param string, arr []string, v url.Values) { + for _, a := range arr { + v.Add(param, a) + } +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/cli/printer/print.go b/vendor/github.com/tektoncd/hub/api/pkg/cli/printer/print.go new file mode 100644 index 0000000000..7361df70dd --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/cli/printer/print.go @@ -0,0 +1,66 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package printer + +import ( + "fmt" + "io" + "text/tabwriter" + "text/template" + + "github.com/tektoncd/hub/api/pkg/cli/formatter" +) + +type Printer struct { + out io.Writer +} + +// New returns new object of Printer +func New(out io.Writer) *Printer { + return &Printer{out: out} +} + +// JSON prints formats json data and prints it +func (p *Printer) JSON(data []byte, err error) error { + if err != nil { + return err + } + + res, err := formatter.FormatJSON(data) + if err != nil { + fmt.Fprintf(p.out, "ERROR: %s\n", err) + } + fmt.Fprintf(p.out, string(res)) + return nil +} + +// Tabbed prints data in table form based on the template passed +func (p *Printer) Tabbed(tmpl *template.Template, templateData interface{}) error { + + w := tabwriter.NewWriter(p.out, 0, 5, 3, ' ', tabwriter.TabIndent) + defer w.Flush() + + return tmpl.Execute(w, templateData) +} + +// Raw prints the raw byte array to printer's output stream +func (p *Printer) Raw(data []byte, err error) error { + if err != nil { + return err + } + + fmt.Fprintln(p.out, string(data)) + return nil +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/git/fetch_spec.go b/vendor/github.com/tektoncd/hub/api/pkg/git/fetch_spec.go new file mode 100644 index 0000000000..27bdfd651b --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/git/fetch_spec.go @@ -0,0 +1,42 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package git + +import ( + "net/url" + "path/filepath" + "strings" +) + +// FetchSpec describes how to initialize and fetch from a Git repository. +type FetchSpec struct { + URL string + Revision string + Path string + Depth uint + SSLVerify bool +} + +func (f *FetchSpec) sanitize() { + f.URL = strings.TrimSpace(f.URL) + f.Path = strings.TrimSpace(f.Path) + f.Revision = strings.TrimSpace(f.Revision) +} + +func (f *FetchSpec) clonePath() string { + f.sanitize() + u, _ := url.Parse(f.URL) + return filepath.Join(f.Path, u.Host, u.Path+"@"+f.Revision) +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/git/git.go b/vendor/github.com/tektoncd/hub/api/pkg/git/git.go new file mode 100644 index 0000000000..5c5e9a7c47 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/git/git.go @@ -0,0 +1,162 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package git + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "strconv" + "strings" + + "github.com/mitchellh/go-homedir" + "go.uber.org/zap" +) + +type Client interface { + Fetch(spec FetchSpec) (Repo, error) +} + +type client struct { + log *zap.SugaredLogger +} + +func New(log *zap.SugaredLogger) Client { + return &client{log: log} +} + +// Fetch fetches the specified git repository at the revision into path. +func (c *client) Fetch(spec FetchSpec) (Repo, error) { + spec.sanitize() + log := c.log.With("name", "git") + if err := ensureHomeEnv(log); err != nil { + return nil, err + } + + log.With("path", spec.clonePath()).Info("cloning") + + repo, err := c.initRepo(spec) + if err != nil { + os.RemoveAll(spec.clonePath()) + return nil, err + } + + fetchArgs := []string{"fetch", "--recurse-submodules=yes", "origin", spec.Revision} + + if _, err := git(log, "", fetchArgs...); err != nil { + // Fetch can fail if an old commit id was used so try git pull, performing regardless of error + // as no guarantee that the same error is returned by all git servers gitlab, github etc... + if _, err := git(log, "", "pull", "--recurse-submodules=yes", "origin"); err != nil { + log.Info("Failed to pull origin", "err", err) + } + if _, err := git(log, "", "checkout", spec.Revision); err != nil { + return nil, err + } + } else if _, err := git(log, "", "reset", "--hard", "FETCH_HEAD"); err != nil { + return nil, err + } + log.With("url", spec.URL, "revision", spec.Revision, "path", repo.path).Info("successfully cloned") + + return repo, nil +} + +func (c *client) initRepo(spec FetchSpec) (*LocalRepo, error) { + log := c.log.With("name", "repo").With("url", spec.URL) + + clonePath := spec.clonePath() + repo := &LocalRepo{path: clonePath} + + // if already cloned, cd to the cloned path + if _, err := os.Stat(clonePath); err == nil { + if err := os.Chdir(clonePath); err != nil { + return nil, fmt.Errorf("failed to change directory with path %s; err: %w", clonePath, err) + } + return repo, nil + } + + if _, err := git(log, "", "init", clonePath); err != nil { + return nil, err + } + + if err := os.Chdir(clonePath); err != nil { + return nil, fmt.Errorf("failed to change directory with path %s; err: %w", spec.Path, err) + } + + if _, err := git(log, "", "remote", "add", "origin", spec.URL); err != nil { + return nil, err + } + + if _, err := git(log, "", "config", "http.sslVerify", strconv.FormatBool(spec.SSLVerify)); err != nil { + log.Error(err, "failed to set http.sslVerify in git configs") + return nil, err + } + return repo, nil +} + +func ensureHomeEnv(log *zap.SugaredLogger) error { + // HACK: This is to get git+ssh to work since ssh doesn't respect the HOME + // env variable. + homepath, err := homedir.Dir() + if err != nil { + log.Error(err, "Unexpected error: getting the user home directory") + return err + } + homeenv := os.Getenv("HOME") + euid := os.Geteuid() + // Special case the root user/directory + if euid == 0 { + if err := os.Symlink(homeenv+"/.ssh", "/root/.ssh"); err != nil { + // Only do a warning, in case we don't have a real home + // directory writable in our image + log.Error(err, "Unexpected error: creating symlink") + } + } else if homeenv != "" && homeenv != homepath { + if _, err := os.Stat(homepath + "/.ssh"); os.IsNotExist(err) { + if err := os.Symlink(homeenv+"/.ssh", homepath+"/.ssh"); err != nil { + // Only do a warning, in case we don't have a real home + // directory writable in our image + log.Error(err, "Unexpected error: creating symlink: %v", err) + } + } + } + return nil +} + +func git(log *zap.SugaredLogger, kind string, args ...string) (string, error) { + output, err := rawGit(kind, args...) + + if err != nil { + log.Errorf("git %s : error %s ;output: %s", strings.Join(args, " "), output) + return "", err + } + return output, nil +} + +func rawGit(dir string, args ...string) (string, error) { + c := exec.Command("git", args...) + var output bytes.Buffer + c.Stderr = &output + c.Stdout = &output + // This is the optional working directory. If not set, it defaults to the current + // working directory of the process. + if dir != "" { + c.Dir = dir + } + if err := c.Run(); err != nil { + return "", err + } + return output.String(), nil +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/git/repo.go b/vendor/github.com/tektoncd/hub/api/pkg/git/repo.go new file mode 100644 index 0000000000..58170fbd07 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/git/repo.go @@ -0,0 +1,66 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package git + +import ( + "path/filepath" + "strings" + "time" +) + +type Repo interface { + Path() string + Head() string + ModifiedTime(path string) (time.Time, error) + RelPath(path string) (string, error) +} + +type LocalRepo struct { + path string + head string +} + +func (r LocalRepo) Path() string { + return r.path +} + +func (r LocalRepo) Head() string { + if r.head == "" { + head, _ := rawGit("", "rev-parse", "HEAD") + r.head = strings.TrimSuffix(head, "\n") + } + return r.head +} + +// ModifiedTime returns the modified (commited/changed) time of the file by +// querying the git history. +func (r LocalRepo) ModifiedTime(path string) (time.Time, error) { + gitPath, err := r.RelPath(path) + if err != nil { + return time.Time{}, err + } + + commitedAt, err := rawGit(r.path, "log", "-1", "--pretty=format:%cI", gitPath) + if err != nil { + return time.Time{}, err + } + + return time.Parse(time.RFC3339, commitedAt) +} + +// RelPath returns the path of file relative to repo's path +func (r LocalRepo) RelPath(file string) (string, error) { + return filepath.Rel(r.path, file) +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/parser/catalog.go b/vendor/github.com/tektoncd/hub/api/pkg/parser/catalog.go new file mode 100644 index 0000000000..7a2af21896 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/parser/catalog.go @@ -0,0 +1,396 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "bytes" + "context" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" + + "github.com/tektoncd/hub/api/pkg/git" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + decoder "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/client-go/kubernetes/scheme" + "knative.dev/pkg/apis" +) + +const ( + VersionLabel = "app.kubernetes.io/version" + DisplayNameAnnotation = "tekton.dev/displayName" + MinPipelinesVersionAnnotation = "tekton.dev/pipelines.minVersion" + TagsAnnotation = "tekton.dev/tags" +) + +type ( + Resource struct { + Name string + Kind string + Tags []string + Versions []VersionInfo + } + + VersionInfo struct { + Version string + DisplayName string + MinPipelinesVersion string + Description string + Path string + ModifiedAt time.Time + } +) + +type CatalogParser struct { + logger *zap.SugaredLogger + repo git.Repo + contextPath string +} + +func (c *CatalogParser) Parse() ([]Resource, Result) { + resources := []Resource{} + result := Result{} + + for _, k := range kinds { + res, r := c.findResourcesByKind(k) + resources = append(resources, res...) + result.Combine(r) + } + if len(resources) == 0 { + result.AddError(fmt.Errorf("no resources found in repo")) + } + return resources, result +} + +func ignoreNotExists(err error) error { + if os.IsNotExist(err) { + return nil + } + return err +} + +func (c CatalogParser) findResourcesByKind(kind string) ([]Resource, Result) { + log := c.logger.With("kind", kind) + log.Info("looking for resources") + + found := []Resource{} + result := Result{} + + // search for resources under catalog// + kindPath := filepath.Join(c.repo.Path(), c.contextPath, strings.ToLower(kind)) + + resourceDirs, err := ioutil.ReadDir(kindPath) + if err != nil && ignoreNotExists(err) != nil { + log.Warnf("failed to find %s: %s", kind, err) + // NOTE: returns empty task list; upto caller to check for error + result.AddError(err) + return []Resource{}, result + } + + for _, res := range resourceDirs { + if !res.IsDir() { + log.Infof("ignoring %q not a directory for %s", res.Name(), kind) + continue + } + + res, r := c.parseResource(kind, kindPath, res) + result.Combine(r) + if r.Errors != nil { + log.Warn(r.Error()) + continue + } + + // NOTE: res can be nil if no files exists for resource (invalid resource) + if res != nil { + found = append(found, *res) + } + } + + log.Infof("found %d resources of kind %s", len(found), kind) + return found, result + +} + +func dirCount(path string) int { + count := 0 + dirs, _ := ioutil.ReadDir(path) + for _, d := range dirs { + if d.IsDir() { + count++ + } + } + return count +} + +func (c CatalogParser) parseResource(kind, kindPath string, f os.FileInfo) (*Resource, Result) { + log := c.logger.With("kind", kind) + name := f.Name() + log.Info("checking path", kindPath, " resource: ", name) + + res := Resource{ + Name: name, + Kind: kind, + Versions: []VersionInfo{}, + } + result := Result{} + + // search for catalog///// + pattern := filepath.Join(kindPath, name, "*", name+".yaml") + + matches, err := filepath.Glob(pattern) + if err != nil { + log.Warn(err, "failed to glob %s", pattern) + result.AddError(err) + return nil, result + } + if len(matches) == 0 { + log.Warn("failed to find resources in path %s", kindPath) + result.Critical("failed to find any resource matching %s", pattern) + return nil, result + } + + if exp, got := dirCount(filepath.Join(kindPath, name)), len(matches); got != exp { + log.Warn("expected to find %d versions for %s/%s but found only", exp, kind, name, got) + result.Critical("expected to find %d versions but found only %d for %s ", exp, got, pattern) + } + + for _, m := range matches { + log.Info(" found file: ", m) + + r := c.appendVersion(&res, m) + result.Combine(r) + if r.Errors != nil { + log.Warn(result.Error()) + continue + } + } + + log.Infof("found %d versions of resource %s/%s", len(res.Versions), kind, name) + if len(res.Versions) == 0 { + return nil, result + } + + return &res, result +} + +// appendVersion reads the contents of the file at filePath and use the K8s deserializer +// to marshal the contents into a Tekton struct. +// This fails if the resource is unparseable or is not a Tekton resource. +func (c CatalogParser) appendVersion(res *Resource, filePath string) Result { + + result := Result{} + + kind := res.Kind + log := c.logger.With("kind", kind) + + relPath, err := c.repo.RelPath(filePath) + if err != nil { + result.AddError(err) + log.Error("unexpected error %q when computing path in repo of %q", filePath) + return result + } + + modified, err := c.repo.ModifiedTime(filePath) + if err != nil { + issue := fmt.Errorf("internal error computing modified time for %q: %s", relPath, err) + result.AddError(err) + log.Warn(issue) + return result + } + + f, err := os.Open(filePath) + if err != nil { + result.AddError(err) + return result + } + + tkn, err := decodeResource(f, kind) + if err != nil { + log.Warn(err) + result.AddError(err) + return result + } + + log = log.With("kind", kind, "name", tkn.Name) + + u := tkn.Unstructured + + // mandatory checks + labels := u.GetLabels() + version, ok := labels[VersionLabel] + if !ok { + issue := fmt.Sprintf("Resource %s - %s is missing mandatory version label", tkn.GVK, tkn.Name) + result.Critical(issue) + log.With("action", "error").Warn(issue) + return result + } + + annotations := u.GetAnnotations() + MinPipelinesVersion, ok := annotations[MinPipelinesVersionAnnotation] + if !ok { + issue := fmt.Sprintf("Resource %s - %s is missing mandatory minimum pipeline version annotation", tkn.GVK, tkn.Name) + log.With("action", "error").Warn(issue) + result.Critical(issue) + return result + } + + // optionals checks + displayName, ok := annotations[DisplayNameAnnotation] + if !ok { + issue := fmt.Sprintf("Resource %s - %s has no display name", tkn.GVK, tkn.Name) + log.With("action", "ignore").Info(issue) + result.Info(issue) + } + + description, found, err := unstructured.NestedString(u.Object, "spec", "description") + if !found || err != nil { + issue := fmt.Sprintf("Resource %s - %s has no description", tkn.GVK, tkn.Name) + log.With("action", "ignore").Info(issue) + return result + } + + tags := annotations[TagsAnnotation] + tagList := strings.FieldsFunc(tags, func(c rune) bool { return c == ',' || c == ' ' }) + res.Tags = append(res.Tags, tagList...) + res.Versions = append(res.Versions, + VersionInfo{ + Version: version, + DisplayName: displayName, + MinPipelinesVersion: MinPipelinesVersion, + Description: description, + Path: relPath, + ModifiedAt: modified, + }, + ) + + return result +} + +func ignoreEOF(err error) error { + if err == io.EOF { + return nil + } + return err +} + +// decode consumes the given reader and parses its contents as YAML. +func decodeResource(reader io.Reader, kind string) (*TektonResource, error) { + + // create a duplicate for UniversalDeserializer and NewYAMLToJSONDecoder + // to read from readers + var dup bytes.Buffer + r := io.TeeReader(reader, &dup) + contents, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + object, gvk, err := scheme.Codecs.UniversalDeserializer().Decode(contents, nil, nil) + if err != nil || !isTektonKind(gvk) { + return nil, fmt.Errorf("parse error: invalid resource %+v:\n%s", err, contents) + } + + decoder := decoder.NewYAMLToJSONDecoder(&dup) + + var res *unstructured.Unstructured + for { + res = &unstructured.Unstructured{} + if err := decoder.Decode(res); err != nil { + return nil, fmt.Errorf("failed to decode: %w", err) + } + + if len(res.Object) == 0 { + continue + } + + // break at the first object that has a kind and Catalog TEP expects + // that the files have only one resource + if res.GetKind() != "" { + break + } + } + + if k := res.GetKind(); k != kind { + return nil, fmt.Errorf("expected kind to be %s but got %s", kind, k) + } + + if _, err := convertToTyped(res); err != nil { + return nil, err + } + + return &TektonResource{ + Name: res.GetName(), + Kind: gvk.Kind, + GVK: res.GroupVersionKind(), + Unstructured: res, + Object: object, + }, nil +} + +func convertToTyped(u *unstructured.Unstructured) (interface{}, error) { + t, err := typeForKind(u.GetKind()) + if err != nil { + return nil, err + } + + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, t); err != nil { + return nil, err + } + + t.SetDefaults(context.Background()) + if fe := t.Validate(context.Background()); !isEmptyFieldError(fe) { + return nil, fe + } + + return t, nil +} + +func isEmptyFieldError(fe *apis.FieldError) bool { + if fe == nil { + return true + } + return fe.Message == "" && fe.Details == "" && len(fe.Paths) == 0 +} + +type tektonKind interface { + apis.Validatable + apis.Defaultable +} + +func typeForKind(kind string) (tektonKind, error) { + switch kind { + case "Task": + return &v1beta1.Task{}, nil + case "ClusterTask": + return &v1beta1.ClusterTask{}, nil + case "Pipeline": + return &v1beta1.Pipeline{}, nil + } + + return nil, fmt.Errorf("unknown kind %s", kind) +} + +func isTektonKind(gvk *schema.GroupVersionKind) bool { + id := gvk.GroupVersion().Identifier() + return id == v1beta1.SchemeGroupVersion.Identifier() +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/parser/kind.go b/vendor/github.com/tektoncd/hub/api/pkg/parser/kind.go new file mode 100644 index 0000000000..4bdd45a584 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/parser/kind.go @@ -0,0 +1,41 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "strings" +) + +// kinds defines the list of supported Tekton kind +var kinds = []string{ + "Task", + "Pipeline", +} + +// SupportedKinds returns list of supported Tekton kind +func SupportedKinds() []string { + return kinds +} + +// IsSupportedKind checks if passed kind is supported +func IsSupportedKind(kind string) bool { + + for _, k := range kinds { + if strings.ToLower(k) == strings.ToLower(kind) { + return true + } + } + return false +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/parser/parser.go b/vendor/github.com/tektoncd/hub/api/pkg/parser/parser.go new file mode 100644 index 0000000000..2c252f54e4 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/parser/parser.go @@ -0,0 +1,46 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "sync" + + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + + "github.com/tektoncd/hub/api/pkg/git" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +type Parser interface { + Parse() ([]Resource, error) +} + +func registerSchema() { + beta1 := runtime.NewSchemeBuilder(v1beta1.AddToScheme) + beta1.AddToScheme(scheme.Scheme) +} + +var once sync.Once + +func ForCatalog(logger *zap.SugaredLogger, repo git.Repo, contextPath string) *CatalogParser { + once.Do(registerSchema) + return &CatalogParser{ + logger: logger.With("component", "parser"), + repo: repo, + contextPath: contextPath, + } +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/parser/resource.go b/vendor/github.com/tektoncd/hub/api/pkg/parser/resource.go new file mode 100644 index 0000000000..bbcaf7ea37 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/parser/resource.go @@ -0,0 +1,35 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +type ( + TektonResource struct { + Name string + Kind string + GVK schema.GroupVersionKind + Unstructured *unstructured.Unstructured + Object runtime.Object + } +) + +func (r *TektonResource) ToType() (interface{}, error) { + return convertToTyped(r.Unstructured) +} diff --git a/vendor/github.com/tektoncd/hub/api/pkg/parser/result.go b/vendor/github.com/tektoncd/hub/api/pkg/parser/result.go new file mode 100644 index 0000000000..31db52b135 --- /dev/null +++ b/vendor/github.com/tektoncd/hub/api/pkg/parser/result.go @@ -0,0 +1,92 @@ +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "fmt" + "strings" +) + +type IssueType int + +const ( + Critical IssueType = iota + Warning + Info +) + +func (t IssueType) String() string { + return [...]string{"critical", "warning", "info"}[t] +} + +type Issue struct { + Type IssueType + Message string +} + +type Result struct { + Errors []error + Issues []Issue +} + +func (r *Result) add(t IssueType, format string, args ...interface{}) { + if r.Issues == nil { + r.Issues = []Issue{} + } + r.Issues = append(r.Issues, Issue{Type: t, Message: fmt.Sprintf(format, args...)}) + +} + +func (r *Result) Critical(format string, args ...interface{}) { + r.add(Critical, format, args...) +} + +func (r *Result) Warn(format string, args ...interface{}) { + r.add(Warning, format, args...) +} + +func (r *Result) Info(format string, args ...interface{}) { + r.add(Info, format, args...) +} + +func (r *Result) Error() string { + + if r.Errors == nil { + return "" + } + + if len(r.Errors) == 1 { + return r.Errors[0].Error() + } + + buf := strings.Builder{} + for _, err := range r.Errors { + buf.WriteString(err.Error()) + buf.WriteString("\n") + } + return buf.String() +} + +func (r *Result) AddError(err error) { + if r.Errors == nil { + r.Errors = []error{} + } + r.Errors = append(r.Errors, err) +} + +func (r *Result) Combine(other Result) { + r.Issues = append(r.Issues, other.Issues...) + r.Errors = append(r.Errors, other.Errors...) +} diff --git a/vendor/goa.design/goa/v3/LICENSE b/vendor/goa.design/goa/v3/LICENSE new file mode 100644 index 0000000000..d4c1b519a5 --- /dev/null +++ b/vendor/goa.design/goa/v3/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Raphael Simon and goa Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/goa.design/goa/v3/http/client.go b/vendor/goa.design/goa/v3/http/client.go new file mode 100644 index 0000000000..1022c0a61c --- /dev/null +++ b/vendor/goa.design/goa/v3/http/client.go @@ -0,0 +1,220 @@ +package http + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "os" + "sort" + "strings" +) + +type ( + // Doer is the HTTP client interface. + Doer interface { + Do(*http.Request) (*http.Response, error) + } + + // DebugDoer is a Doer that can print the low level HTTP details. + DebugDoer interface { + Doer + // Fprint prints the HTTP request and response details. + Fprint(io.Writer) + } + + // debugDoer wraps a doer and implements DebugDoer. + debugDoer struct { + Doer + // Request is the captured request. + Request *http.Request + // Response is the captured response. + Response *http.Response + } + + // ClientError is an error returned by a HTTP service client. + ClientError struct { + // Name is a name for this class of errors. + Name string + // Message contains the specific error details. + Message string + // Service is the name of the service. + Service string + // Method is the name of the service method. + Method string + // Is the error temporary? + Temporary bool + // Is the error a timeout? + Timeout bool + // Is the error a server-side fault? + Fault bool + } +) + +// NewDebugDoer wraps the given doer and captures the request and response so +// they can be printed. +func NewDebugDoer(d Doer) DebugDoer { + return &debugDoer{Doer: d} +} + +// Do captures the request and response. +func (dd *debugDoer) Do(req *http.Request) (*http.Response, error) { + var reqb []byte + if req.Body != nil { + reqb, _ = ioutil.ReadAll(req.Body) + req.Body = ioutil.NopCloser(bytes.NewBuffer(reqb)) + } + + resp, err := dd.Doer.Do(req) + + if err != nil { + return nil, err + } + + respb, err := ioutil.ReadAll(resp.Body) + if err != nil { + respb = []byte(fmt.Sprintf("!!failed to read response: %s", err)) + } + resp.Body = ioutil.NopCloser(bytes.NewBuffer(respb)) + + dd.Response = resp + + req.Body = ioutil.NopCloser(bytes.NewBuffer(reqb)) + dd.Request = req + + dd.Fprint(os.Stderr) + + return resp, err +} + +// Printf dumps the captured request and response details to w. +func (dd *debugDoer) Fprint(w io.Writer) { + if dd.Request == nil { + return + } + buf := &bytes.Buffer{} + buf.WriteString(fmt.Sprintf("> %s %s", dd.Request.Method, dd.Request.URL.String())) + + keys := make([]string, len(dd.Request.Header)) + i := 0 + for k := range dd.Request.Header { + keys[i] = k + i++ + } + sort.Strings(keys) + for _, k := range keys { + buf.WriteString(fmt.Sprintf("\n> %s: %s", k, strings.Join(dd.Request.Header[k], ", "))) + } + + b, _ := ioutil.ReadAll(dd.Request.Body) + if len(b) > 0 { + dd.Request.Body = ioutil.NopCloser(bytes.NewBuffer(b)) // reset the request body + buf.WriteByte('\n') + buf.Write(b) + } + + if dd.Response == nil { + w.Write(buf.Bytes()) + return + } + buf.WriteString(fmt.Sprintf("\n< %s", dd.Response.Status)) + + keys = make([]string, len(dd.Response.Header)) + i = 0 + for k := range dd.Response.Header { + keys[i] = k + i++ + } + sort.Strings(keys) + for _, k := range keys { + buf.WriteString(fmt.Sprintf("\n< %s: %s", k, strings.Join(dd.Response.Header[k], ", "))) + } + + rb, _ := ioutil.ReadAll(dd.Response.Body) // this is reading from a memory buffer so safe to ignore errors + if len(rb) > 0 { + dd.Response.Body = ioutil.NopCloser(bytes.NewBuffer(rb)) // reset the response body + buf.WriteByte('\n') + buf.Write(rb) + } + w.Write(buf.Bytes()) + w.Write([]byte{'\n'}) +} + +// Error builds an error message. +func (c *ClientError) Error() string { + return fmt.Sprintf("[%s %s]: %s", c.Service, c.Method, c.Message) +} + +// ErrInvalidType is the error returned when the wrong type is given to a +// method function. +func ErrInvalidType(svc, m, expected string, actual interface{}) error { + msg := fmt.Sprintf("invalid value expected %s, got %v", expected, actual) + return &ClientError{Name: "invalid_type", Message: msg, Service: svc, Method: m} +} + +// ErrEncodingError is the error returned when the encoder fails to encode the +// request body. +func ErrEncodingError(svc, m string, err error) error { + msg := fmt.Sprintf("failed to encode request body: %s", err) + return &ClientError{Name: "encoding_error", Message: msg, Service: svc, Method: m} +} + +// ErrInvalidURL is the error returned when the URL computed for an method is +// invalid. +func ErrInvalidURL(svc, m, u string, err error) error { + msg := fmt.Sprintf("invalid URL %s: %s", u, err) + return &ClientError{Name: "invalid_url", Message: msg, Service: svc, Method: m} +} + +// ErrDecodingError is the error returned when the decoder fails to decode the +// response body. +func ErrDecodingError(svc, m string, err error) error { + msg := fmt.Sprintf("failed to decode response body: %s", err) + return &ClientError{Name: "decoding_error", Message: msg, Service: svc, Method: m} +} + +// ErrValidationError is the error returned when the response body is properly +// received and decoded but fails validation. +func ErrValidationError(svc, m string, err error) error { + msg := fmt.Sprintf("invalid response: %s", err) + return &ClientError{Name: "validation_error", Message: msg, Service: svc, Method: m} +} + +// ErrInvalidResponse is the error returned when the service responded with an +// unexpected response status code. +func ErrInvalidResponse(svc, m string, code int, body string) error { + var b string + if body != "" { + b = ", body: " + } + msg := fmt.Sprintf("invalid response code %#v"+b+"%s", code, body) + + temporary := code == http.StatusServiceUnavailable || + code == http.StatusConflict || + code == http.StatusTooManyRequests || + code == http.StatusGatewayTimeout + + timeout := code == http.StatusRequestTimeout || + code == http.StatusGatewayTimeout + + fault := code == http.StatusInternalServerError || + code == http.StatusNotImplemented || + code == http.StatusBadGateway + + return &ClientError{Name: "invalid_response", Message: msg, Service: svc, Method: m, + Temporary: temporary, Timeout: timeout, Fault: fault} +} + +// ErrRequestError is the error returned when the request fails to be sent. +func ErrRequestError(svc, m string, err error) error { + temporary := false + timeout := false + if nerr, ok := err.(net.Error); ok { + temporary = nerr.Temporary() + timeout = nerr.Timeout() + } + return &ClientError{Name: "request_error", Message: err.Error(), Service: svc, Method: m, + Temporary: temporary, Timeout: timeout} +} diff --git a/vendor/goa.design/goa/v3/http/doc.go b/vendor/goa.design/goa/v3/http/doc.go new file mode 100644 index 0000000000..f22a66b926 --- /dev/null +++ b/vendor/goa.design/goa/v3/http/doc.go @@ -0,0 +1,7 @@ +/* +Package http contains HTTP specific constructs that complement the code +generated by Goa. The constructs include a composable HTTP client, default +encodings, a mux and a websocket implementation that relies on the Gorilla +websocket package. +*/ +package http diff --git a/vendor/goa.design/goa/v3/http/encoding.go b/vendor/goa.design/goa/v3/http/encoding.go new file mode 100644 index 0000000000..57e63931c2 --- /dev/null +++ b/vendor/goa.design/goa/v3/http/encoding.go @@ -0,0 +1,305 @@ +package http + +import ( + "bytes" + "context" + "encoding/gob" + "encoding/json" + "encoding/xml" + "fmt" + "io" + "io/ioutil" + "mime" + "net/http" + "strings" +) + +const ( + // AcceptTypeKey is the context key used to store the value of the HTTP + // request Accept-Type header. The value may be used by encoders and + // decoders to implement a content type negotiation algorithm. + AcceptTypeKey contextKey = iota + 1 + + // ContentTypeKey is the context key used to store the value of the HTTP + // response Content-Type header when explicitly set in the DSL. The value + // may be used by encoders to set the header appropriately. + ContentTypeKey +) + +type ( + // Decoder provides the actual decoding algorithm used to load HTTP + // request and response bodies. + Decoder interface { + // Decode decodes into v. + Decode(v interface{}) error + } + + // Encoder provides the actual encoding algorithm used to write HTTP + // request and response bodies. + Encoder interface { + // Encode encodes v. + Encode(v interface{}) error + } + + // EncodingFunc allows a function with appropriate signature to act as a + // Decoder/Encoder. + EncodingFunc func(v interface{}) error + + // private type used to define context keys. + contextKey int +) + +// RequestDecoder returns a HTTP request body decoder suitable for the given +// request. The decoder handles the following mime types: +// +// * application/json using package encoding/json +// * application/xml using package encoding/xml +// * application/gob using package encoding/gob +// +// RequestDecoder defaults to the JSON decoder if the request "Content-Type" +// header does not match any of the supported mime type or is missing +// altogether. +func RequestDecoder(r *http.Request) Decoder { + contentType := r.Header.Get("Content-Type") + if contentType == "" { + // default to JSON + contentType = "application/json" + } else { + // sanitize + if mediaType, _, err := mime.ParseMediaType(contentType); err == nil { + contentType = mediaType + } + } + switch contentType { + case "application/json": + return json.NewDecoder(r.Body) + case "application/gob": + return gob.NewDecoder(r.Body) + case "application/xml": + return xml.NewDecoder(r.Body) + case "text/html", "text/plain": + return newTextDecoder(r.Body, contentType) + default: + return json.NewDecoder(r.Body) + } +} + +// ResponseEncoder returns a HTTP response encoder leveraging the mime type +// set in the context under the AcceptTypeKey or the ContentTypeKey if any. +// The encoder supports the following mime types: +// +// * application/json using package encoding/json +// * application/xml using package encoding/xml +// * application/gob using package encoding/gob +// * text/html and text/plain for strings +// +// ResponseEncoder defaults to the JSON encoder if the context AcceptTypeKey or +// ContentTypeKey value does not match any of the supported mime types or is +// missing altogether. +func ResponseEncoder(ctx context.Context, w http.ResponseWriter) Encoder { + negotiate := func(a string) (Encoder, string) { + switch a { + case "", "application/json": + // default to JSON + return json.NewEncoder(w), "application/json" + case "application/xml": + return xml.NewEncoder(w), "application/xml" + case "application/gob": + return gob.NewEncoder(w), "application/gob" + case "text/html", "text/plain": + return newTextEncoder(w, a), a + } + return nil, "" + } + var accept string + { + if a := ctx.Value(AcceptTypeKey); a != nil { + accept = a.(string) + } + } + var ct string + { + if a := ctx.Value(ContentTypeKey); a != nil { + ct = a.(string) + } + } + var ( + enc Encoder + mt string + err error + ) + { + if ct != "" { + // If content type explicitly set in the DSL, infer the response encoder + // from the content type context key. + if mt, _, err = mime.ParseMediaType(ct); err == nil { + switch { + case ct == "application/json" || strings.HasSuffix(ct, "+json"): + enc = json.NewEncoder(w) + case ct == "application/xml" || strings.HasSuffix(ct, "+xml"): + enc = xml.NewEncoder(w) + case ct == "application/gob" || strings.HasSuffix(ct, "+gob"): + enc = gob.NewEncoder(w) + case ct == "text/html" || ct == "text/plain" || + strings.HasSuffix(ct, "+html") || strings.HasSuffix(ct, "+txt"): + enc = newTextEncoder(w, ct) + default: + enc = json.NewEncoder(w) + } + } + SetContentType(w, mt) + return enc + } + // If Accept header exists in the request, infer the response encoder + // from the header value. + if enc, mt = negotiate(accept); enc == nil { + // attempt to normalize + if mt, _, err = mime.ParseMediaType(accept); err == nil { + enc, mt = negotiate(mt) + } + } + if enc == nil { + enc, mt = negotiate("") + } + } + SetContentType(w, mt) + return enc +} + +// RequestEncoder returns a HTTP request encoder. +// The encoder uses package encoding/json. +func RequestEncoder(r *http.Request) Encoder { + var buf bytes.Buffer + r.Body = ioutil.NopCloser(&buf) + return json.NewEncoder(&buf) +} + +// ResponseDecoder returns a HTTP response decoder. +// The decoder handles the following content types: +// +// * application/json using package encoding/json (default) +// * application/xml using package encoding/xml +// * application/gob using package encoding/gob +// * text/html and text/plain for strings +// +func ResponseDecoder(resp *http.Response) Decoder { + ct := resp.Header.Get("Content-Type") + if ct == "" { + return json.NewDecoder(resp.Body) + } + if mediaType, _, err := mime.ParseMediaType(ct); err == nil { + ct = mediaType + } + switch { + case ct == "application/json" || strings.HasSuffix(ct, "+json"): + return json.NewDecoder(resp.Body) + case ct == "application/xml" || strings.HasSuffix(ct, "+xml"): + return xml.NewDecoder(resp.Body) + case ct == "application/gob" || strings.HasSuffix(ct, "+gob"): + return gob.NewDecoder(resp.Body) + case ct == "text/html" || ct == "text/plain" || + strings.HasSuffix(ct, "+html") || strings.HasSuffix(ct, "+txt"): + return newTextDecoder(resp.Body, ct) + default: + return json.NewDecoder(resp.Body) + } +} + +// ErrorEncoder returns an encoder that encodes errors returned by service +// methods. The default encoder checks whether the error is a goa ServiceError +// struct and if so uses the error temporary and timeout fields to infer a +// proper HTTP status code and marshals the error struct to the body using the +// provided encoder. If the error is not a goa ServiceError struct then it is +// encoded as a permanent internal server error. This behavior as well as the +// shape of the response can be overridden by providing a non-nil formatter. +func ErrorEncoder(encoder func(context.Context, http.ResponseWriter) Encoder, formatter func(err error) Statuser) func(context.Context, http.ResponseWriter, error) error { + return func(ctx context.Context, w http.ResponseWriter, err error) error { + enc := encoder(ctx, w) + if formatter == nil { + formatter = NewErrorResponse + } + resp := formatter(err) + w.WriteHeader(resp.StatusCode()) + return enc.Encode(resp) + } +} + +// Decode implements the Decoder interface. It simply calls f(v). +func (f EncodingFunc) Decode(v interface{}) error { return f(v) } + +// Encode implements the Encoder interface. It simply calls f(v). +func (f EncodingFunc) Encode(v interface{}) error { return f(v) } + +// SetContentType initializes the response Content-Type header given a MIME +// type. If the Content-Type header is already set and the MIME type is +// "application/json" or "application/xml" then SetContentType appends a suffix +// to the header ("+json" or "+xml" respectively). +func SetContentType(w http.ResponseWriter, ct string) { + h := w.Header().Get("Content-Type") + if h == "" { + w.Header().Set("Content-Type", ct) + return + } + // RFC6839 only defines suffixes for a few mime types, we only concern + // ourselves with JSON and XML. + if ct != "application/json" && ct != "application/xml" { + w.Header().Set("Content-Type", ct) + return + } + if strings.Contains(h, "+") { + return + } + suffix := "+json" + if ct == "application/xml" { + suffix = "+xml" + } + w.Header().Set("Content-Type", h+suffix) +} + +func newTextEncoder(w io.Writer, ct string) Encoder { + return &textEncoder{w, ct} +} + +type textEncoder struct { + w io.Writer + ct string +} + +func (e *textEncoder) Encode(v interface{}) (err error) { + switch c := v.(type) { + case string: + _, err = e.w.Write([]byte(c)) + case *string: // v may be a string pointer when the Response Body is set to the field of a custom response type. + _, err = e.w.Write([]byte(*c)) + case []byte: + _, err = e.w.Write(c) + default: + err = fmt.Errorf("can't encode %T as %s", c, e.ct) + } + return +} + +func newTextDecoder(r io.Reader, ct string) Decoder { + return &textDecoder{r, ct} +} + +type textDecoder struct { + r io.Reader + ct string +} + +func (e *textDecoder) Decode(v interface{}) error { + b, err := ioutil.ReadAll(e.r) + if err != nil { + return err + } + switch c := v.(type) { + case *string: + *c = string(b) + case *[]byte: + *c = b + default: + return fmt.Errorf("can't decode %s to %T", e.ct, c) + } + return nil +} diff --git a/vendor/goa.design/goa/v3/http/error.go b/vendor/goa.design/goa/v3/http/error.go new file mode 100644 index 0000000000..f0dc0f7543 --- /dev/null +++ b/vendor/goa.design/goa/v3/http/error.go @@ -0,0 +1,70 @@ +package http + +import ( + "net/http" + + goa "goa.design/goa/v3/pkg" +) + +type ( + // ErrorResponse is the default data structure encoded in HTTP responses + // that correspond to errors created by the generated code. This struct is + // mainly intended for clients to decode error responses. + ErrorResponse struct { + // Name is a name for that class of errors. + Name string `json:"name" xml:"name" form:"name"` + // ID is the unique error instance identifier. + ID string `json:"id" xml:"id" form:"id"` + // Message describes the specific error occurrence. + Message string `json:"message" xml:"message" form:"message"` + // Temporary indicates whether the error is temporary. + Temporary bool `json:"temporary" xml:"temporary" form:"temporary"` + // Timeout indicates whether the error is a timeout. + Timeout bool `json:"timeout" xml:"timeout" form:"timeout"` + // Fault indicates whether the error is a server-side fault. + Fault bool `json:"fault" xml:"fault" form:"fault"` + } + + // Statuser is implemented by error response object to provide the response + // HTTP status code. + Statuser interface { + // StatusCode return the HTTP status code used to encode the response + // when not defined in the design. + StatusCode() int + } +) + +// NewErrorResponse creates a HTTP response from the given error. +func NewErrorResponse(err error) Statuser { + if gerr, ok := err.(*goa.ServiceError); ok { + return &ErrorResponse{ + Name: gerr.Name, + ID: gerr.ID, + Message: gerr.Message, + Timeout: gerr.Timeout, + Temporary: gerr.Temporary, + Fault: gerr.Fault, + } + } + return NewErrorResponse(goa.Fault(err.Error())) +} + +// StatusCode implements a heuristic that computes a HTTP response status code +// appropriate for the timeout, temporary and fault characteristics of the +// error. This method is used by the generated server code when the error is not +// described explicitly in the design. +func (resp *ErrorResponse) StatusCode() int { + if resp.Fault { + return http.StatusInternalServerError + } + if resp.Timeout { + if resp.Temporary { + return http.StatusGatewayTimeout + } + return http.StatusRequestTimeout + } + if resp.Temporary { + return http.StatusServiceUnavailable + } + return http.StatusBadRequest +} diff --git a/vendor/goa.design/goa/v3/http/mux.go b/vendor/goa.design/goa/v3/http/mux.go new file mode 100644 index 0000000000..6d4e4cfdf2 --- /dev/null +++ b/vendor/goa.design/goa/v3/http/mux.go @@ -0,0 +1,92 @@ +package http + +import ( + "context" + "fmt" + "net/http" + "regexp" + + "github.com/dimfeld/httptreemux/v5" +) + +type ( + // Muxer is the HTTP request multiplexer interface used by the generated + // code. ServerHTTP must match the HTTP method and URL of each incoming + // request against the list of registered patterns and call the handler + // for the corresponding method and the pattern that most closely + // matches the URL. + // + // The patterns may include wildcards that identify URL segments that + // must be captured. + // + // There are two forms of wildcards the implementation must support: + // + // - "{name}" wildcards capture a single path segment, for example the + // pattern "/images/{name}" captures "/images/favicon.ico" and adds + // the key "name" with the value "favicon.ico" to the map returned + // by Vars. + // + // - "{*name}" wildcards must appear at the end of the pattern and + // captures the entire path starting where the wildcard matches. For + // example the pattern "/images/{*filename}" captures + // "/images/public/thumbnail.jpg" and associates the key key + // "filename" with "public/thumbnail.jpg" in the map returned by + // Vars. + // + // The names of wildcards must match the regular expression + // "[a-zA-Z0-9_]+". + Muxer interface { + // Handle registers the handler function for the given method + // and pattern. + Handle(method, pattern string, handler http.HandlerFunc) + + // ServeHTTP dispatches the request to the handler whose method + // matches the request method and whose pattern most closely + // matches the request URL. + ServeHTTP(http.ResponseWriter, *http.Request) + + // Vars returns the path variables captured for the given + // request. + Vars(*http.Request) map[string]string + } + + // mux is the default Muxer implementation. It leverages the + // httptreemux router and simply substitutes the syntax used to define + // wildcards from ":wildcard" and "*wildcard" to "{wildcard}" and + // "{*wildcard}" respectively. + mux struct { + *httptreemux.ContextMux + } +) + +// NewMuxer returns a Muxer implementation based on the httptreemux router. +func NewMuxer() Muxer { + r := httptreemux.NewContextMux() + r.EscapeAddedRoutes = true + r.NotFoundHandler = func(w http.ResponseWriter, req *http.Request) { + ctx := context.WithValue(req.Context(), AcceptTypeKey, req.Header.Get("Accept")) + enc := ResponseEncoder(ctx, w) + w.WriteHeader(http.StatusNotFound) + enc.Encode(NewErrorResponse(fmt.Errorf("404 page not found"))) + } + return &mux{r} +} + +// Handle maps the wildcard format used by goa to the one used by httptreemux. +func (m *mux) Handle(method, pattern string, handler http.HandlerFunc) { + m.ContextMux.Handle(method, treemuxify(pattern), handler) +} + +// Vars extracts the path variables from the request context. +func (m *mux) Vars(r *http.Request) map[string]string { + return httptreemux.ContextParams(r.Context()) +} + +var wildSeg = regexp.MustCompile(`/{([a-zA-Z0-9_]+)}`) +var wildPath = regexp.MustCompile(`/{\*([a-zA-Z0-9_]+)}`) + +func treemuxify(pattern string) string { + pattern = wildSeg.ReplaceAllString(pattern, "/:$1") + pattern = wildPath.ReplaceAllString(pattern, "/*$1") + return pattern +} diff --git a/vendor/goa.design/goa/v3/http/server.go b/vendor/goa.design/goa/v3/http/server.go new file mode 100644 index 0000000000..4439d1da42 --- /dev/null +++ b/vendor/goa.design/goa/v3/http/server.go @@ -0,0 +1,21 @@ +package http + +import "net/http" + +type ( + // Server is the HTTP server interface used to wrap the server handlers + // with the given middleware. + Server interface { + Use(func(http.Handler) http.Handler) + } + + // Servers is a list of servers. + Servers []Server +) + +// Use wraps the servers with the given middleware. +func (s Servers) Use(m func(http.Handler) http.Handler) { + for _, v := range s { + v.Use(m) + } +} diff --git a/vendor/goa.design/goa/v3/http/websocket.go b/vendor/goa.design/goa/v3/http/websocket.go new file mode 100644 index 0000000000..2f726943e3 --- /dev/null +++ b/vendor/goa.design/goa/v3/http/websocket.go @@ -0,0 +1,27 @@ +package http + +import ( + "context" + "net/http" + + "github.com/gorilla/websocket" +) + +type ( + // Upgrader is an HTTP connection that is able to upgrade to websocket. + Upgrader interface { + // Upgrade upgrades the HTTP connection to the websocket protocol. + Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*websocket.Conn, error) + } + + // Dialer creates a websocket connection to a given URL. + Dialer interface { + // DialContext creates a client connection to the websocket server. + DialContext(ctx context.Context, url string, h http.Header) (*websocket.Conn, *http.Response, error) + } + + // ConnConfigureFunc is used to configure a websocket connection with + // custom handlers. The cancel function cancels the request context when + // invoked in the configure function. + ConnConfigureFunc func(conn *websocket.Conn, cancel context.CancelFunc) *websocket.Conn +) diff --git a/vendor/goa.design/goa/v3/pkg/doc.go b/vendor/goa.design/goa/v3/pkg/doc.go new file mode 100644 index 0000000000..529c1f4483 --- /dev/null +++ b/vendor/goa.design/goa/v3/pkg/doc.go @@ -0,0 +1,19 @@ +/* +Package goa implements a Go framework for writing microservices that promotes +best practice by providing a single source of truth from which server code, +client code, and documentation is derived. The code generated by goa follows +the clean architecture pattern where composable modules are generated for the +transport, endpoint, and business logic layers. The goa package contains +middleware, plugins, and other complementary functionality that can be +leveraged in tandem with the generated code to implement complete +microservices in an efficient manner. By using Goa for developing +microservices, implementers don’t have to worry with the documentation +getting out of sync from the implementation as goa takes care of generating +OpenAPI specifications for HTTP based services and gRPC protocol buffer files +for gRPC based services (or both if the service supports both transports). +Reviewers can also be assured that the implementation follows the +documentation as the code is generated from the same source. + +Visit https://goa.design for more information. +*/ +package goa diff --git a/vendor/goa.design/goa/v3/pkg/endpoint.go b/vendor/goa.design/goa/v3/pkg/endpoint.go new file mode 100644 index 0000000000..0a79fa3878 --- /dev/null +++ b/vendor/goa.design/goa/v3/pkg/endpoint.go @@ -0,0 +1,24 @@ +package goa + +import "context" + +const ( + // MethodKey is the request context key used to store the name of the + // method as defined in the design. The generated transport code + // initializes the corresponding value prior to invoking the endpoint. + MethodKey contextKey = iota + 1 + + // ServiceKey is the request context key used to store the name of the + // service as defined in the design. The generated transport code + // initializes the corresponding value prior to invoking the endpoint. + ServiceKey +) + +type ( + // private type used to define context keys. + contextKey int +) + +// Endpoint exposes service methods to remote clients independently of the +// underlying transport. +type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error) diff --git a/vendor/goa.design/goa/v3/pkg/error.go b/vendor/goa.design/goa/v3/pkg/error.go new file mode 100644 index 0000000000..7c080d9071 --- /dev/null +++ b/vendor/goa.design/goa/v3/pkg/error.go @@ -0,0 +1,209 @@ +package goa + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + "io" + "strings" +) + +type ( + // ServiceError is the default error type used by the goa package to + // encode and decode error responses. + ServiceError struct { + // Name is a name for that class of errors. + Name string + // ID is a unique value for each occurrence of the error. + ID string + // Message contains the specific error details. + Message string + // Is the error a timeout? + Timeout bool + // Is the error temporary? + Temporary bool + // Is the error a server-side fault? + Fault bool + } +) + +// Fault creates an error given a format and values a la fmt.Printf. The error +// has the Fault field set to true. +func Fault(format string, v ...interface{}) *ServiceError { + return newError("fault", false, false, true, format, v...) +} + +// PermanentError creates an error given a name and a format and values a la +// fmt.Printf. +func PermanentError(name, format string, v ...interface{}) *ServiceError { + return newError(name, false, false, false, format, v...) +} + +// TemporaryError is an error class that indicates that the error is temporary +// and that retrying the request may be successful. TemporaryError creates an +// error given a name and a format and values a la fmt.Printf. The error has the +// Temporary field set to true. +func TemporaryError(name, format string, v ...interface{}) *ServiceError { + return newError(name, false, true, false, format, v...) +} + +// PermanentTimeoutError creates an error given a name and a format and values a +// la fmt.Printf. The error has the Timeout field set to true. +func PermanentTimeoutError(name, format string, v ...interface{}) *ServiceError { + return newError(name, true, false, false, format, v...) +} + +// TemporaryTimeoutError creates an error given a name and a format and values a +// la fmt.Printf. The error has both the Timeout and Temporary fields set to +// true. +func TemporaryTimeoutError(name, format string, v ...interface{}) *ServiceError { + return newError(name, true, true, false, format, v...) +} + +// MissingPayloadError is the error produced by the generated code when a +// request is missing a required payload. +func MissingPayloadError() error { + return PermanentError("missing_payload", "missing required payload") +} + +// DecodePayloadError is the error produced by the generated code when a request +// body cannot be decoded successfully. +func DecodePayloadError(msg string) error { + return PermanentError("decode_payload", msg) +} + +// InvalidFieldTypeError is the error produced by the generated code when the +// type of a payload field does not match the type defined in the design. +func InvalidFieldTypeError(name string, val interface{}, expected string) error { + return PermanentError("invalid_field_type", "invalid value %#v for %q, must be a %s", val, name, expected) +} + +// MissingFieldError is the error produced by the generated code when a payload +// is missing a required field. +func MissingFieldError(name, context string) error { + return PermanentError("missing_field", "%q is missing from %s", name, context) +} + +// InvalidEnumValueError is the error produced by the generated code when the +// value of a payload field does not match one the values defined in the design +// Enum validation. +func InvalidEnumValueError(name string, val interface{}, allowed []interface{}) error { + elems := make([]string, len(allowed)) + for i, a := range allowed { + elems[i] = fmt.Sprintf("%#v", a) + } + return PermanentError("invalid_enum_value", "value of %s must be one of %s but got value %#v", name, strings.Join(elems, ", "), val) +} + +// InvalidFormatError is the error produced by the generated code when the value +// of a payload field does not match the format validation defined in the +// design. +func InvalidFormatError(name, target string, format Format, formatError error) error { + return PermanentError("invalid_format", "%s must be formatted as a %s but got value %q, %s", name, format, target, formatError.Error()) +} + +// InvalidPatternError is the error produced by the generated code when the +// value of a payload field does not match the pattern validation defined in the +// design. +func InvalidPatternError(name, target string, pattern string) error { + return PermanentError("invalid_pattern", "%s must match the regexp %q but got value %q", name, pattern, target) +} + +// InvalidRangeError is the error produced by the generated code when the value +// of a payload field does not match the range validation defined in the design. +// value may be an int or a float64. +func InvalidRangeError(name string, target interface{}, value interface{}, min bool) error { + comp := "greater or equal" + if !min { + comp = "lesser or equal" + } + return PermanentError("invalid_range", "%s must be %s than %d but got value %#v", name, comp, value, target) +} + +// InvalidLengthError is the error produced by the generated code when the value +// of a payload field does not match the length validation defined in the +// design. +func InvalidLengthError(name string, target interface{}, ln, value int, min bool) error { + comp := "greater or equal" + if !min { + comp = "lesser or equal" + } + return PermanentError("invalid_length", "length of %s must be %s than %d but got value %#v (len=%d)", name, comp, value, target, ln) +} + +// NewErrorID creates a unique 8 character ID that is well suited to use as an +// error identifier. +func NewErrorID() string { + // for the curious - simplifying a bit - the probability of 2 values + // being equal for n 6-bytes values is n^2 / 2^49. For n = 1 million + // this gives around 1 chance in 500. 6 bytes seems to be a good + // trade-off between probability of clashes and length of ID (6 * 4/3 = + // 8 chars) since clashes are not catastrophic. + b := make([]byte, 6) + io.ReadFull(rand.Reader, b) + return base64.RawURLEncoding.EncodeToString(b) +} + +// MergeErrors updates an error by merging another into it. It first converts +// other into a ServiceError if not already one. The merge algorithm then: +// +// * uses the name of err if a ServiceError, the name of other otherwise. +// +// * appends both error messages. +// +// * computes Timeout and Temporary by "and"ing the fields of both errors. +// +// Merge returns the updated error. This makes it possible to return other when +// err is nil. +func MergeErrors(err, other error) error { + if err == nil { + if other == nil { + return nil + } + return other + } + if other == nil { + return err + } + e := asError(err) + o := asError(other) + if e.Name == "error" { + e.Name = o.Name + } + e.Message = e.Message + "; " + o.Message + e.Timeout = e.Timeout && o.Timeout + e.Temporary = e.Temporary && o.Temporary + e.Fault = e.Fault && o.Fault + + return e +} + +// Error returns the error message. +func (s *ServiceError) Error() string { return s.Message } + +// ErrorName returns the error name. +func (s *ServiceError) ErrorName() string { return s.Name } + +func newError(name string, timeout, temporary, fault bool, format string, v ...interface{}) *ServiceError { + return &ServiceError{ + Name: name, + ID: NewErrorID(), + Message: fmt.Sprintf(format, v...), + Timeout: timeout, + Temporary: temporary, + Fault: fault, + } +} + +func asError(err error) *ServiceError { + e, ok := err.(*ServiceError) + if !ok { + return &ServiceError{ + Name: "error", + ID: NewErrorID(), + Message: err.Error(), + Fault: true, // Default to fault for unexpected errors + } + } + return e +} diff --git a/vendor/goa.design/goa/v3/pkg/validation.go b/vendor/goa.design/goa/v3/pkg/validation.go new file mode 100644 index 0000000000..7491933f38 --- /dev/null +++ b/vendor/goa.design/goa/v3/pkg/validation.go @@ -0,0 +1,201 @@ +package goa + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "net/mail" + "net/url" + "regexp" + "sync" + "time" +) + +// Format defines a validation format. +type Format string + +const ( + // FormatDate describes RFC3339 date values. + FormatDate Format = "date" + + // FormatDateTime describes RFC3339 date time values. + FormatDateTime Format = "date-time" + + // FormatUUID describes RFC4122 UUID values. + FormatUUID = "uuid" + + // FormatEmail describes RFC5322 email addresses. + FormatEmail = "email" + + // FormatHostname describes RFC1035 Internet hostnames. + FormatHostname = "hostname" + + // FormatIPv4 describes RFC2373 IPv4 address values. + FormatIPv4 = "ipv4" + + // FormatIPv6 describes RFC2373 IPv6 address values. + FormatIPv6 = "ipv6" + + // FormatIP describes RFC2373 IPv4 or IPv6 address values. + FormatIP = "ip" + + // FormatURI describes RFC3986 URI values. + FormatURI = "uri" + + // FormatMAC describes IEEE 802 MAC-48, EUI-48 or EUI-64 MAC address values. + FormatMAC = "mac" + + // FormatCIDR describes RFC4632 and RFC4291 CIDR notation IP address values. + FormatCIDR = "cidr" + + // FormatRegexp describes regular expression syntax accepted by RE2. + FormatRegexp = "regexp" + + // FormatJSON describes JSON text. + FormatJSON = "json" + + // FormatRFC1123 describes RFC1123 date time values. + FormatRFC1123 = "rfc1123" +) + +var ( + hostnameRegex = regexp.MustCompile(`^[[:alnum:]][[:alnum:]\-]{0,61}[[:alnum:]]|[[:alpha:]]$`) + ipv4Regex = regexp.MustCompile(`^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$`) + uuidURNPrefix = []byte("urn:uuid:") + uuidByteGroups = []int{8, 4, 4, 4, 12} +) + +// ValidateFormat validates val against f. It returns nil if the string conforms +// to the format, an error otherwise. name is the name of the variable used in +// error messages. where in a data structure the error occurred if any. The +// format specification follows the json schema draft 4 validation extension. +// see http://json-schema.org/latest/json-schema-validation.html#anchor105 +// Supported formats are: +// +// - "date": RFC3339 date value +// - "date-time": RFC3339 date time value +// - "email": RFC5322 email address +// - "hostname": RFC1035 Internet host name +// - "ipv4", "ipv6", "ip": RFC2673 and RFC2373 IP address values +// - "uri": RFC3986 URI value +// - "mac": IEEE 802 MAC-48, EUI-48 or EUI-64 MAC address value +// - "cidr": RFC4632 and RFC4291 CIDR notation IP address value +// - "regexp": Regular expression syntax accepted by RE2 +// - "rfc1123": RFC1123 date time value +func ValidateFormat(name string, val string, f Format) error { + var err error + switch f { + case FormatDate: + _, err = time.Parse("2006-01-02", val) + case FormatDateTime: + _, err = time.Parse(time.RFC3339, val) + case FormatUUID: + err = validateUUID(val) + case FormatEmail: + _, err = mail.ParseAddress(val) + case FormatHostname: + if !hostnameRegex.MatchString(val) { + err = fmt.Errorf("hostname value '%s' does not match %s", + val, hostnameRegex.String()) + } + case FormatIPv4, FormatIPv6, FormatIP: + ip := net.ParseIP(val) + if ip == nil { + err = fmt.Errorf("\"%s\" is an invalid %s value", val, f) + } + if f == FormatIPv4 { + if !ipv4Regex.MatchString(val) { + err = fmt.Errorf("\"%s\" is an invalid ipv4 value", val) + } + } + if f == FormatIPv6 { + if ipv4Regex.MatchString(val) { + err = fmt.Errorf("\"%s\" is an invalid ipv6 value", val) + } + } + case FormatURI: + _, err = url.ParseRequestURI(val) + case FormatMAC: + _, err = net.ParseMAC(val) + case FormatCIDR: + _, _, err = net.ParseCIDR(val) + case FormatRegexp: + _, err = regexp.Compile(val) + case FormatJSON: + if !json.Valid([]byte(val)) { + err = fmt.Errorf("invalid JSON") + } + case FormatRFC1123: + _, err = time.Parse(time.RFC1123, val) + default: + return fmt.Errorf("unknown format %#v", f) + } + if err != nil { + return InvalidFormatError(name, val, f, err) + } + return nil +} + +// knownPatterns records the compiled patterns. +// TBD: refactor all this so that the generated code initializes the map on start to get rid of the +// need for a RW mutex. +var knownPatterns = make(map[string]*regexp.Regexp) + +// knownPatternsLock is the mutex used to access knownPatterns +var knownPatternsLock = &sync.RWMutex{} + +// ValidatePattern returns an error if val does not match the regular expression +// p. It makes an effort to minimize the number of times the regular expression +// needs to be compiled. name is the name of the variable used in error messages. +func ValidatePattern(name, val, p string) error { + knownPatternsLock.RLock() + r, ok := knownPatterns[p] + knownPatternsLock.RUnlock() + if !ok { + r = regexp.MustCompile(p) // DSL validation makes sure regexp is valid + knownPatternsLock.Lock() + knownPatterns[p] = r + knownPatternsLock.Unlock() + } + if !r.MatchString(val) { + return InvalidPatternError(name, val, p) + } + return nil +} + +// The following formats are supported: +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" +func validateUUID(uuid string) error { + if len(uuid) < 32 { + return fmt.Errorf("uuid: UUID string too short: %s", uuid) + } + t := []byte(uuid) + braced := false + if bytes.Equal(t[:9], uuidURNPrefix) { + t = t[9:] + } else if t[0] == '{' { + t = t[1:] + braced = true + } + for i, byteGroup := range uuidByteGroups { + if i > 0 { + if t[0] != '-' { + return fmt.Errorf("uuid: invalid string format") + } + t = t[1:] + } + if len(t) < byteGroup { + return fmt.Errorf("uuid: UUID string too short: %s", uuid) + } + if i == 4 && len(t) > byteGroup && + ((braced && t[byteGroup] != '}') || len(t[byteGroup:]) > 1 || !braced) { + return fmt.Errorf("uuid: UUID string too long: %s", uuid) + } + t = t[byteGroup:] + } + + return nil +} diff --git a/vendor/goa.design/goa/v3/pkg/version.go b/vendor/goa.design/goa/v3/pkg/version.go new file mode 100644 index 0000000000..2290af7ed0 --- /dev/null +++ b/vendor/goa.design/goa/v3/pkg/version.go @@ -0,0 +1,45 @@ +package goa + +import ( + "fmt" + "regexp" + "strconv" +) + +const ( + // Major version number + Major = 3 + // Minor version number + Minor = 2 + // Build number + Build = 2 + // Suffix - set to empty string in release tag commits. + Suffix = "" +) + +var ( + // Version format + versionFormat = regexp.MustCompile(`v(\d+?)\.(\d+?)\.(\d+?)(?:-.+)?`) +) + +// Version returns the complete version number. +func Version() string { + if Suffix != "" { + return fmt.Sprintf("v%d.%d.%d-%s", Major, Minor, Build, Suffix) + } + return fmt.Sprintf("v%d.%d.%d", Major, Minor, Build) +} + +// Compatible returns true if Major matches the major version of the given version string. +// It returns an error if the given string is not a valid version string. +func Compatible(v string) (bool, error) { + matches := versionFormat.FindStringSubmatch(v) + if len(matches) != 4 { + return false, fmt.Errorf("invalid version string format %#v, %+v", v, matches) + } + mj, err := strconv.Atoi(matches[1]) + if err != nil { + return false, fmt.Errorf("invalid major version number %#v, must be number, %v", matches[1], err) + } + return mj == Major, nil +} diff --git a/vendor/gotest.tools/v3/assert/assert.go b/vendor/gotest.tools/v3/assert/assert.go index 83610aa852..e75d1f38a0 100644 --- a/vendor/gotest.tools/v3/assert/assert.go +++ b/vendor/gotest.tools/v3/assert/assert.go @@ -119,12 +119,8 @@ func assert( return true case error: - // Handle nil structs which implement error as a nil error - if reflect.ValueOf(check).IsNil() { - return true - } - msg := "error is not nil: " - t.Log(format.WithCustomMessage(failureMessage+msg+check.Error(), msgAndArgs...)) + msg := failureMsgFromError(check) + t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...)) case cmp.Comparison: success = runComparison(t, argSelector, check, msgAndArgs...) @@ -179,6 +175,15 @@ func logFailureFromBool(t TestingT, msgAndArgs ...interface{}) { t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...)) } +func failureMsgFromError(err error) string { + // Handle errors with non-nil types + v := reflect.ValueOf(err) + if v.Kind() == reflect.Ptr && v.IsNil() { + return fmt.Sprintf("error is not nil: error has type %T", err) + } + return "error is not nil: " + err.Error() +} + func boolFailureMessage(expr ast.Expr) (string, error) { if binaryExpr, ok := expr.(*ast.BinaryExpr); ok && binaryExpr.Op == token.NEQ { x, err := source.FormatNode(binaryExpr.X) diff --git a/vendor/modules.txt b/vendor/modules.txt index 7c4e52dd15..344165b3a1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -110,6 +110,8 @@ github.com/cpuguy83/go-md2man/md2man github.com/davecgh/go-spew/spew # github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go +# github.com/dimfeld/httptreemux/v5 v5.2.2 +github.com/dimfeld/httptreemux/v5 # github.com/docker/cli v0.0.0-20200210162036-a4bedce16568 github.com/docker/cli/cli/config github.com/docker/cli/cli/config/configfile @@ -229,6 +231,8 @@ github.com/gophercloud/gophercloud/openstack/identity/v2/tokens github.com/gophercloud/gophercloud/openstack/identity/v3/tokens github.com/gophercloud/gophercloud/openstack/utils github.com/gophercloud/gophercloud/pagination +# github.com/gorilla/websocket v1.4.2 +github.com/gorilla/websocket # github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc github.com/gregjones/httpcache github.com/gregjones/httpcache/diskcache @@ -276,7 +280,7 @@ github.com/lightstep/tracecontext.go/traceparent github.com/lightstep/tracecontext.go/tracestate # github.com/lucasb-eyer/go-colorful v1.0.2 github.com/lucasb-eyer/go-colorful -# github.com/mailru/easyjson v0.7.1-0.20191009090205-6c0755d89d1e +# github.com/mailru/easyjson v0.7.1 github.com/mailru/easyjson/buffer github.com/mailru/easyjson/jlexer github.com/mailru/easyjson/jwriter @@ -290,6 +294,8 @@ github.com/mattn/go-runewidth github.com/matttproud/golang_protobuf_extensions/pbutil # github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b github.com/mgutz/ansi +# github.com/mitchellh/go-homedir v1.1.0 +github.com/mitchellh/go-homedir # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd github.com/modern-go/concurrent # github.com/modern-go/reflect2 v1.0.1 @@ -328,6 +334,20 @@ github.com/sirupsen/logrus github.com/spf13/cobra # github.com/spf13/pflag v1.0.5 github.com/spf13/pflag +# github.com/tektoncd/hub/api v0.0.0-20201021041518-f3b44fba7d18 +github.com/tektoncd/hub/api/gen/http/resource/client +github.com/tektoncd/hub/api/gen/resource +github.com/tektoncd/hub/api/gen/resource/views +github.com/tektoncd/hub/api/pkg/cli/app +github.com/tektoncd/hub/api/pkg/cli/cmd +github.com/tektoncd/hub/api/pkg/cli/cmd/get +github.com/tektoncd/hub/api/pkg/cli/cmd/search +github.com/tektoncd/hub/api/pkg/cli/flag +github.com/tektoncd/hub/api/pkg/cli/formatter +github.com/tektoncd/hub/api/pkg/cli/hub +github.com/tektoncd/hub/api/pkg/cli/printer +github.com/tektoncd/hub/api/pkg/git +github.com/tektoncd/hub/api/pkg/parser # github.com/tektoncd/pipeline v0.17.1-0.20201007165454-9611f3e4509e github.com/tektoncd/pipeline/pkg/apis/config github.com/tektoncd/pipeline/pkg/apis/pipeline @@ -467,6 +487,9 @@ go.uber.org/zap/internal/exit go.uber.org/zap/internal/ztest go.uber.org/zap/zapcore go.uber.org/zap/zaptest +# goa.design/goa/v3 v3.2.2 +goa.design/goa/v3/http +goa.design/goa/v3/pkg # golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a golang.org/x/crypto/ssh/terminal # golang.org/x/net v0.0.0-20200904194848-62affa334b73 @@ -650,7 +673,7 @@ gotest.tools/assert/cmp gotest.tools/internal/difflib gotest.tools/internal/format gotest.tools/internal/source -# gotest.tools/v3 v3.0.1 +# gotest.tools/v3 v3.0.2 gotest.tools/v3/assert gotest.tools/v3/assert/cmp gotest.tools/v3/golden