From 3f7d62de0e535a5baed310aaee8ddcde6beebea9 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Tue, 3 Mar 2020 15:06:23 +0100 Subject: [PATCH 01/80] Initial commit --- .github/config.yml | 12 ++++ .github/issue_template.md | 0 .github/pull_request_template.md | 0 .github/settings.yml | 98 ++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+) create mode 100644 .github/config.yml create mode 100644 .github/issue_template.md create mode 100644 .github/pull_request_template.md create mode 100644 .github/settings.yml diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 000000000..c723ec7fe --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,12 @@ +# Configuration for update-docs - https://github.com/behaviorbot/update-docs + +# Comment to be posted to on PRs that don't update documentation +updateDocsComment: > + Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a [changelog](https://github.com/owncloud/ocis-thumbnails/blob/master/changelog/README.md) item based on your changes. +updateDocsWhiteList: + - Tests-only + - tests-only + - Tests-Only + +updateDocsTargetFiles: + - changelog/unreleased/ diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 000000000..e69de29bb diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..e69de29bb diff --git a/.github/settings.yml b/.github/settings.yml new file mode 100644 index 000000000..bc11f70cc --- /dev/null +++ b/.github/settings.yml @@ -0,0 +1,98 @@ +--- +repository: + name: ocis-thumbnails + description: ':atom_symbol: Thumbnail Generator for oCIS' + homepage: https://owncloud.github.io/ocis-thumbnails/ + topics: reva, ocis + + private: false + has_issues: true + has_projects: false + has_wiki: false + has_downloads: false + + default_branch: master + + allow_squash_merge: true + allow_merge_commit: true + allow_rebase_merge: true + +labels: + - name: bug + color: d73a4a + description: Something isn't working + - name: documentation + color: 0075ca + description: Improvements or additions to documentation + - name: duplicate + color: cfd3d7 + description: This issue or pull request already exists + - name: enhancement + color: a2eeef + description: New feature or request + - name: good first issue + color: 7057ff + description: Good for newcomers + - name: help wanted + color: 008672 + description: Extra attention is needed + - name: invalid + color: e4e669 + description: This doesn't seem right + - name: question + color: d876e3 + description: Further information is requested + - name: wontfix + color: ffffff + description: This will not be worked on + - name: effort/trivial + color: c2e0c6 + description: Required effort to finish task + - name: effort/0.25d + color: c2e0c6 + description: Required effort to finish task + - name: effort/0.5d + color: c2e0c6 + description: Required effort to finish task + - name: effort/1d + color: c2e0c6 + description: Required effort to finish task + - name: effort/2d + color: c2e0c6 + description: Required effort to finish task + - name: effort/4d + color: c2e0c6 + description: Required effort to finish task + - name: effort/5d + color: c2e0c6 + description: Required effort to finish task + - name: effort/10d + color: c2e0c6 + description: Required effort to finish task + +teams: + - name: ci + permission: admin + - name: employees + permission: push + +branches: + - name: master + protection: + required_pull_request_reviews: + required_approving_review_count: 1 + dismiss_stale_reviews: false + require_code_owner_reviews: false + dismissal_restrictions: {} + required_status_checks: + strict: true + contexts: + - continuous-integration/drone/pr + enforce_admins: false + restrictions: + users: [] + teams: + - ci + - employees + +... From 883778d883e8b4ed89d57cd19bbfb4ccac5cc608 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 3 Mar 2020 11:06:38 +0100 Subject: [PATCH 02/80] create extension from template --- .codacy.yml | 8 + .drone.star | 698 ++++++++++++++++++++++ .editorconfig | 35 ++ .gitignore | 8 + LICENSE | 202 +++++++ Makefile | 194 +++++++ README.md | 40 ++ changelog/CHANGELOG.tmpl | 53 ++ changelog/README.md | 6 + changelog/TEMPLATE | 11 + cmd/ocis-thumbnails/main.go | 13 + config/example.json | 18 + config/example.yml | 18 + docker/Dockerfile.linux.amd64 | 19 + docker/Dockerfile.linux.arm | 19 + docker/Dockerfile.linux.arm64 | 19 + docker/manifest.tmpl | 22 + docs/_index.md | 10 + docs/building.md | 28 + docs/getting-started.md | 272 +++++++++ docs/license.md | 10 + go.mod | 19 + go.sum | 1025 +++++++++++++++++++++++++++++++++ pkg/command/health.go | 49 ++ pkg/command/root.go | 103 ++++ pkg/command/server.go | 219 +++++++ pkg/config/config.go | 46 ++ pkg/flagset/flagset.go | 138 +++++ pkg/metrics/metrics.go | 32 + pkg/server/debug/option.go | 50 ++ pkg/server/debug/server.go | 51 ++ pkg/server/http/option.go | 76 +++ pkg/server/http/server.go | 56 ++ pkg/service/v0/instrument.go | 30 + pkg/service/v0/logging.go | 30 + pkg/service/v0/option.go | 50 ++ pkg/service/v0/service.go | 52 ++ pkg/service/v0/tracing.go | 26 + pkg/version/version.go | 19 + reflex.conf | 2 + 40 files changed, 3776 insertions(+) create mode 100644 .codacy.yml create mode 100644 .drone.star create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 changelog/CHANGELOG.tmpl create mode 100644 changelog/README.md create mode 100644 changelog/TEMPLATE create mode 100644 cmd/ocis-thumbnails/main.go create mode 100644 config/example.json create mode 100644 config/example.yml create mode 100644 docker/Dockerfile.linux.amd64 create mode 100644 docker/Dockerfile.linux.arm create mode 100644 docker/Dockerfile.linux.arm64 create mode 100644 docker/manifest.tmpl create mode 100644 docs/_index.md create mode 100644 docs/building.md create mode 100644 docs/getting-started.md create mode 100644 docs/license.md create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/command/health.go create mode 100644 pkg/command/root.go create mode 100644 pkg/command/server.go create mode 100644 pkg/config/config.go create mode 100644 pkg/flagset/flagset.go create mode 100644 pkg/metrics/metrics.go create mode 100644 pkg/server/debug/option.go create mode 100644 pkg/server/debug/server.go create mode 100644 pkg/server/http/option.go create mode 100644 pkg/server/http/server.go create mode 100644 pkg/service/v0/instrument.go create mode 100644 pkg/service/v0/logging.go create mode 100644 pkg/service/v0/option.go create mode 100644 pkg/service/v0/service.go create mode 100644 pkg/service/v0/tracing.go create mode 100644 pkg/version/version.go create mode 100644 reflex.conf diff --git a/.codacy.yml b/.codacy.yml new file mode 100644 index 000000000..ca6ed0072 --- /dev/null +++ b/.codacy.yml @@ -0,0 +1,8 @@ +--- +exclude_paths: + - CHANGELOG.md + - changelog/** + - docs/** + - pkg/proto/** + +... \ No newline at end of file diff --git a/.drone.star b/.drone.star new file mode 100644 index 000000000..df632bf2a --- /dev/null +++ b/.drone.star @@ -0,0 +1,698 @@ +def main(ctx): + before = [ + testing(ctx), + ] + + stages = [ + docker(ctx, 'amd64'), + docker(ctx, 'arm64'), + docker(ctx, 'arm'), + binary(ctx, 'linux'), + binary(ctx, 'darwin'), + binary(ctx, 'windows'), + ] + + after = [ + manifest(ctx), + changelog(ctx), + readme(ctx), + badges(ctx), + website(ctx), + ] + + return before + stages + after + +def testing(ctx): + return { + 'kind': 'pipeline', + 'type': 'docker', + 'name': 'testing', + 'platform': { + 'os': 'linux', + 'arch': 'amd64', + }, + 'steps': [ + { + 'name': 'generate', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make generate', + ], + 'volumes': [ + { + 'name': 'gopath', + 'path': '/srv/app', + }, + ], + }, + { + 'name': 'vet', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make vet', + ], + 'volumes': [ + { + 'name': 'gopath', + 'path': '/srv/app', + }, + ], + }, + { + 'name': 'staticcheck', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make staticcheck', + ], + 'volumes': [ + { + 'name': 'gopath', + 'path': '/srv/app', + }, + ], + }, + { + 'name': 'lint', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make lint', + ], + 'volumes': [ + { + 'name': 'gopath', + 'path': '/srv/app', + }, + ], + }, + { + 'name': 'build', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make build', + ], + 'volumes': [ + { + 'name': 'gopath', + 'path': '/srv/app', + }, + ], + }, + { + 'name': 'test', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make test', + ], + 'volumes': [ + { + 'name': 'gopath', + 'path': '/srv/app', + }, + ], + }, + { + 'name': 'codacy', + 'image': 'plugins/codacy:1', + 'pull': 'always', + 'settings': { + 'token': { + 'from_secret': 'codacy_token', + }, + }, + }, + ], + 'volumes': [ + { + 'name': 'gopath', + 'temp': {}, + }, + ], + 'trigger': { + 'ref': [ + 'refs/heads/master', + 'refs/tags/**', + 'refs/pull/**', + ], + }, + } + +def docker(ctx, arch): + return { + 'kind': 'pipeline', + 'type': 'docker', + 'name': arch, + 'platform': { + 'os': 'linux', + 'arch': arch, + }, + 'steps': [ + { + 'name': 'generate', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make generate', + ], + 'volumes': [ + { + 'name': 'gopath', + 'path': '/srv/app', + }, + ], + }, + { + 'name': 'build', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make build', + ], + 'volumes': [ + { + 'name': 'gopath', + 'path': '/srv/app', + }, + ], + }, + { + 'name': 'dryrun', + 'image': 'plugins/docker:18.09', + 'pull': 'always', + 'settings': { + 'dry_run': True, + 'tags': 'linux-%s' % (arch), + 'dockerfile': 'docker/Dockerfile.linux.%s' % (arch), + 'repo': ctx.repo.slug, + }, + 'when': { + 'ref': { + 'include': [ + 'refs/pull/**', + ], + }, + }, + }, + { + 'name': 'docker', + 'image': 'plugins/docker:18.09', + 'pull': 'always', + 'settings': { + 'username': { + 'from_secret': 'docker_username', + }, + 'password': { + 'from_secret': 'docker_password', + }, + 'auto_tag': True, + 'auto_tag_suffix': 'linux-%s' % (arch), + 'dockerfile': 'docker/Dockerfile.linux.%s' % (arch), + 'repo': ctx.repo.slug, + }, + 'when': { + 'ref': { + 'exclude': [ + 'refs/pull/**', + ], + }, + }, + }, + ], + 'volumes': [ + { + 'name': 'gopath', + 'temp': {}, + }, + ], + 'depends_on': [ + 'testing', + ], + 'trigger': { + 'ref': [ + 'refs/heads/master', + 'refs/tags/**', + 'refs/pull/**', + ], + }, + } + +def binary(ctx, name): + if ctx.build.event == "tag": + settings = { + 'endpoint': { + 'from_secret': 's3_endpoint', + }, + 'access_key': { + 'from_secret': 'aws_access_key_id', + }, + 'secret_key': { + 'from_secret': 'aws_secret_access_key', + }, + 'bucket': { + 'from_secret': 's3_bucket', + }, + 'path_style': True, + 'strip_prefix': 'dist/release/', + 'source': 'dist/release/*', + 'target': '/ocis/%s/%s' % (ctx.repo.name.replace("ocis-", ""), ctx.build.ref.replace("refs/tags/v", "")), + } + else: + settings = { + 'endpoint': { + 'from_secret': 's3_endpoint', + }, + 'access_key': { + 'from_secret': 'aws_access_key_id', + }, + 'secret_key': { + 'from_secret': 'aws_secret_access_key', + }, + 'bucket': { + 'from_secret': 's3_bucket', + }, + 'path_style': True, + 'strip_prefix': 'dist/release/', + 'source': 'dist/release/*', + 'target': '/ocis/%s/testing' % (ctx.repo.name.replace("ocis-", "")), + } + + return { + 'kind': 'pipeline', + 'type': 'docker', + 'name': name, + 'platform': { + 'os': 'linux', + 'arch': 'amd64', + }, + 'steps': [ + { + 'name': 'generate', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make generate', + ], + 'volumes': [ + { + 'name': 'gopath', + 'path': '/srv/app', + }, + ], + }, + { + 'name': 'build', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make release-%s' % (name), + ], + 'volumes': [ + { + 'name': 'gopath', + 'path': '/srv/app', + }, + ], + }, + { + 'name': 'finish', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make release-finish', + ], + 'volumes': [ + { + 'name': 'gopath', + 'path': '/srv/app', + }, + ], + }, + { + 'name': 'upload', + 'image': 'plugins/s3:1', + 'pull': 'always', + 'settings': settings, + 'when': { + 'ref': [ + 'refs/heads/master', + 'refs/tags/**', + ], + }, + }, + { + 'name': 'changelog', + 'image': 'toolhippie/calens:latest', + 'pull': 'always', + 'commands': [ + 'calens --version %s -o dist/CHANGELOG.md' % ctx.build.ref.replace("refs/tags/v", "").split("-")[0], + ], + 'when': { + 'ref': [ + 'refs/tags/**', + ], + }, + }, + { + 'name': 'release', + 'image': 'plugins/github-release:1', + 'pull': 'always', + 'settings': { + 'api_key': { + 'from_secret': 'github_token', + }, + 'files': [ + 'dist/release/*', + ], + 'title': ctx.build.ref.replace("refs/tags/v", ""), + 'note': 'dist/CHANGELOG.md', + 'overwrite': True, + 'prerelease': len(ctx.build.ref.split("-")) > 1, + }, + 'when': { + 'ref': [ + 'refs/tags/**', + ], + }, + }, + ], + 'volumes': [ + { + 'name': 'gopath', + 'temp': {}, + }, + ], + 'depends_on': [ + 'testing', + ], + 'trigger': { + 'ref': [ + 'refs/heads/master', + 'refs/tags/**', + 'refs/pull/**', + ], + }, + } + +def manifest(ctx): + return { + 'kind': 'pipeline', + 'type': 'docker', + 'name': 'manifest', + 'platform': { + 'os': 'linux', + 'arch': 'amd64', + }, + 'steps': [ + { + 'name': 'execute', + 'image': 'plugins/manifest:1', + 'pull': 'always', + 'settings': { + 'username': { + 'from_secret': 'docker_username', + }, + 'password': { + 'from_secret': 'docker_password', + }, + 'spec': 'docker/manifest.tmpl', + 'auto_tag': True, + 'ignore_missing': True, + }, + }, + ], + 'depends_on': [ + 'amd64', + 'arm64', + 'arm', + 'linux', + 'darwin', + 'windows', + ], + 'trigger': { + 'ref': [ + 'refs/heads/master', + 'refs/tags/**', + ], + }, + } + +def changelog(ctx): + repo_slug = ctx.build.source_repo if ctx.build.source_repo else ctx.repo.slug + return { + 'kind': 'pipeline', + 'type': 'docker', + 'name': 'changelog', + 'platform': { + 'os': 'linux', + 'arch': 'amd64', + }, + 'clone': { + 'disable': True, + }, + 'steps': [ + { + 'name': 'clone', + 'image': 'plugins/git-action:1', + 'pull': 'always', + 'settings': { + 'actions': [ + 'clone', + ], + 'remote': 'https://github.com/%s' % (repo_slug), + 'branch': ctx.build.source if ctx.build.event == 'pull_request' else 'master', + 'path': '/drone/src', + 'netrc_machine': 'github.com', + 'netrc_username': { + 'from_secret': 'github_username', + }, + 'netrc_password': { + 'from_secret': 'github_token', + }, + }, + }, + { + 'name': 'generate', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'make changelog', + ], + }, + { + 'name': 'diff', + 'image': 'owncloud/alpine:latest', + 'pull': 'always', + 'commands': [ + 'git diff', + ], + }, + { + 'name': 'output', + 'image': 'webhippie/golang:1.13', + 'pull': 'always', + 'commands': [ + 'cat CHANGELOG.md', + ], + }, + { + 'name': 'publish', + 'image': 'plugins/git-action:1', + 'pull': 'always', + 'settings': { + 'actions': [ + 'commit', + 'push', + ], + 'message': 'Automated changelog update [skip ci]', + 'branch': 'master', + 'author_email': 'devops@owncloud.com', + 'author_name': 'ownClouders', + 'netrc_machine': 'github.com', + 'netrc_username': { + 'from_secret': 'github_username', + }, + 'netrc_password': { + 'from_secret': 'github_token', + }, + }, + 'when': { + 'ref': { + 'exclude': [ + 'refs/pull/**', + ], + }, + }, + }, + ], + 'depends_on': [ + 'manifest', + ], + 'trigger': { + 'ref': [ + 'refs/heads/master', + 'refs/pull/**', + ], + }, + } + +def readme(ctx): + return { + 'kind': 'pipeline', + 'type': 'docker', + 'name': 'readme', + 'platform': { + 'os': 'linux', + 'arch': 'amd64', + }, + 'steps': [ + { + 'name': 'execute', + 'image': 'sheogorath/readme-to-dockerhub:latest', + 'pull': 'always', + 'environment': { + 'DOCKERHUB_USERNAME': { + 'from_secret': 'docker_username', + }, + 'DOCKERHUB_PASSWORD': { + 'from_secret': 'docker_password', + }, + 'DOCKERHUB_REPO_PREFIX': ctx.repo.namespace, + 'DOCKERHUB_REPO_NAME': ctx.repo.name, + 'SHORT_DESCRIPTION': 'Docker images for %s' % (ctx.repo.name), + 'README_PATH': 'README.md', + }, + }, + ], + 'depends_on': [ + 'changelog', + ], + 'trigger': { + 'ref': [ + 'refs/heads/master', + 'refs/tags/**', + ], + }, + } + +def badges(ctx): + return { + 'kind': 'pipeline', + 'type': 'docker', + 'name': 'badges', + 'platform': { + 'os': 'linux', + 'arch': 'amd64', + }, + 'steps': [ + { + 'name': 'execute', + 'image': 'plugins/webhook:1', + 'pull': 'always', + 'settings': { + 'urls': { + 'from_secret': 'microbadger_url', + }, + }, + }, + ], + 'depends_on': [ + 'readme', + ], + 'trigger': { + 'ref': [ + 'refs/heads/master', + 'refs/tags/**', + ], + }, + } + +def website(ctx): + return { + 'kind': 'pipeline', + 'type': 'docker', + 'name': 'website', + 'platform': { + 'os': 'linux', + 'arch': 'amd64', + }, + 'steps': [ + { + 'name': 'prepare', + 'image': 'owncloudci/alpine:latest', + 'commands': [ + 'make docs-copy' + ], + }, + { + 'name': 'test', + 'image': 'webhippie/hugo:latest', + 'commands': [ + 'cd hugo', + 'hugo', + ], + }, + { + 'name': 'list', + 'image': 'owncloudci/alpine:latest', + 'commands': [ + 'tree hugo/public', + ], + }, + { + 'name': 'publish', + 'image': 'plugins/gh-pages:1', + 'pull': 'always', + 'settings': { + 'username': { + 'from_secret': 'github_username', + }, + 'password': { + 'from_secret': 'github_token', + }, + 'pages_directory': 'docs/', + 'target_branch': 'docs', + }, + 'when': { + 'ref': { + 'exclude': [ + 'refs/pull/**', + ], + }, + }, + }, + { + 'name': 'downstream', + 'image': 'plugins/downstream', + 'settings': { + 'server': 'https://cloud.drone.io/', + 'token': { + 'from_secret': 'drone_token', + }, + 'repositories': [ + 'owncloud/owncloud.github.io@source', + ], + }, + 'when': { + 'ref': { + 'exclude': [ + 'refs/pull/**', + ], + }, + }, + }, + ], + 'depends_on': [ + 'badges', + ], + 'trigger': { + 'ref': [ + 'refs/heads/master', + 'refs/pull/**', + ], + }, + } diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..77129cd39 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,35 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true + +[Makefile] +indent_style = tab +indent_size = 4 + +[*.go] +indent_style = tab +indent_size = 4 + +[*.starlark] +indent_style = space +indent_size = 2 + +[*.{yml,json}] +indent_style = space +indent_size = 2 + +[*.{js,vue}] +indent_style = space +indent_size = 2 + +[*.{css,less}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..606767879 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +coverage.out + +/bin +/dist +/hugo + +/node_modules +/assets diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + 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/Makefile b/Makefile new file mode 100644 index 000000000..8e45e1524 --- /dev/null +++ b/Makefile @@ -0,0 +1,194 @@ +SHELL := bash +NAME := ocis-thumbnails +IMPORT := github.com/owncloud/$(NAME) +BIN := bin +DIST := dist +HUGO := hugo + +ifeq ($(OS), Windows_NT) + EXECUTABLE := $(NAME).exe + UNAME := Windows +else + EXECUTABLE := $(NAME) + UNAME := $(shell uname -s) +endif + +ifeq ($(UNAME), Darwin) + GOBUILD ?= go build -i +else + GOBUILD ?= go build +endif + +PACKAGES ?= $(shell go list ./...) +SOURCES ?= $(shell find . -name "*.go" -type f -not -path "./node_modules/*") +GENERATE ?= $(PACKAGES) + +TAGS ?= + +ifndef OUTPUT + ifneq ($(DRONE_TAG),) + OUTPUT ?= $(subst v,,$(DRONE_TAG)) + else + OUTPUT ?= testing + endif +endif + +ifndef VERSION + ifneq ($(DRONE_TAG),) + VERSION ?= $(subst v,,$(DRONE_TAG)) + else + VERSION ?= $(shell git rev-parse --short HEAD) + endif +endif + +ifndef DATE + DATE := $(shell date -u '+%Y%m%d') +endif + +LDFLAGS += -s -w -X "$(IMPORT)/pkg/version.String=$(VERSION)" -X "$(IMPORT)/pkg/version.Date=$(DATE)" +GCFLAGS += all=-N -l + +.PHONY: all +all: build + +.PHONY: sync +sync: + go mod download + +.PHONY: clean +clean: + go clean -i ./... + rm -rf $(BIN) $(DIST) $(HUGO) + +.PHONY: fmt +fmt: + gofmt -s -w $(SOURCES) + +.PHONY: vet +vet: + go vet $(PACKAGES) + +.PHONY: staticcheck +staticcheck: + go run honnef.co/go/tools/cmd/staticcheck -tags '$(TAGS)' $(PACKAGES) + +.PHONY: lint +lint: + for PKG in $(PACKAGES); do go run golang.org/x/lint/golint -set_exit_status $$PKG || exit 1; done; + +.PHONY: generate +generate: + go generate $(GENERATE) + +.PHONY: changelog +changelog: + go run github.com/restic/calens >| CHANGELOG.md + +.PHONY: test +test: + go run github.com/haya14busa/goverage -v -coverprofile coverage.out $(PACKAGES) + +.PHONY: install +install: $(SOURCES) + go install -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ./cmd/$(NAME) + +.PHONY: build +build: $(BIN)/$(EXECUTABLE) $(BIN)/$(EXECUTABLE)-debug + +$(BIN)/$(EXECUTABLE): $(SOURCES) + $(GOBUILD) -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $@ ./cmd/$(NAME) + +$(BIN)/$(EXECUTABLE)-debug: $(SOURCES) + $(GOBUILD) -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -gcflags '$(GCFLAGS)' -o $@ ./cmd/$(NAME) + +.PHONY: release +release: release-dirs release-linux release-windows release-darwin release-copy release-check + +.PHONY: release-dirs +release-dirs: + mkdir -p $(DIST)/binaries $(DIST)/release + +.PHONY: release-linux +release-linux: release-dirs + go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'linux' -arch 'amd64 386 arm64 arm' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)--' ./cmd/$(NAME) + +.PHONY: release-windows +release-windows: release-dirs + go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'windows' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)--' ./cmd/$(NAME) + +.PHONY: release-darwin +release-darwin: release-dirs + go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -os 'darwin' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)--' ./cmd/$(NAME) + +.PHONY: release-copy +release-copy: + $(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));) + +.PHONY: release-check +release-check: + cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;) + +.PHONY: release-finish +release-finish: release-copy release-check + +.PHONY: docs-copy +docs-copy: + mkdir -p $(HUGO); \ + mkdir -p $(HUGO)/content/extensions; \ + cd $(HUGO); \ + git init; \ + git remote rm origin; \ + git remote add origin https://github.com/owncloud/owncloud.github.io; \ + git fetch; \ + git checkout origin/source -f; \ + rsync --delete -ax ../docs/ content/extensions/$(NAME) + +.PHONY: docs-build +docs-build: + cd $(HUGO); hugo + +.PHONY: docs +docs: docs-copy docs-build + +.PHONY: watch +watch: + go run github.com/cespare/reflex -c reflex.conf + +# $(GOPATH)/bin/protoc-gen-go: +# GO111MODULE=off go get -v github.com/golang/protobuf/protoc-gen-go + +# $(GOPATH)/bin/protoc-gen-micro: +# GO111MODULE=off go get -v github.com/micro/protoc-gen-micro + +# $(GOPATH)/bin/protoc-gen-microweb: +# GO111MODULE=off go get -v github.com/webhippie/protoc-gen-microweb + +# $(GOPATH)/bin/protoc-gen-swagger: +# GO111MODULE=off go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger + +# pkg/proto/v0/example.pb.go: pkg/proto/v0/example.proto +# protoc \ +# -I=third_party/ \ +# -I=pkg/proto/v0/ \ +# --go_out=logtostderr=true:pkg/proto/v0 example.proto + +# pkg/proto/v0/example.pb.micro.go: pkg/proto/v0/example.proto +# protoc \ +# -I=third_party/ \ +# -I=pkg/proto/v0/ \ +# --micro_out=logtostderr=true:pkg/proto/v0 example.proto + +# pkg/proto/v0/example.pb.web.go: pkg/proto/v0/example.proto +# protoc \ +# -I=third_party/ \ +# -I=pkg/proto/v0/ \ +# --microweb_out=logtostderr=true:pkg/proto/v0 example.proto + +# pkg/proto/v0/example.swagger.json: pkg/proto/v0/example.proto +# protoc \ +# -I=third_party/ \ +# -I=pkg/proto/v0/ \ +# --swagger_out=logtostderr=true:pkg/proto/v0 example.proto + +# .PHONY: protobuf +# protobuf: $(GOPATH)/bin/protoc-gen-go $(GOPATH)/bin/protoc-gen-micro $(GOPATH)/bin/protoc-gen-microweb $(GOPATH)/bin/protoc-gen-swagger pkg/proto/v0/example.pb.go pkg/proto/v0/example.pb.micro.go pkg/proto/v0/example.pb.web.go pkg/proto/v0/example.swagger.json diff --git a/README.md b/README.md new file mode 100644 index 000000000..ae2d1e635 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# ownCloud Infinite Scale: THUMBNAILS + +== Badges need to be provided manually == + +**This project is under heavy development, it's not in a working state yet!** + +## Install + +You can download prebuilt binaries from the GitHub releases or from our [download mirrors](http://download.owncloud.com/ocis/thumbnails/). For instructions how to install this on your platform you should take a look at our [documentation](https://owncloud.github.io/extensions/ocis_thumbnails/) + +## Development + +Make sure you have a working Go environment, for further reference or a guide take a look at the [install instructions](http://golang.org/doc/install.html). This project requires Go >= v1.13. + +```console +git clone https://github.com/owncloud/ocis-thumbnails.git +cd ocis-thumbnails + +make generate build + +./bin/ocis-thumbnails -h +``` + +## Security + +If you find a security issue please contact security@owncloud.com first. + +## Contributing + +Fork -> Patch -> Push -> Pull Request + +## License + +Apache-2.0 + +## Copyright + +```console +Copyright (c) 2020 ownCloud GmbH +``` diff --git a/changelog/CHANGELOG.tmpl b/changelog/CHANGELOG.tmpl new file mode 100644 index 000000000..886288f44 --- /dev/null +++ b/changelog/CHANGELOG.tmpl @@ -0,0 +1,53 @@ +{{ $allVersions := . }} +{{- range $index, $changes := . }}{{ with $changes -}} +{{ if gt (len $allVersions) 1 -}} +# Changelog for [{{ .Version }}] ({{ .Date }}) + +The following sections list the changes in ownCloudocis-thumbnails{{ .Version }}. + +{{/* creating version compare links */ -}} +{{ $next := add1 $index -}} +{{ if ne (len $allVersions) $next -}} +{{ $previousVersion := (index $allVersions $next).Version -}} +{{ if eq .Version "unreleased" -}} +[{{ .Version }}]: https://github.com/owncloud/ocis-thumbnails/compare/v{{ $previousVersion }}...master + +{{ else -}} +[{{ .Version }}]: https://github.com/owncloud/ocis-thumbnails/compare/v{{ $previousVersion }}...v{{ .Version }} + +{{ end -}} +{{ end -}} + +{{- /* last version managed by calens, end of the loop */ -}} +{{ if eq .Version "0.1.0" -}} +[{{ .Version }}]: https://github.com/owncloud/ocis-thumbnails/compare/c43f3a33cb0b57d7e25ebc88c138d22e95f88cfe...v{{ .Version }} + +{{ end -}} +{{ else -}} +# Changes in {{ .Version }} + +{{ end -}} + +## Summary +{{ range $entry := .Entries }}{{ with $entry }} + * {{ .TypeShort }} #{{ .PrimaryID }}: {{ .Title }} +{{- end }}{{ end }} + +## Details +{{ range $entry := .Entries }}{{ with $entry }} + * {{ .Type }} #{{ .PrimaryID }}: {{ .Title }} +{{ range $par := .Paragraphs }} + {{ wrapIndent $par 80 3 }} +{{ end -}} +{{ range $url := .IssueURLs }} + {{ $url -}} +{{ end -}} +{{ range $url := .PRURLs }} + {{ $url -}} +{{ end -}} +{{ range $url := .OtherURLs }} + {{ $url -}} +{{ end }} + +{{ end }}{{ end -}} +{{ end }}{{ end -}} \ No newline at end of file diff --git a/changelog/README.md b/changelog/README.md new file mode 100644 index 000000000..0ae5d5b3e --- /dev/null +++ b/changelog/README.md @@ -0,0 +1,6 @@ +# Changelog + +We are using [calens](https://github.com/restic/calens) to properly generate a +changelog before we are tagging a new release. To get an idea how this could +look like would be the +best reference. diff --git a/changelog/TEMPLATE b/changelog/TEMPLATE new file mode 100644 index 000000000..0cf2d8a05 --- /dev/null +++ b/changelog/TEMPLATE @@ -0,0 +1,11 @@ +Bugfix: Fix behavior for foobar (in present tense) + +We've fixed the behavior for foobar, a long-standing annoyance for users. The +text should be wrapped at 80 characters length. + +The text in the paragraphs is written in past tense. The last section is a list +of issue URLs, PR URLs and other URLs. The first issue ID (or the first PR ID, +in case there aren't any issue links) is used as the primary ID. + +https://github.com/owncloud/ocis-thumbnails/issues/1234 +https://github.com/owncloud/ocis-thumbnails/pull/55555 diff --git a/cmd/ocis-thumbnails/main.go b/cmd/ocis-thumbnails/main.go new file mode 100644 index 000000000..bdbbc1b46 --- /dev/null +++ b/cmd/ocis-thumbnails/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "os" + + "github.com/owncloud/ocis-thumbnails/pkg/command" +) + +func main() { + if err := command.Execute(); err != nil { + os.Exit(1) + } +} \ No newline at end of file diff --git a/config/example.json b/config/example.json new file mode 100644 index 000000000..baf3238f3 --- /dev/null +++ b/config/example.json @@ -0,0 +1,18 @@ +{ + "debug": { + "addr": "0.0.0.0:9114", + "token": "", + "pprof": false, + "zpages": false + }, + "http": { + "addr": "0.0.0.0:9110" + }, + "tracing": { + "enabled": false, + "type": "jaeger", + "endpoint": "localhost:6831", + "collector": "http://localhost:14268/api/traces", + "service": "thumbnails" + } +} diff --git a/config/example.yml b/config/example.yml new file mode 100644 index 000000000..33473539d --- /dev/null +++ b/config/example.yml @@ -0,0 +1,18 @@ +--- +debug: + addr: 0.0.0.0:9114 + token: + pprof: false + zpages: false + +http: + addr: 0.0.0.0:9110 + +tracing: + enabled: false + type: jaeger + endpoint: localhost:6831 + collector: http://localhost:14268/api/traces + service: thumbnails + +... diff --git a/docker/Dockerfile.linux.amd64 b/docker/Dockerfile.linux.amd64 new file mode 100644 index 000000000..ce868ead2 --- /dev/null +++ b/docker/Dockerfile.linux.amd64 @@ -0,0 +1,19 @@ +FROM amd64/alpine:edge + +RUN apk update && \ + apk upgrade && \ + apk add ca-certificates mailcap && \ + rm -rf /var/cache/apk/* && \ + echo 'hosts: files dns' >| /etc/nsswitch.conf + +LABEL maintainer="ownCloud GmbH " \ + org.label-schema.name="oCIS Thumbnails" \ + org.label-schema.vendor="ownCloud GmbH" \ + org.label-schema.schema-version="1.0" + +EXPOSE 9110 9114 + +ENTRYPOINT ["/usr/bin/ocis-thumbnails"] +CMD ["server"] + +COPY bin/ocis-thumbnails /usr/bin/ocis-thumbnails diff --git a/docker/Dockerfile.linux.arm b/docker/Dockerfile.linux.arm new file mode 100644 index 000000000..4ef28cfa1 --- /dev/null +++ b/docker/Dockerfile.linux.arm @@ -0,0 +1,19 @@ +FROM arm32v6/alpine:edge + +RUN apk update && \ + apk upgrade && \ + apk add ca-certificates mailcap && \ + rm -rf /var/cache/apk/* && \ + echo 'hosts: files dns' >| /etc/nsswitch.conf + +LABEL maintainer="ownCloud GmbH " \ + org.label-schema.name="oCIS Thumbnails" \ + org.label-schema.vendor="ownCloud GmbH" \ + org.label-schema.schema-version="1.0" + +EXPOSE 9110 9114 + +ENTRYPOINT ["/usr/bin/ocis-thumbnails"] +CMD ["server"] + +COPY bin/ocis-thumbnails /usr/bin/ocis-thumbnails diff --git a/docker/Dockerfile.linux.arm64 b/docker/Dockerfile.linux.arm64 new file mode 100644 index 000000000..34dc691aa --- /dev/null +++ b/docker/Dockerfile.linux.arm64 @@ -0,0 +1,19 @@ +FROM arm64v8/alpine:edge + +RUN apk update && \ + apk upgrade && \ + apk add ca-certificates mailcap && \ + rm -rf /var/cache/apk/* && \ + echo 'hosts: files dns' >| /etc/nsswitch.conf + +LABEL maintainer="ownCloud GmbH " \ + org.label-schema.name="oCIS Thumbnails" \ + org.label-schema.vendor="ownCloud GmbH" \ + org.label-schema.schema-version="1.0" + +EXPOSE 9110 9114 + +ENTRYPOINT ["/usr/bin/ocis-thumbnails"] +CMD ["server"] + +COPY bin/ocis-thumbnails /usr/bin/ocis-thumbnails diff --git a/docker/manifest.tmpl b/docker/manifest.tmpl new file mode 100644 index 000000000..254456d9b --- /dev/null +++ b/docker/manifest.tmpl @@ -0,0 +1,22 @@ +image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} +{{#if build.tags}} +tags: +{{#each build.tags}} + - {{this}} +{{/each}} +{{/if}} +manifests: + - image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 + platform: + architecture: amd64 + os: linux + - image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 + platform: + architecture: arm64 + variant: v8 + os: linux + - image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm + platform: + architecture: arm + variant: v6 + os: linux diff --git a/docs/_index.md b/docs/_index.md new file mode 100644 index 000000000..1ca7e5d18 --- /dev/null +++ b/docs/_index.md @@ -0,0 +1,10 @@ +--- +title: "Thumbnails" +date: 2018-05-02T00:00:00+00:00 +weight: 10 +geekdocRepo: https://github.com/owncloud/ocis-thumbnails +geekdocEditPath: edit/master/docs +geekdocFilePath: _index.md +--- + +This service provides ... diff --git a/docs/building.md b/docs/building.md new file mode 100644 index 000000000..784946f4c --- /dev/null +++ b/docs/building.md @@ -0,0 +1,28 @@ +--- +title: "Building" +date: 2018-05-02T00:00:00+00:00 +weight: 30 +geekdocRepo: https://github.com/owncloud/ocis-thumbnails +geekdocEditPath: edit/master/docs +geekdocFilePath: building.md +--- + +{{< toc >}} + +As this project is built with Go, so you need to install that first. The installation of Go is out of the scope of this document, please follow the official documentation for [Go](https://golang.org/doc/install), to build this project you have to install Go >= v1.12. After the installation of the required tools you need to get the sources: + +{{< highlight txt >}} +git clone https://github.com/owncloud/{{ Name }}.git +cd {{ Name }} +{{< / highlight >}} + +All required tool besides Go itself and make are bundled or getting automatically installed within the `Gopath`. All commands to build this project are part of our `Makefile`. + +## Backend + +{{< highlight txt >}} +make generate +make build +{{< / highlight >}} + +Finally you should have the binary within the `bin/` folder now, give it a try with `./bin/ocis-thumbnails -h` to see all available options. diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 000000000..07394b2a6 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,272 @@ +--- +title: "Getting Started" +date: 2018-05-02T00:00:00+00:00 +weight: 20 +geekdocRepo: https://github.com/owncloud/ocis-thumbnails +geekdocEditPath: edit/master/docs +geekdocFilePath: getting-started.md +--- + +{{< toc >}} + +## Installation + +So far we are offering two different variants for the installation. You can choose between [Docker](https://www.docker.com/) or pre-built binaries which are stored on our download mirrors and GitHub releases. Maybe we will also provide system packages for the major distributions later if we see the need for it. + +### Docker + +TBD + +### Binaries + +TBD + +## Configuration + +We provide overall three different variants of configuration. The variant based on environment variables and commandline flags are split up into global values and command-specific values. + +### Envrionment variables + +If you prefer to configure the service with environment variables you can see the available variables below. + +#### Global + +THUMBNAILS_CONFIG_FILE +: Path to config file, empty default value + +THUMBNAILS_LOG_LEVEL +: Set logging level, defaults to `info` + +THUMBNAILS_LOG_COLOR +: Enable colored logging, defaults to `true` + +THUMBNAILS_LOG_PRETTY +: Enable pretty logging, defaults to `true` + +#### Server + +THUMBNAILS_TRACING_ENABLED +: Enable sending traces, defaults to `false` + +THUMBNAILS_TRACING_TYPE +: Tracing backend type, defaults to `jaeger` + +THUMBNAILS_TRACING_ENDPOINT +: Endpoint for the agent, empty default value + +THUMBNAILS_TRACING_COLLECTOR +: Endpoint for the collector, empty default value + +THUMBNAILS_TRACING_SERVICE +: Service name for tracing, defaults to `ocis-thumbnails` + +THUMBNAILS_DEBUG_ADDR +: Address to bind debug server, defaults to `0.0.0.0:9114` + +THUMBNAILS_DEBUG_TOKEN +: Token to grant metrics access, empty default value + +THUMBNAILS_DEBUG_PPROF +: Enable pprof debugging, defaults to `false` + +THUMBNAILS_DEBUG_ZPAGES +: Enable zpages debugging, defaults to `false` + +THUMBNAILS_HTTP_ADDR +: Address to bind http server, defaults to `0.0.0.0:9110` + +THUMBNAILS_HTTP_NAMESPACE +: The http namespace + +THUMBNAILS_HTTP_ROOT +: Root path of http server, defaults to `/` + +#### Health + +THUMBNAILS_DEBUG_ADDR +: Address to debug endpoint, defaults to `0.0.0.0:9114` + +### Commandline flags + +If you prefer to configure the service with commandline flags you can see the available variables below. + +#### Global + +--config-file +: Path to config file, empty default value + +--log-level +: Set logging level, defaults to `info` + +--log-color +: Enable colored logging, defaults to `true` + +--log-pretty +: Enable pretty logging, defaults to `true` + +#### Server + +--tracing-enabled +: Enable sending traces, defaults to `false` + +--tracing-type +: Tracing backend type, defaults to `jaeger` + +--tracing-endpoint +: Endpoint for the agent, empty default value + +--tracing-collector +: Endpoint for the collector, empty default value + +--tracing-service +: Service name for tracing, defaults to `thumbnails` + +--debug-addr +: Address to bind debug server, defaults to `0.0.0.0:9114` + +--debug-token +: Token to grant metrics access, empty default value + +--debug-pprof +: Enable pprof debugging, defaults to `false` + +--debug-zpages +: Enable zpages debugging, defaults to `false` + +--http-addr +: Address to bind http server, defaults to `0.0.0.0:9110` + +--http-namespace +: Namespace for internal services communication, defaults to `com.owncloud.web` + +--http-root +: Root path of http server, defaults to `/` + +#### Health + +--debug-addr +: Address to debug endpoint, defaults to `0.0.0.0:9114` + +### Configuration file + +So far we support the file formats `JSON` and `YAML`, if you want to get a full example configuration just take a look at [our repository](https://github.com/owncloud/ocis-thumbnails/tree/master/config), there you can always see the latest configuration format. These example configurations include all available options and the default values. The configuration file will be automatically loaded if it's placed at `/etc/ocis/thumbnails.yml`, `${HOME}/.ocis/thumbnails.yml` or `$(pwd)/config/thumbnails.yml`. + +## Usage + +The program provides a few sub-commands on execution. The available configuration methods have already been mentioned above. Generally you can always see a formated help output if you execute the binary via `ocis-thumbnails --help`. + +### Server + +The server command is used to start the http and debug server on two addresses within a single process. The http server is serving the general webservice while the debug server is used for health check, readiness check and to server the metrics mentioned below. For further help please execute: + +{{< highlight txt >}} +{{ Name }} server --help +{{< / highlight >}} + +### Health + +The health command is used to execute a health check, if the exit code equals zero the service should be up and running, if the exist code is greater than zero the service is not in a healthy state. Generally this command is used within our Docker containers, it could also be used within Kubernetes. + +{{< highlight txt >}} +{{ Name }} health --help +{{< / highlight >}} + +## Metrics + +This service provides some [Prometheus](https://prometheus.io/) metrics through the debug endpoint, you can optionally secure the metrics endpoint by some random token, which got to be configured through one of the flag `--debug-token` or the environment variable `THUMBNAILS_DEBUG_TOKEN` mentioned above. By default the metrics endpoint is bound to `http://0.0.0.0:9114/metrics`. + +go_gc_duration_seconds +: A summary of the GC invocation durations + +go_gc_duration_seconds_sum +: A summary of the GC invocation durations + +go_gc_duration_seconds_count +: A summary of the GC invocation durations + +go_goroutines +: Number of goroutines that currently exist + +go_info +: Information about the Go environment + +go_memstats_alloc_bytes +: Number of bytes allocated and still in use + +go_memstats_alloc_bytes_total +: Total number of bytes allocated, even if freed + +go_memstats_buck_hash_sys_bytes +: Number of bytes used by the profiling bucket hash table + +go_memstats_frees_total +: Total number of frees + +go_memstats_gc_cpu_fraction +: The fraction of this program's available CPU time used by the GC since the program started + +go_memstats_gc_sys_bytes +: Number of bytes used for garbage collection system metadata + +go_memstats_heap_alloc_bytes +: Number of heap bytes allocated and still in use + +go_memstats_heap_idle_bytes +: Number of heap bytes waiting to be used + +go_memstats_heap_inuse_bytes +: Number of heap bytes that are in use + +go_memstats_heap_objects +: Number of allocated objects + +go_memstats_heap_released_bytes +: Number of heap bytes released to OS + +go_memstats_heap_sys_bytes +: Number of heap bytes obtained from system + +go_memstats_last_gc_time_seconds +: Number of seconds since 1970 of last garbage collection + +go_memstats_lookups_total +: Total number of pointer lookups + +go_memstats_mallocs_total +: Total number of mallocs + +go_memstats_mcache_inuse_bytes +: Number of bytes in use by mcache structures + +go_memstats_mcache_sys_bytes +: Number of bytes used for mcache structures obtained from system + +go_memstats_mspan_inuse_bytes +: Number of bytes in use by mspan structures + +go_memstats_mspan_sys_bytes +: Number of bytes used for mspan structures obtained from system + +go_memstats_next_gc_bytes +: Number of heap bytes when next garbage collection will take place + +go_memstats_other_sys_bytes +: Number of bytes used for other system allocations + +go_memstats_stack_inuse_bytes +: Number of bytes in use by the stack allocator + +go_memstats_stack_sys_bytes +: Number of bytes obtained from system for stack allocator + +go_memstats_sys_bytes +: Number of bytes obtained from system + +go_threads +: Number of OS threads created + +promhttp_metric_handler_requests_in_flight +: Current number of scrapes being served + +promhttp_metric_handler_requests_total +: Total number of scrapes by HTTP status code diff --git a/docs/license.md b/docs/license.md new file mode 100644 index 000000000..17bd89d71 --- /dev/null +++ b/docs/license.md @@ -0,0 +1,10 @@ +--- +title: "License" +date: 2018-05-02T00:00:00+00:00 +weight: 40 +geekdocRepo: https://github.com/owncloud/ocis-thumbnails +geekdocEditPath: edit/master/docs +geekdocFilePath: license.md +--- + +This project is licensed under the [Apache 2.0](https://github.com/owncloud/ocis-thumbnails/blob/master/LICENSE) license. For the license of the used libraries you have to check the respective sources. diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..7069238a2 --- /dev/null +++ b/go.mod @@ -0,0 +1,19 @@ +module github.com/owncloud/ocis-thumbnails + +go 1.13 + +require ( + contrib.go.opencensus.io/exporter/jaeger v0.2.0 + contrib.go.opencensus.io/exporter/ocagent v0.6.0 + contrib.go.opencensus.io/exporter/zipkin v0.1.1 + github.com/cespare/reflex v0.2.0 // indirect + github.com/go-chi/chi v4.0.2+incompatible + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/micro/cli/v2 v2.1.1 + github.com/ogier/pflag v0.0.1 // indirect + github.com/oklog/run v1.0.0 + github.com/openzipkin/zipkin-go v0.2.2 + github.com/owncloud/ocis-pkg/v2 v2.0.1 + github.com/spf13/viper v1.5.0 + go.opencensus.io v0.22.2 +) diff --git a/go.sum b/go.sum new file mode 100644 index 000000000..bde3bff73 --- /dev/null +++ b/go.sum @@ -0,0 +1,1025 @@ +cloud.google.com/go v0.26.0/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= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= +contrib.go.opencensus.io/exporter/jaeger v0.2.0 h1:nhTv/Ry3lGmqbJ/JGvCjWxBl5ozRfqo86Ngz59UAlfk= +contrib.go.opencensus.io/exporter/jaeger v0.2.0/go.mod h1:ukdzwIYYHgZ7QYtwVFQUjiT28BJHiMhTERo32s6qVgM= +contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= +contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= +contrib.go.opencensus.io/exporter/ocagent v0.6.0 h1:Z1n6UAyr0QwM284yUuh5Zd8JlvxUGAhFZcgMJkMPrGM= +contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs= +contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw= +contrib.go.opencensus.io/exporter/zipkin v0.1.1 h1:PR+1zWqY8ceXs1qDQQIlgXe+sdiwCf0n32bH4+Epk8g= +contrib.go.opencensus.io/exporter/zipkin v0.1.1/go.mod h1:GMvdSl3eJ2gapOaLKzTKE3qDgUkJ86k9k3yY2eqwkzc= +contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE= +contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU= +github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg= +github.com/Azure/azure-pipeline-go v0.1.9/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg= +github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0= +github.com/Azure/azure-storage-blob-go v0.6.0/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= +github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw= +github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= +github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= +github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM= +github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= +github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190605020000-c4ba1fdf4d36/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/sarama v1.24.1/go.mod h1:fGP8eQ6PugKEI0iUETYYtnP6d1pH/bdDMTel1X5ajsU= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/abbot/go-http-auth v0.4.1-0.20181019201920-860ed7f246ff/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= +github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= +github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= +github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= +github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= +github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y= +github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ= +github.com/anacrolix/sync v0.2.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g= +github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= +github.com/anacrolix/utp v0.0.0-20180219060659-9e0e1d1d0572/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee h1:3T/l+vMotQ7cDSLWNAn2Vg1SAQ3mdyLgBWWBitSS3uU= +github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee/go.mod h1:u7Wtt4WATGGgae9mURNGQQqxAudPKrxfsbSDSGOso+g= +github.com/asim/go-awsxray v0.0.0-20161209120537-0d8a60b6e205/go.mod h1:frVmN4PtXUuL1EbZn0uL4PHSTKNKFnbMpBIhngqMuNQ= +github.com/asim/go-bson v0.0.0-20160318195205-84522947cabd/go.mod h1:L59ZX7HuzTbNzFBt8g3SJkRraj+GBOgvLAfJYJUcQ5w= +github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.25.31/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= +github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= +github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= +github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= +github.com/bwmarrin/discordgo v0.20.1/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= +github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/reflex v0.2.0 h1:6d9WpWJseKjJvZEevKP7Pk42nPx2+BUTqmhNk8wZPwM= +github.com/cespare/reflex v0.2.0/go.mod h1:ooqOLJ4algvHP/oYvKWfWJ9tFUzCLDk5qkIJduMYrgI= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA= +github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= +github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= +github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= +github.com/cloudflare/cloudflare-go v0.10.6/go.mod h1:dcRl7AXBH5Bf7QFTBVc3TRzwvotSeO4AlnMhuxORAX8= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.17+incompatible h1:f/Z3EoDSx1yjaIjLQGo1diYUlQYSBrrAQ5vP8NjwXwo= +github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.18+incompatible h1:Zz1aXgDrFFi1nadh58tA9ktt06cmPTwNNP3dXwIq1lE= +github.com/coreos/etcd v3.3.18+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decker502/dnspod-go v0.2.0/go.mod h1:qsurYu1FgxcDwfSwXJdLt4kRsBLZeosEb9uq4Sy+08g= +github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +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/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.4.2-0.20190710153559-aa8249ae1b8b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= +github.com/eknkc/basex v1.0.0/go.mod h1:k/F/exNEHFdbs3ZHuasoP2E7zeWwZblG84Y7Z59vQRo= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c/go.mod h1:pFdJbAhRf7rh6YYMUdIQGyzne6zYL1tCUW8QV2B3UfY= +github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsouza/go-dockerclient v1.4.4/go.mod h1:PrwszSL5fbmsESocROrOGq/NULMXRw+bajY0ltzD6MA= +github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-acme/lego/v3 v3.1.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE= +github.com/go-acme/lego/v3 v3.3.0/go.mod h1:iGSY2vQrvQs3WezicSB/oVbO2eCrD88dpWPwb1qLqu0= +github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= +github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= +github.com/go-log/log v0.1.0 h1:wudGTNsiGzrD5ZjgIkVZ517ugi2XRe9Q/xRCzwEO4/U= +github.com/go-log/log v0.1.0/go.mod h1:4mBwpdRMFLiuXZDCwU2lKQFsoSCo72j3HqBK9d81N2M= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-redsync/redsync v1.3.1/go.mod h1:qxZwM5JOimfq8y98Wk2+c8dKtxJgG5/yIl2ODz2E5Dk= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stomp/stomp v2.0.3+incompatible/go.mod h1:VqCtqNZv1226A1/79yh+rMiFUcfY3R109np+7ke4n0c= +github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= +github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +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 v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc h1:55rEp52jU6bkyslZ1+C/7NGfpQsEc6pxGLAGDOctqbw= +github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= +github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= +github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.2.0/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/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.4 h1:5xLhQjsk4zqPf9EHCrja2qFZMx+yBqkO3XgJ14bNnU0= +github.com/grpc-ecosystem/grpc-gateway v1.9.4/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hako/branca v0.0.0-20180808000428-10b799466ada/go.mod h1:tOPn4gvKEUWqIJNE+zpTeTALaRAXnrRqqSnPlO3VpEo= +github.com/hashicorp/consul/api v1.2.0/go.mod h1:1SIkFYi2ZTXUE5Kgt179+4hH33djo11+0Eo2XgTAtkw= +github.com/hashicorp/consul/sdk v0.2.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= +github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.1.5/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= +github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5 h1:FdBGmSkD2QpQzRWup//SGObvWf2nq89zj9+ta9OvI3A= +github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5/go.mod h1:0YZ2wQSuwviXXXGUiK6zXzskyBLAbLXhamxzcFHSLoM= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= +github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= +github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1/go.mod h1:DFXrEwSRX0p/aSvxE21319menCBFeQO0jXpRj7LEZUA= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= +github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= +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/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= +github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= +github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +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/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= +github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ= +github.com/lucas-clemente/quic-go v0.12.1/go.mod h1:UXJJPE4RfFef/xPO5wQm0tITK8gNfqwTxjbE7s3Vb8s= +github.com/lucas-clemente/quic-go v0.13.1 h1:CxtJTXQIh2aboCPk0M6vf530XOov6DZjVBiSE3nSj8s= +github.com/lucas-clemente/quic-go v0.13.1/go.mod h1:Vn3/Fb0/77b02SGhQk36KzOUmXgVpFfizUfW5WMaqyU= +github.com/lucas-clemente/quic-go v0.14.1 h1:c1aKoBZKOPA+49q96B1wGkibyPP0AxYh45WuAoq+87E= +github.com/lucas-clemente/quic-go v0.14.1/go.mod h1:Vn3/Fb0/77b02SGhQk36KzOUmXgVpFfizUfW5WMaqyU= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/marten-seemann/chacha20 v0.2.0 h1:f40vqzzx+3GdOmzQoItkLX5WLvHgPgyYqFFIO5Gh4hQ= +github.com/marten-seemann/chacha20 v0.2.0/go.mod h1:HSdjFau7GzYRj+ahFNwsO3ouVJr1HFkWoEwNDb4TMtE= +github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI= +github.com/marten-seemann/qtls v0.3.2/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= +github.com/marten-seemann/qtls v0.4.1 h1:YlT8QP3WCCvvok7MGEZkMldXbyqgr8oFg5/n8Gtbkks= +github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mholt/certmagic v0.7.5/go.mod h1:91uJzK5K8IWtYQqTi5R2tsxV1pCde+wdGfaRaOZi6aQ= +github.com/mholt/certmagic v0.8.3/go.mod h1:91uJzK5K8IWtYQqTi5R2tsxV1pCde+wdGfaRaOZi6aQ= +github.com/mholt/certmagic v0.9.1/go.mod h1:nu8jbsbtwK4205EDH/ZUMTKsfYpJA1Q7MKXHfgTihNw= +github.com/micro/cli v0.2.0 h1:ut3rV5JWqZjsXIa2MvGF+qMUP8DAUTvHX9Br5gO4afA= +github.com/micro/cli v0.2.0/go.mod h1:jRT9gmfVKWSS6pkKcXQ8YhUyj6bzwxK8Fp5b0Y7qNnk= +github.com/micro/cli/v2 v2.1.1 h1:uFw0SMIKmGuyHIm8lXns/NOn7V62bM5y7DnlxUM+BEQ= +github.com/micro/cli/v2 v2.1.1/go.mod h1:EguNh6DAoWKm9nmk+k/Rg0H3lQnDxqzu5x5srOtGtYg= +github.com/micro/go-micro v1.16.0/go.mod h1:A0F58bHLh2m0LAI9QyhvmbN8c1cxhAZo3cM6s+iDsrM= +github.com/micro/go-micro v1.17.1 h1:BhwC4Lnwr3hdci9T8kN5MEabKS+CT0QT0YH4hinTdNs= +github.com/micro/go-micro v1.17.1/go.mod h1:klwUJL1gkdY1MHFyz+fFJXn52dKcty4hoe95Mp571AA= +github.com/micro/go-micro v1.18.0 h1:gP70EZVHpJuUIT0YWth192JmlIci+qMOEByHm83XE9E= +github.com/micro/go-micro v1.18.0/go.mod h1:klwUJL1gkdY1MHFyz+fFJXn52dKcty4hoe95Mp571AA= +github.com/micro/go-micro/v2 v2.0.0 h1:bMx549RwJ9Yuiui8cDVlfYhVNP8I8KBJTMyLthEXpRw= +github.com/micro/go-micro/v2 v2.0.0/go.mod h1:v7QP5UhKRt37ixjJe8DouWmg0/eE6dltr5h0idJ9BpE= +github.com/micro/go-plugins v1.5.1/go.mod h1:jcxejzJCAMH731cQHbS/hncyKe0rxAbzKkibj8glad4= +github.com/micro/go-plugins/wrapper/trace/opencensus/v2 v2.0.1/go.mod h1:QrkcwcDtIs2hIJpIEhozekyf6Rfz5C36kFI8+zzCpX0= +github.com/micro/mdns v0.3.0 h1:bYycYe+98AXR3s8Nq5qvt6C573uFTDPIYzJemWON0QE= +github.com/micro/mdns v0.3.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc= +github.com/micro/micro v1.16.0/go.mod h1:TO5Ng0KidbfRYIxVM4Q3deZ0A+qwRyP9WeXp+k2fWNA= +github.com/micro/protoc-gen-micro v1.0.0/go.mod h1:C8ij4DJhapBmypcT00AXdb0cZ675/3PqUO02buWWqbE= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.22 h1:Jm64b3bO9kP43ddLjL2EY3Io6bmy1qGb9Xxz6TqS6rc= +github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= +github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= +github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= +github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +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/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= +github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.0/go.mod h1:r5y0WgCag0dTj/qiHkHrXAcKQ/f5GMOZaEGdoxxnJ4I= +github.com/nats-io/nats-server/v2 v2.1.2 h1:i2Ly0B+1+rzNZHHWtD4ZwKi+OU5l+uQo1iDHZ2PmiIc= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= +github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= +github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nats-io/stan.go v0.5.0/go.mod h1:dYqB+vMN3C2F9pT1FRQpg9eHbjPj6mP0yYuyBNuXHZE= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/nlopes/slack v0.6.0/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk= +github.com/nlopes/slack v0.6.1-0.20191106133607-d06c2a2b3249/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk= +github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw= +github.com/nrdcg/dnspod-go v0.3.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= +github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ= +github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= +github.com/nsqio/go-nsq v1.0.7/go.mod h1:XP5zaUs3pqf+Q71EqUJs3HYfBIqfK6G83WQMdNN+Ito= +github.com/ogier/pflag v0.0.1 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750= +github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= +github.com/owncloud/ocis-pkg v1.1.0 h1:cwhzqQAHLA8fz94YKdmqSJguSCswx5TBNkgLAU8DbJI= +github.com/owncloud/ocis-pkg v1.1.0/go.mod h1:EfbeXoe60Me2lB/AWjYl8UFNv4isqCPP6lokd5R7nyM= +github.com/owncloud/ocis-pkg v1.3.0 h1:2fkgvfd/spTjschuulYMHRuzxkCGGXae9ocebVYkm74= +github.com/owncloud/ocis-pkg v1.3.0/go.mod h1:Wo0QfOmhadh2vNcUoQIsw2yaOT3zeftk+xaOOwP3y88= +github.com/owncloud/ocis-pkg/v2 v2.0.0 h1:RJAe5wYsNZrDjbRX8iKpVf5pnzKbFAoVa23oApv4mEE= +github.com/owncloud/ocis-pkg/v2 v2.0.0/go.mod h1:7bVnn3VUaqdmvpMkXF0QVEF1fRugs35hSkuVTAq9yjk= +github.com/owncloud/ocis-pkg/v2 v2.0.1 h1:3ISEtfjAz4pDFczTggIJwKuft3bVsAp1C7dFY9BBPEs= +github.com/owncloud/ocis-pkg/v2 v2.0.1/go.mod h1:7bVnn3VUaqdmvpMkXF0QVEF1fRugs35hSkuVTAq9yjk= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= +github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI= +github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/restic/calens v0.1.0 h1:RHGokdZ72dICyIz1EjEsfZwUhvNZz/zy2SawxJktdWA= +github.com/restic/calens v0.1.0/go.mod h1:u67f5msOjCTDYNzOf/NoAUSdmXP03YXPCwIQLYADy5M= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.17.2 h1:RMRHFw2+wF7LO0QqtELQwo8hqSmqISyCJeFeAAuWcRo= +github.com/rs/zerolog v1.17.2/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= +github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.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/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4= +github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= +github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= +github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= +github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= +github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= +github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY= +github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= +github.com/uber/jaeger-client-go v2.15.0+incompatible h1:NP3qsSqNxh8VYr956ur1N/1C1PjvOJnJykCzcD5QHbk= +github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= +github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +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 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= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= +go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.12.0 h1:dySoUQPFBGj6xwjmBzageVL8jGi8uxc6bEmJQjA06bw= +go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +gocloud.dev v0.17.0/go.mod h1:tIHTRdR1V5dlD8sTkzYdTGizBJ314BDykJ8KmadEXwo= +gocloud.dev/pubsub/rabbitpubsub v0.17.0/go.mod h1:7o1XYDiIC+b0mmcwJuofsDg08t0DtU2ubfn7C/Uz7Y0= +golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/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= +golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/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-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a h1:R/qVym5WAxsZWQqZCwDY/8sdVKV1m1WgU4/S5IRQAzc= +golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200117160349-530e935923ad h1:Jh8cai0fqIK+f6nG0UgPW5wFk8wmiMhM3AyciDBdtQg= +golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/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-20191011234655-491137f69257/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/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-20191109021931-daa7c04131f5 h1:bHNaocaoJxYBo5cw41UyTMLjYlb8wPY7+WFrnklbHOM= +golang.org/x/net v0.0.0-20191109021931-daa7c04131f5/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/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-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-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM= +golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +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-20190907020128-2ca718005c18/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-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 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba h1:pRj9OXZbwNtbtZtOB4dLwfK4u+EVRMvP+e9zKkg2grM= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +gopkg.in/DataDog/dd-trace-go.v1 v1.19.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= +gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= +gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= +gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= +gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= +gopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= +gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= +gopkg.in/ldap.v3 v3.1.0/go.mod h1:dQjCc0R0kfyFjIlWNMH1DORwUASZyDxo2Ry1B51dXaQ= +gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= +gopkg.in/olivere/elastic.v5 v5.0.82/go.mod h1:uhHoB4o3bvX5sorxBU29rPcmBQdV2Qfg0FBrx5D6pV0= +gopkg.in/redis.v3 v3.6.4/go.mod h1:6XeGv/CrsUFDU9aVbUdNykN7k1zVmoeg83KC9RbQfiU= +gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= +gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= +gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +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= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +k8s.io/api v0.0.0-20191109101513-0171b7c15da1/go.mod h1:VJq7+38rpM4TSUbRiZX4P5UVAKK2UQpNQLZClkFQkpE= +k8s.io/apimachinery v0.0.0-20191109100837-dffb012825f2/go.mod h1:+6CX7hP4aLfX2sb91JYDMIp0VqDSog2kZu0BHe+lP+s= +k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc/go.mod h1:+6CX7hP4aLfX2sb91JYDMIp0VqDSog2kZu0BHe+lP+s= +k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/pkg/command/health.go b/pkg/command/health.go new file mode 100644 index 000000000..c2c9a1c8e --- /dev/null +++ b/pkg/command/health.go @@ -0,0 +1,49 @@ +package command + +import ( + "fmt" + "net/http" + + "github.com/micro/cli/v2" + "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/owncloud/ocis-thumbnails/pkg/flagset" +) + +// Health is the entrypoint for the health command. +func Health(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "health", + Usage: "Check health status", + Flags: flagset.HealthWithConfig(cfg), + Action: func(c *cli.Context) error { + logger := NewLogger(cfg) + + resp, err := http.Get( + fmt.Sprintf( + "http://%s/healthz", + cfg.Debug.Addr, + ), + ) + + if err != nil { + logger.Fatal(). + Err(err). + Msg("Failed to request health check") + } + + defer resp.Body.Close() + + if resp.StatusCode != 200 { + logger.Fatal(). + Int("code", resp.StatusCode). + Msg("Health seems to be in bad state") + } + + logger.Debug(). + Int("code", resp.StatusCode). + Msg("Health got a good state") + + return nil + }, + } +} \ No newline at end of file diff --git a/pkg/command/root.go b/pkg/command/root.go new file mode 100644 index 000000000..adb4ee9dc --- /dev/null +++ b/pkg/command/root.go @@ -0,0 +1,103 @@ +package command + +import ( + "os" + "strings" + + "github.com/micro/cli/v2" + "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/owncloud/ocis-thumbnails/pkg/flagset" + "github.com/owncloud/ocis-thumbnails/pkg/version" + "github.com/owncloud/ocis-pkg/v2/log" + "github.com/spf13/viper" +) + +// Execute is the entry point for the ocis-thumbnails command. +func Execute() error { + cfg := config.New() + + app := &cli.App{ + Name: "ocis-thumbnails", + Version: version.String, + Usage: "Example usage", + Compiled: version.Compiled(), + + Authors: []*cli.Author{ + { + Name: "ownCloud GmbH", + Email: "support@owncloud.com", + }, + }, + + Flags: flagset.RootWithConfig(cfg), + + Before: func(c *cli.Context) error { + logger := NewLogger(cfg) + + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.SetEnvPrefix("THUMBNAILS") + viper.AutomaticEnv() + + if c.IsSet("config-file") { + viper.SetConfigFile(c.String("config-file")) + } else { + viper.SetConfigName("thumbnails") + + viper.AddConfigPath("/etc/ocis") + viper.AddConfigPath("$HOME/.ocis") + viper.AddConfigPath("./config") + } + + if err := viper.ReadInConfig(); err != nil { + switch err.(type) { + case viper.ConfigFileNotFoundError: + logger.Info(). + Msg("Continue without config") + case viper.UnsupportedConfigError: + logger.Fatal(). + Err(err). + Msg("Unsupported config type") + default: + logger.Fatal(). + Err(err). + Msg("Failed to read config") + } + } + + if err := viper.Unmarshal(&cfg); err != nil { + logger.Fatal(). + Err(err). + Msg("Failed to parse config") + } + + return nil + }, + + Commands: []*cli.Command{ + Server(cfg), + Health(cfg), + }, + } + + cli.HelpFlag = &cli.BoolFlag{ + Name: "help,h", + Usage: "Show the help", + } + + cli.VersionFlag = &cli.BoolFlag{ + Name: "version,v", + Usage: "Print the version", + } + + return app.Run(os.Args) +} + +// NewLogger initializes a service-specific logger instance. +func NewLogger(cfg *config.Config) log.Logger { + return log.NewLogger( + log.Name("thumbnails"), + log.Level(cfg.Log.Level), + log.Pretty(cfg.Log.Pretty), + log.Color(cfg.Log.Color), + ) +} diff --git a/pkg/command/server.go b/pkg/command/server.go new file mode 100644 index 000000000..31f8dce9d --- /dev/null +++ b/pkg/command/server.go @@ -0,0 +1,219 @@ +package command + +import ( + "context" + "os" + "os/signal" + "strings" + "time" + + "contrib.go.opencensus.io/exporter/jaeger" + "contrib.go.opencensus.io/exporter/ocagent" + "contrib.go.opencensus.io/exporter/zipkin" + "github.com/micro/cli/v2" + "github.com/oklog/run" + openzipkin "github.com/openzipkin/zipkin-go" + zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http" + "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/owncloud/ocis-thumbnails/pkg/flagset" + "github.com/owncloud/ocis-thumbnails/pkg/metrics" + "github.com/owncloud/ocis-thumbnails/pkg/server/debug" + "github.com/owncloud/ocis-thumbnails/pkg/server/http" + "go.opencensus.io/stats/view" + "go.opencensus.io/trace" +) + +// Server is the entrypoint for the server command. +func Server(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "server", + Usage: "Start integrated server", + Flags: flagset.ServerWithConfig(cfg), + Before: func(c *cli.Context) error { + if cfg.HTTP.Root != "/" { + cfg.HTTP.Root = strings.TrimSuffix(cfg.HTTP.Root, "/") + } + + return nil + }, + Action: func(c *cli.Context) error { + logger := NewLogger(cfg) + + if cfg.Tracing.Enabled { + switch t := cfg.Tracing.Type; t { + case "agent": + exporter, err := ocagent.NewExporter( + ocagent.WithReconnectionPeriod(5*time.Second), + ocagent.WithAddress(cfg.Tracing.Endpoint), + ocagent.WithServiceName(cfg.Tracing.Service), + ) + + if err != nil { + logger.Error(). + Err(err). + Str("endpoint", cfg.Tracing.Endpoint). + Str("collector", cfg.Tracing.Collector). + Msg("Failed to create agent tracing") + + return err + } + + trace.RegisterExporter(exporter) + view.RegisterExporter(exporter) + + case "jaeger": + exporter, err := jaeger.NewExporter( + jaeger.Options{ + AgentEndpoint: cfg.Tracing.Endpoint, + CollectorEndpoint: cfg.Tracing.Collector, + ServiceName: cfg.Tracing.Service, + }, + ) + + if err != nil { + logger.Error(). + Err(err). + Str("endpoint", cfg.Tracing.Endpoint). + Str("collector", cfg.Tracing.Collector). + Msg("Failed to create jaeger tracing") + + return err + } + + trace.RegisterExporter(exporter) + + case "zipkin": + endpoint, err := openzipkin.NewEndpoint( + cfg.Tracing.Service, + cfg.Tracing.Endpoint, + ) + + if err != nil { + logger.Error(). + Err(err). + Str("endpoint", cfg.Tracing.Endpoint). + Str("collector", cfg.Tracing.Collector). + Msg("Failed to create zipkin tracing") + + return err + } + + exporter := zipkin.NewExporter( + zipkinhttp.NewReporter( + cfg.Tracing.Collector, + ), + endpoint, + ) + + trace.RegisterExporter(exporter) + + default: + logger.Warn(). + Str("type", t). + Msg("Unknown tracing backend") + } + + trace.ApplyConfig( + trace.Config{ + DefaultSampler: trace.AlwaysSample(), + }, + ) + } else { + logger.Debug(). + Msg("Tracing is not enabled") + } + + var ( + gr = run.Group{} + ctx, cancel = context.WithCancel(context.Background()) + metrics = metrics.New() + ) + + defer cancel() + + { + server, err := http.Server( + http.Logger(logger), + http.Context(ctx), + http.Config(cfg), + http.Metrics(metrics), + http.Flags(flagset.RootWithConfig(cfg)), + http.Flags(flagset.ServerWithConfig(cfg)), + ) + + if err != nil { + logger.Info(). + Err(err). + Str("transport", "http"). + Msg("Failed to initialize server") + + return err + } + + gr.Add(func() error { + return server.Run() + }, func(_ error) { + logger.Info(). + Str("transport", "http"). + Msg("Shutting down server") + + cancel() + }) + } + + { + server, err := debug.Server( + debug.Logger(logger), + debug.Context(ctx), + debug.Config(cfg), + ) + + if err != nil { + logger.Info(). + Err(err). + Str("transport", "debug"). + Msg("Failed to initialize server") + + return err + } + + gr.Add(func() error { + return server.ListenAndServe() + }, func(_ error) { + ctx, timeout := context.WithTimeout(ctx, 5*time.Second) + + defer timeout() + defer cancel() + + if err := server.Shutdown(ctx); err != nil { + logger.Info(). + Err(err). + Str("transport", "debug"). + Msg("Failed to shutdown server") + } else { + logger.Info(). + Str("transport", "debug"). + Msg("Shutting down server") + } + }) + } + + { + stop := make(chan os.Signal, 1) + + gr.Add(func() error { + signal.Notify(stop, os.Interrupt) + + <-stop + + return nil + }, func(err error) { + close(stop) + cancel() + }) + } + + return gr.Run() + }, + } +} diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 000000000..c92c9820c --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,46 @@ +package config + +// Log defines the available logging configuration. +type Log struct { + Level string + Pretty bool + Color bool +} + +// Debug defines the available debug configuration. +type Debug struct { + Addr string + Token string + Pprof bool + Zpages bool +} + +// HTTP defines the available http configuration. +type HTTP struct { + Addr string + Namespace string + Root string +} + +// Tracing defines the available tracing configuration. +type Tracing struct { + Enabled bool + Type string + Endpoint string + Collector string + Service string +} + +// Config combines all available configuration parts. +type Config struct { + File string + Log Log + Debug Debug + HTTP HTTP + Tracing Tracing +} + +// New initializes a new configuration with or without defaults. +func New() *Config { + return &Config{} +} diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go new file mode 100644 index 000000000..253f24d8b --- /dev/null +++ b/pkg/flagset/flagset.go @@ -0,0 +1,138 @@ +package flagset + +import ( + "github.com/micro/cli/v2" + "github.com/owncloud/ocis-thumbnails/pkg/config" +) + +// RootWithConfig applies cfg to the root flagset +func RootWithConfig(cfg *config.Config) []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "config-file", + Value: "", + Usage: "Path to config file", + EnvVars: []string{"THUMBNAILS_CONFIG_FILE"}, + Destination: &cfg.File, + }, + &cli.StringFlag{ + Name: "log-level", + Value: "info", + Usage: "Set logging level", + EnvVars: []string{"THUMBNAILS_LOG_LEVEL"}, + Destination: &cfg.Log.Level, + }, + &cli.BoolFlag{ + Name: "log-pretty", + Usage: "Enable pretty logging", + EnvVars: []string{"THUMBNAILS_LOG_PRETTY"}, + Destination: &cfg.Log.Pretty, + }, + &cli.BoolFlag{ + Name: "log-color", + Usage: "Enable colored logging", + EnvVars: []string{"THUMBNAILS_LOG_COLOR"}, + Destination: &cfg.Log.Color, + }, + } +} + +// HealthWithConfig applies cfg to the root flagset +func HealthWithConfig(cfg *config.Config) []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "debug-addr", + Value: "0.0.0.0:9114", + Usage: "Address to debug endpoint", + EnvVars: []string{"THUMBNAILS_DEBUG_ADDR"}, + Destination: &cfg.Debug.Addr, + }, + } +} + +// ServerWithConfig applies cfg to the root flagset +func ServerWithConfig(cfg *config.Config) []cli.Flag { + return []cli.Flag{ + &cli.BoolFlag{ + Name: "tracing-enabled", + Usage: "Enable sending traces", + EnvVars: []string{"THUMBNAILS_TRACING_ENABLED"}, + Destination: &cfg.Tracing.Enabled, + }, + &cli.StringFlag{ + Name: "tracing-type", + Value: "jaeger", + Usage: "Tracing backend type", + EnvVars: []string{"THUMBNAILS_TRACING_TYPE"}, + Destination: &cfg.Tracing.Type, + }, + &cli.StringFlag{ + Name: "tracing-endpoint", + Value: "", + Usage: "Endpoint for the agent", + EnvVars: []string{"THUMBNAILS_TRACING_ENDPOINT"}, + Destination: &cfg.Tracing.Endpoint, + }, + &cli.StringFlag{ + Name: "tracing-collector", + Value: "", + Usage: "Endpoint for the collector", + EnvVars: []string{"THUMBNAILS_TRACING_COLLECTOR"}, + Destination: &cfg.Tracing.Collector, + }, + &cli.StringFlag{ + Name: "tracing-service", + Value: "thumbnails", + Usage: "Service name for tracing", + EnvVars: []string{"THUMBNAILS_TRACING_SERVICE"}, + Destination: &cfg.Tracing.Service, + }, + &cli.StringFlag{ + Name: "debug-addr", + Value: "0.0.0.0:9114", + Usage: "Address to bind debug server", + EnvVars: []string{"THUMBNAILS_DEBUG_ADDR"}, + Destination: &cfg.Debug.Addr, + }, + &cli.StringFlag{ + Name: "debug-token", + Value: "", + Usage: "Token to grant metrics access", + EnvVars: []string{"THUMBNAILS_DEBUG_TOKEN"}, + Destination: &cfg.Debug.Token, + }, + &cli.BoolFlag{ + Name: "debug-pprof", + Usage: "Enable pprof debugging", + EnvVars: []string{"THUMBNAILS_DEBUG_PPROF"}, + Destination: &cfg.Debug.Pprof, + }, + &cli.BoolFlag{ + Name: "debug-zpages", + Usage: "Enable zpages debugging", + EnvVars: []string{"THUMBNAILS_DEBUG_ZPAGES"}, + Destination: &cfg.Debug.Zpages, + }, + &cli.StringFlag{ + Name: "http-addr", + Value: "0.0.0.0:9110", + Usage: "Address to bind http server", + EnvVars: []string{"THUMBNAILS_HTTP_ADDR"}, + Destination: &cfg.HTTP.Addr, + }, + &cli.StringFlag{ + Name: "http-namespace", + Value: "com.owncloud.web", + Usage: "Set the base namespace for the http namespace", + EnvVars: []string{"THUMBNAILS_HTTP_NAMESPACE"}, + Destination: &cfg.HTTP.Namespace, + }, + &cli.StringFlag{ + Name: "http-root", + Value: "/", + Usage: "Root path of http server", + EnvVars: []string{"THUMBNAILS_HTTP_ROOT"}, + Destination: &cfg.HTTP.Root, + }, + } +} diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go new file mode 100644 index 000000000..f08a93a98 --- /dev/null +++ b/pkg/metrics/metrics.go @@ -0,0 +1,32 @@ +package metrics + +var ( + // Namespace defines the namespace for the defines metrics. + Namespace = "ocis" + + // Subsystem defines the subsystem for the defines metrics. + Subsystem = "thumbnails" +) + +// Metrics defines the available metrics of this service. +type Metrics struct { + // Counter *prometheus.CounterVec +} + +// New initializes the available metrics. +func New() *Metrics { + m := &Metrics{ + // Counter: prometheus.NewCounterVec(prometheus.CounterOpts{ + // Namespace: Namespace, + // Subsystem: Subsystem, + // Name: "greet_total", + // Help: "How many greeting requests processed", + // }, []string{}), + } + + // prometheus.Register( + // m.Counter, + // ) + + return m +} diff --git a/pkg/server/debug/option.go b/pkg/server/debug/option.go new file mode 100644 index 000000000..b07f8b6ad --- /dev/null +++ b/pkg/server/debug/option.go @@ -0,0 +1,50 @@ +package debug + +import ( + "context" + + "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/owncloud/ocis-pkg/v2/log" +) + +// Option defines a single option function. +type Option func(o *Options) + +// Options defines the available options for this package. +type Options struct { + Logger log.Logger + Context context.Context + Config *config.Config +} + +// newOptions initializes the available default options. +func newOptions(opts ...Option) Options { + opt := Options{} + + for _, o := range opts { + o(&opt) + } + + return opt +} + +// Logger provides a function to set the logger option. +func Logger(val log.Logger) Option { + return func(o *Options) { + o.Logger = val + } +} + +// Context provides a function to set the context option. +func Context(val context.Context) Option { + return func(o *Options) { + o.Context = val + } +} + +// Config provides a function to set the config option. +func Config(val *config.Config) Option { + return func(o *Options) { + o.Config = val + } +} diff --git a/pkg/server/debug/server.go b/pkg/server/debug/server.go new file mode 100644 index 000000000..0ac9f253a --- /dev/null +++ b/pkg/server/debug/server.go @@ -0,0 +1,51 @@ +package debug + +import ( + "io" + "net/http" + + "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/owncloud/ocis-thumbnails/pkg/version" + "github.com/owncloud/ocis-pkg/v2/service/debug" +) + +// Server initializes the debug service and server. +func Server(opts ...Option) (*http.Server, error) { + options := newOptions(opts...) + + return debug.NewService( + debug.Logger(options.Logger), + debug.Name("ocis-thumbnails"), + debug.Version(version.String), + debug.Address(options.Config.Debug.Addr), + debug.Token(options.Config.Debug.Token), + debug.Pprof(options.Config.Debug.Pprof), + debug.Zpages(options.Config.Debug.Zpages), + debug.Health(health(options.Config)), + debug.Ready(ready(options.Config)), + ), nil +} + +// health implements the health check. +func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + + // TODO(tboerger): check if services are up and running + + io.WriteString(w, http.StatusText(http.StatusOK)) + } +} + +// ready implements the ready check. +func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + + // TODO(tboerger): check if services are up and running + + io.WriteString(w, http.StatusText(http.StatusOK)) + } +} diff --git a/pkg/server/http/option.go b/pkg/server/http/option.go new file mode 100644 index 000000000..5712bc5f2 --- /dev/null +++ b/pkg/server/http/option.go @@ -0,0 +1,76 @@ +package http + +import ( + "context" + + "github.com/micro/cli/v2" + "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/owncloud/ocis-thumbnails/pkg/metrics" + "github.com/owncloud/ocis-pkg/v2/log" +) + +// Option defines a single option function. +type Option func(o *Options) + +// Options defines the available options for this package. +type Options struct { + Namespace string + Logger log.Logger + Context context.Context + Config *config.Config + Metrics *metrics.Metrics + Flags []cli.Flag +} + +// newOptions initializes the available default options. +func newOptions(opts ...Option) Options { + opt := Options{} + + for _, o := range opts { + o(&opt) + } + + return opt +} + +// Logger provides a function to set the logger option. +func Logger(val log.Logger) Option { + return func(o *Options) { + o.Logger = val + } +} + +// Context provides a function to set the context option. +func Context(val context.Context) Option { + return func(o *Options) { + o.Context = val + } +} + +// Config provides a function to set the config option. +func Config(val *config.Config) Option { + return func(o *Options) { + o.Config = val + } +} + +// Metrics provides a function to set the metrics option. +func Metrics(val *metrics.Metrics) Option { + return func(o *Options) { + o.Metrics = val + } +} + +// Flags provides a function to set the flags option. +func Flags(val []cli.Flag) Option { + return func(o *Options) { + o.Flags = append(o.Flags, val...) + } +} + +// Namespace provides a function to set the Namespace option. +func Namespace(val string) Option { + return func(o *Options) { + o.Namespace = val + } +} diff --git a/pkg/server/http/server.go b/pkg/server/http/server.go new file mode 100644 index 000000000..66607a2fc --- /dev/null +++ b/pkg/server/http/server.go @@ -0,0 +1,56 @@ +package http + +import ( + svc "github.com/owncloud/ocis-thumbnails/pkg/service/v0" + "github.com/owncloud/ocis-thumbnails/pkg/version" + "github.com/owncloud/ocis-pkg/v2/middleware" + "github.com/owncloud/ocis-pkg/v2/service/http" +) + +// Server initializes the http service and server. +func Server(opts ...Option) (http.Service, error) { + options := newOptions(opts...) + + service := http.NewService( + http.Logger(options.Logger), + http.Name("thumbnails"), + http.Version(version.String), + http.Namespace(options.Config.HTTP.Namespace), + http.Address(options.Config.HTTP.Addr), + http.Context(options.Context), + http.Flags(options.Flags...), + ) + + handle := svc.NewService( + svc.Logger(options.Logger), + svc.Config(options.Config), + svc.Middleware( + middleware.RealIP, + middleware.RequestID, + middleware.Cache, + middleware.Cors, + middleware.Secure, + middleware.Version( + "thumbnails", + version.String, + ), + middleware.Logger( + options.Logger, + ), + ), + ) + + { + handle = svc.NewInstrument(handle, options.Metrics) + handle = svc.NewLogging(handle, options.Logger) + handle = svc.NewTracing(handle) + } + + service.Handle( + "/", + handle, + ) + + service.Init() + return service, nil +} diff --git a/pkg/service/v0/instrument.go b/pkg/service/v0/instrument.go new file mode 100644 index 000000000..5021bf1c9 --- /dev/null +++ b/pkg/service/v0/instrument.go @@ -0,0 +1,30 @@ +package svc + +import ( + "net/http" + + "github.com/owncloud/ocis-thumbnails/pkg/metrics" +) + +// NewInstrument returns a service that instruments metrics. +func NewInstrument(next Service, metrics *metrics.Metrics) Service { + return instrument{ + next: next, + metrics: metrics, + } +} + +type instrument struct { + next Service + metrics *metrics.Metrics +} + +// ServeHTTP implements the Service interface. +func (i instrument) ServeHTTP(w http.ResponseWriter, r *http.Request) { + i.next.ServeHTTP(w, r) +} + +// Dummy implements the Service interface. +func (i instrument) Dummy(w http.ResponseWriter, r *http.Request) { + i.next.Dummy(w, r) +} diff --git a/pkg/service/v0/logging.go b/pkg/service/v0/logging.go new file mode 100644 index 000000000..e0b10f8c2 --- /dev/null +++ b/pkg/service/v0/logging.go @@ -0,0 +1,30 @@ +package svc + +import ( + "net/http" + + "github.com/owncloud/ocis-pkg/v2/log" +) + +// NewLogging returns a service that logs messages. +func NewLogging(next Service, logger log.Logger) Service { + return logging{ + next: next, + logger: logger, + } +} + +type logging struct { + next Service + logger log.Logger +} + +// ServeHTTP implements the Service interface. +func (l logging) ServeHTTP(w http.ResponseWriter, r *http.Request) { + l.next.ServeHTTP(w, r) +} + +// Dummy implements the Service interface. +func (l logging) Dummy(w http.ResponseWriter, r *http.Request) { + l.next.Dummy(w, r) +} diff --git a/pkg/service/v0/option.go b/pkg/service/v0/option.go new file mode 100644 index 000000000..b8d33f7b7 --- /dev/null +++ b/pkg/service/v0/option.go @@ -0,0 +1,50 @@ +package svc + +import ( + "net/http" + + "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/owncloud/ocis-pkg/v2/log" +) + +// Option defines a single option function. +type Option func(o *Options) + +// Options defines the available options for this package. +type Options struct { + Logger log.Logger + Config *config.Config + Middleware []func(http.Handler) http.Handler +} + +// newOptions initializes the available default options. +func newOptions(opts ...Option) Options { + opt := Options{} + + for _, o := range opts { + o(&opt) + } + + return opt +} + +// Logger provides a function to set the logger option. +func Logger(val log.Logger) Option { + return func(o *Options) { + o.Logger = val + } +} + +// Config provides a function to set the config option. +func Config(val *config.Config) Option { + return func(o *Options) { + o.Config = val + } +} + +// Middleware provides a function to set the middleware option. +func Middleware(val ...func(http.Handler) http.Handler) Option { + return func(o *Options) { + o.Middleware = val + } +} diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go new file mode 100644 index 000000000..a251734d0 --- /dev/null +++ b/pkg/service/v0/service.go @@ -0,0 +1,52 @@ +package svc + +import ( + "net/http" + + "github.com/go-chi/chi" + "github.com/owncloud/ocis-thumbnails/pkg/config" +) + +// Service defines the extension handlers. +type Service interface { + ServeHTTP(http.ResponseWriter, *http.Request) + Dummy(http.ResponseWriter, *http.Request) +} + +// NewService returns a service implementation for Service. +func NewService(opts ...Option) Service { + options := newOptions(opts...) + + m := chi.NewMux() + m.Use(options.Middleware...) + + svc := Thumbnails{ + config: options.Config, + mux: m, + } + + m.Route(options.Config.HTTP.Root, func(r chi.Router) { + r.Get("/", svc.Dummy) + }) + + return svc +} + +// Thumbnails defines implements the business logic for Service. +type Thumbnails struct { + config *config.Config + mux *chi.Mux +} + +// ServeHTTP implements the Service interface. +func (g Thumbnails) ServeHTTP(w http.ResponseWriter, r *http.Request) { + g.mux.ServeHTTP(w, r) +} + +// Dummy implements the Service interface. +func (g Thumbnails) Dummy(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + + w.Write([]byte("Hello ocis-thumbnails!")) +} diff --git a/pkg/service/v0/tracing.go b/pkg/service/v0/tracing.go new file mode 100644 index 000000000..48da0b320 --- /dev/null +++ b/pkg/service/v0/tracing.go @@ -0,0 +1,26 @@ +package svc + +import ( + "net/http" +) + +// NewTracing returns a service that instruments traces. +func NewTracing(next Service) Service { + return tracing{ + next: next, + } +} + +type tracing struct { + next Service +} + +// ServeHTTP implements the Service interface. +func (t tracing) ServeHTTP(w http.ResponseWriter, r *http.Request) { + t.next.ServeHTTP(w, r) +} + +// Dummy implements the Service interface. +func (t tracing) Dummy(w http.ResponseWriter, r *http.Request) { + t.next.Dummy(w, r) +} diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 000000000..81e81815f --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,19 @@ +package version + +import ( + "time" +) + +var ( + // String gets defined by the build system. + String = "0.0.0" + + // Date indicates the build date. + Date = "00000000" +) + +// Compiled returns the compile time of this service. +func Compiled() time.Time { + t, _ := time.Parse("20060102", Date) + return t +} diff --git a/reflex.conf b/reflex.conf new file mode 100644 index 000000000..b8309f8e9 --- /dev/null +++ b/reflex.conf @@ -0,0 +1,2 @@ +# backend +-r '^(cmd|pkg)/.*\.go$' -R '^node_modules/' -s -- sh -c 'make bin/ocis-thumbnails-debug && bin/ocis-thumbnails-debug --log-level debug server --debug-pprof --debug-zpages' From a98df0b11fbe9554e395594bea0d5e6a83e01f4a Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 3 Mar 2020 16:40:02 +0100 Subject: [PATCH 03/80] first prototype of the thumbnail service Currently uses in memory caching and loads the file from the local filesystem. --- go.mod | 1 + go.sum | 2 + pkg/service/v0/instrument.go | 4 +- pkg/service/v0/logging.go | 4 +- pkg/service/v0/service.go | 45 ++++++++++++---- pkg/service/v0/tracing.go | 4 +- pkg/thumbnails/cache/cache.go | 11 ++++ pkg/thumbnails/cache/filesystem.go | 3 ++ pkg/thumbnails/cache/inmemory.go | 22 ++++++++ pkg/thumbnails/encoding.go | 47 ++++++++++++++++ pkg/thumbnails/thumbnails.go | 86 ++++++++++++++++++++++++++++++ 11 files changed, 214 insertions(+), 15 deletions(-) create mode 100644 pkg/thumbnails/cache/cache.go create mode 100644 pkg/thumbnails/cache/filesystem.go create mode 100644 pkg/thumbnails/cache/inmemory.go create mode 100644 pkg/thumbnails/encoding.go create mode 100644 pkg/thumbnails/thumbnails.go diff --git a/go.mod b/go.mod index 7069238a2..0cde5ba97 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/go-chi/chi v4.0.2+incompatible github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/micro/cli/v2 v2.1.1 + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/ogier/pflag v0.0.1 // indirect github.com/oklog/run v1.0.0 github.com/openzipkin/zipkin-go v0.2.2 diff --git a/go.sum b/go.sum index bde3bff73..0767e8baf 100644 --- a/go.sum +++ b/go.sum @@ -522,6 +522,8 @@ github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nats-io/stan.go v0.5.0/go.mod h1:dYqB+vMN3C2F9pT1FRQpg9eHbjPj6mP0yYuyBNuXHZE= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nlopes/slack v0.6.0/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk= github.com/nlopes/slack v0.6.1-0.20191106133607-d06c2a2b3249/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk= github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw= diff --git a/pkg/service/v0/instrument.go b/pkg/service/v0/instrument.go index 5021bf1c9..9c3a6d999 100644 --- a/pkg/service/v0/instrument.go +++ b/pkg/service/v0/instrument.go @@ -25,6 +25,6 @@ func (i instrument) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Dummy implements the Service interface. -func (i instrument) Dummy(w http.ResponseWriter, r *http.Request) { - i.next.Dummy(w, r) +func (i instrument) Thumbnails(w http.ResponseWriter, r *http.Request) { + i.next.Thumbnails(w, r) } diff --git a/pkg/service/v0/logging.go b/pkg/service/v0/logging.go index e0b10f8c2..a439f1efd 100644 --- a/pkg/service/v0/logging.go +++ b/pkg/service/v0/logging.go @@ -25,6 +25,6 @@ func (l logging) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Dummy implements the Service interface. -func (l logging) Dummy(w http.ResponseWriter, r *http.Request) { - l.next.Dummy(w, r) +func (l logging) Thumbnails(w http.ResponseWriter, r *http.Request) { + l.next.Thumbnails(w, r) } diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index a251734d0..e47c73c99 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -2,15 +2,18 @@ package svc import ( "net/http" + "strconv" "github.com/go-chi/chi" "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnails" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/cache" ) // Service defines the extension handlers. type Service interface { ServeHTTP(http.ResponseWriter, *http.Request) - Dummy(http.ResponseWriter, *http.Request) + Thumbnails(http.ResponseWriter, *http.Request) } // NewService returns a service implementation for Service. @@ -23,10 +26,13 @@ func NewService(opts ...Option) Service { svc := Thumbnails{ config: options.Config, mux: m, + manager: thumbnails.SimpleManager{ + Cache: cache.NewInMemoryCache(), + }, } m.Route(options.Config.HTTP.Root, func(r chi.Router) { - r.Get("/", svc.Dummy) + r.Get("/thumbnails", svc.Thumbnails) }) return svc @@ -34,8 +40,9 @@ func NewService(opts ...Option) Service { // Thumbnails defines implements the business logic for Service. type Thumbnails struct { - config *config.Config - mux *chi.Mux + config *config.Config + mux *chi.Mux + manager thumbnails.Manager } // ServeHTTP implements the Service interface. @@ -43,10 +50,30 @@ func (g Thumbnails) ServeHTTP(w http.ResponseWriter, r *http.Request) { g.mux.ServeHTTP(w, r) } -// Dummy implements the Service interface. -func (g Thumbnails) Dummy(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusOK) +// Thumbnails provides the endpoint to retrieve a thumbnail for an image +func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query() + width, _ := strconv.Atoi(query.Get("w")) + height, _ := strconv.Atoi(query.Get("h")) + fileType := query.Get("type") + fileID := query.Get("file_id") - w.Write([]byte("Hello ocis-thumbnails!")) + encoder := thumbnails.EncoderForType(fileType) + if encoder == nil { + // TODO: better error responses + w.Write([]byte("can't encode that")) + return + } + ctx := thumbnails.ThumbnailContext{ + Width: width, + Height: height, + ImagePath: fileID, + Encoder: encoder, + } + thumbnail, err := g.manager.Get(ctx) + if err != nil { + w.Write([]byte(err.Error())) + } + + w.Write(thumbnail) } diff --git a/pkg/service/v0/tracing.go b/pkg/service/v0/tracing.go index 48da0b320..444afd3ec 100644 --- a/pkg/service/v0/tracing.go +++ b/pkg/service/v0/tracing.go @@ -21,6 +21,6 @@ func (t tracing) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Dummy implements the Service interface. -func (t tracing) Dummy(w http.ResponseWriter, r *http.Request) { - t.next.Dummy(w, r) +func (t tracing) Thumbnails(w http.ResponseWriter, r *http.Request) { + t.next.Thumbnails(w, r) } diff --git a/pkg/thumbnails/cache/cache.go b/pkg/thumbnails/cache/cache.go new file mode 100644 index 000000000..71db593cd --- /dev/null +++ b/pkg/thumbnails/cache/cache.go @@ -0,0 +1,11 @@ +package cache + +import ( + "image" +) + +// Cache defines the interface for a thumbnail cache. +type Cache interface { + Get(key string) image.Image + Set(key string, thumbnail image.Image) (image.Image, error) +} diff --git a/pkg/thumbnails/cache/filesystem.go b/pkg/thumbnails/cache/filesystem.go new file mode 100644 index 000000000..b3e25a4d8 --- /dev/null +++ b/pkg/thumbnails/cache/filesystem.go @@ -0,0 +1,3 @@ +package cache + +// TODO: implement filesystem cache for longer persistence diff --git a/pkg/thumbnails/cache/inmemory.go b/pkg/thumbnails/cache/inmemory.go new file mode 100644 index 000000000..d9c45fb66 --- /dev/null +++ b/pkg/thumbnails/cache/inmemory.go @@ -0,0 +1,22 @@ +package cache + +import "image" + +func NewInMemoryCache() InMemoryCache { + return InMemoryCache{ + store: make(map[string]image.Image), + } +} + +type InMemoryCache struct { + store map[string]image.Image +} + +func (fsc InMemoryCache) Get(key string) image.Image { + return fsc.store[key] +} + +func (fsc InMemoryCache) Set(key string, thumbnail image.Image) (image.Image, error) { + fsc.store[key] = thumbnail + return thumbnail, nil +} diff --git a/pkg/thumbnails/encoding.go b/pkg/thumbnails/encoding.go new file mode 100644 index 000000000..60a02f59b --- /dev/null +++ b/pkg/thumbnails/encoding.go @@ -0,0 +1,47 @@ +package thumbnails + +import ( + "image" + "image/jpeg" + "image/png" + "io" + "strings" +) + +type Encoder interface { + Encode(io.Writer, image.Image) error + Types() []string +} + +type PngEncoder struct{} + +func (e PngEncoder) Encode(w io.Writer, i image.Image) error { + return png.Encode(w, i) +} + +func (e PngEncoder) Types() []string { + return []string{"png"} +} + +type JpegEncoder struct{} + +func (e JpegEncoder) Encode(w io.Writer, i image.Image) error { + return jpeg.Encode(w, i, nil) +} + +func (e JpegEncoder) Types() []string { + return []string{"jpeg", "jpg"} +} + +func EncoderForType(fileType string) Encoder { + switch strings.ToLower(fileType) { + case "png": + return PngEncoder{} + case "jpg": + fallthrough + case "jpeg": + return JpegEncoder{} + default: + return nil + } +} diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go new file mode 100644 index 000000000..07d163e7f --- /dev/null +++ b/pkg/thumbnails/thumbnails.go @@ -0,0 +1,86 @@ +package thumbnails + +import ( + "bytes" + "image" + "os" + "strings" + "time" + + "github.com/nfnt/resize" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/cache" +) + +// ThumbnailContext bundles information needed to generate a thumbnail for afile +type ThumbnailContext struct { + Width int + Height int + ImagePath string + + Encoder Encoder +} + +// Manager is responsible for generating thumbnails +type Manager interface { + // Get will return a thumbnail for a file + Get(ThumbnailContext) ([]byte, error) + GetCached(ThumbnailContext) []byte +} + +// SimpleManager is a simple implementation of Manager +type SimpleManager struct { + Cache cache.Cache +} + +// Get implements the Get Method of Manager +func (s SimpleManager) Get(ctx ThumbnailContext) ([]byte, error) { + key := buildCacheKey(ctx) + + cached := s.Cache.Get(key) + if cached == nil { + thumbnail := s.generate(ctx) + s.Cache.Set(key, thumbnail) + cached = thumbnail + } + + buf := new(bytes.Buffer) + err := ctx.Encoder.Encode(buf, cached) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// GetCached tries to get the cached thumbnail and return it. +// If there is no cached thumbnail it will return nil +func (s SimpleManager) GetCached(ctx ThumbnailContext) []byte { + key := buildCacheKey(ctx) + cached := s.Cache.Get(key) + if cached == nil { + return nil + } + buf := new(bytes.Buffer) + ctx.Encoder.Encode(buf, cached) + return buf.Bytes() +} + +func (s SimpleManager) generate(ctx ThumbnailContext) image.Image { + // TODO: remove, just for demo purposes + time.Sleep(time.Second * 2) + + // TODO: get file from reva + reader, _ := os.Open(ctx.ImagePath) + defer reader.Close() + m, _, _ := image.Decode(reader) + thumbnail := resize.Thumbnail(uint(ctx.Width), uint(ctx.Height), m, resize.Lanczos2) + return thumbnail +} + +func buildCacheKey(ctx ThumbnailContext) string { + parts := []string{ + ctx.ImagePath, + string(ctx.Width) + "x" + string(ctx.Height), + strings.Join(ctx.Encoder.Types(), ","), + } + return strings.Join(parts, "+") +} From 34dcaf9ffe74fcf561be2ce19aa5e027a93fb2ad Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 4 Mar 2020 16:55:13 +0100 Subject: [PATCH 04/80] implement file sources If the file is not cached we have to get it from somewhere. For that I implemented a webdav file source which gets the file from reva. --- pkg/flagset/flagset.go | 4 +-- pkg/service/v0/service.go | 45 ++++++++++++++++++++++----- pkg/thumbnails/imgsource/imgsource.go | 33 ++++++++++++++++++++ pkg/thumbnails/imgsource/webdav.go | 41 ++++++++++++++++++++++++ pkg/thumbnails/thumbnails.go | 28 ++++++----------- 5 files changed, 123 insertions(+), 28 deletions(-) create mode 100644 pkg/thumbnails/imgsource/imgsource.go create mode 100644 pkg/thumbnails/imgsource/webdav.go diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 253f24d8b..39d4005b7 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -89,7 +89,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { }, &cli.StringFlag{ Name: "debug-addr", - Value: "0.0.0.0:9114", + Value: "0.0.0.0:9194", Usage: "Address to bind debug server", EnvVars: []string{"THUMBNAILS_DEBUG_ADDR"}, Destination: &cfg.Debug.Addr, @@ -115,7 +115,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { }, &cli.StringFlag{ Name: "http-addr", - Value: "0.0.0.0:9110", + Value: "0.0.0.0:9190", Usage: "Address to bind http server", EnvVars: []string{"THUMBNAILS_HTTP_ADDR"}, Destination: &cfg.HTTP.Addr, diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index e47c73c99..d652ecf75 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -8,6 +8,7 @@ import ( "github.com/owncloud/ocis-thumbnails/pkg/config" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/cache" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/imgsource" ) // Service defines the extension handlers. @@ -29,6 +30,9 @@ func NewService(opts ...Option) Service { manager: thumbnails.SimpleManager{ Cache: cache.NewInMemoryCache(), }, + source: imgsource.WebDav{ + Basepath: "http://localhost:9140/remote.php/webdav/", + }, } m.Route(options.Config.HTTP.Root, func(r chi.Router) { @@ -43,6 +47,7 @@ type Thumbnails struct { config *config.Config mux *chi.Mux manager thumbnails.Manager + source imgsource.Source } // ServeHTTP implements the Service interface. @@ -53,27 +58,53 @@ func (g Thumbnails) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Thumbnails provides the endpoint to retrieve a thumbnail for an image func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() - width, _ := strconv.Atoi(query.Get("w")) - height, _ := strconv.Atoi(query.Get("h")) + width, _ := strconv.Atoi(query.Get("width")) + height, _ := strconv.Atoi(query.Get("height")) fileType := query.Get("type") - fileID := query.Get("file_id") + filePath := query.Get("file_path") encoder := thumbnails.EncoderForType(fileType) if encoder == nil { // TODO: better error responses + w.WriteHeader(http.StatusBadRequest) w.Write([]byte("can't encode that")) return } ctx := thumbnails.ThumbnailContext{ Width: width, Height: height, - ImagePath: fileID, + ImagePath: filePath, Encoder: encoder, } - thumbnail, err := g.manager.Get(ctx) - if err != nil { - w.Write([]byte(err.Error())) + + thumbnail := g.manager.GetCached(ctx) + if thumbnail != nil { + w.Write(thumbnail) + return } + auth := r.Header.Get("Authorization") + + sCtx := imgsource.NewContext() + sCtx.Set(imgsource.WebDavAuth, auth) + // TODO: clean up error handling + img, err := g.source.Get(ctx.ImagePath, sCtx) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + if img == nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("img is nil")) + return + } + thumbnail, err = g.manager.Get(ctx, img) + if err != nil { + w.Write([]byte(err.Error())) + return + } + + w.WriteHeader(http.StatusCreated) w.Write(thumbnail) } diff --git a/pkg/thumbnails/imgsource/imgsource.go b/pkg/thumbnails/imgsource/imgsource.go new file mode 100644 index 000000000..6697c9bdc --- /dev/null +++ b/pkg/thumbnails/imgsource/imgsource.go @@ -0,0 +1,33 @@ +package imgsource + +import "image" + +// Source defines the interface for image sources +type Source interface { + Get(path string, ctx SourceContext) (image.Image, error) +} + +// NewContext creates a new SourceContext instance +func NewContext() SourceContext { + return SourceContext{ + m: make(map[string]interface{}), + } +} + +// SourceContext is used to pass source specific parameters +type SourceContext struct { + m map[string]interface{} +} + +// GetString tries to cast the value to a string +func (s SourceContext) GetString(key string) string { + if s, ok := s.m[key].(string); ok { + return s + } + return "" +} + +// Set sets a value +func (s SourceContext) Set(key string, val interface{}) { + s.m[key] = val +} diff --git a/pkg/thumbnails/imgsource/webdav.go b/pkg/thumbnails/imgsource/webdav.go new file mode 100644 index 000000000..5637d943d --- /dev/null +++ b/pkg/thumbnails/imgsource/webdav.go @@ -0,0 +1,41 @@ +package imgsource + +import ( + "fmt" + "image" + "net/http" + "net/url" + "path" +) + +// WebDav implements the Source interface for webdav services +type WebDav struct { + Basepath string +} + +const ( + // WebDavAuth is the parameter name for the autorization token + WebDavAuth = "Authorization" +) + +// Get downloads the file from a webdav service +func (s WebDav) Get(file string, ctx SourceContext) (image.Image, error) { + u, _ := url.Parse(s.Basepath) + u.Path = path.Join(u.Path, file) + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + return nil, fmt.Errorf("could not get the file %s error: %s", file, err.Error()) + } + + auth := ctx.GetString(WebDavAuth) + req.Header.Add("Authorization", auth) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("could not get the file %s error: %s", file, err.Error()) + } + + img, _, _ := image.Decode(resp.Body) + return img, nil +} diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go index 07d163e7f..5f1614add 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnails/thumbnails.go @@ -3,7 +3,6 @@ package thumbnails import ( "bytes" "image" - "os" "strings" "time" @@ -16,14 +15,13 @@ type ThumbnailContext struct { Width int Height int ImagePath string - - Encoder Encoder + Encoder Encoder } // Manager is responsible for generating thumbnails type Manager interface { // Get will return a thumbnail for a file - Get(ThumbnailContext) ([]byte, error) + Get(ThumbnailContext, image.Image) ([]byte, error) GetCached(ThumbnailContext) []byte } @@ -33,18 +31,14 @@ type SimpleManager struct { } // Get implements the Get Method of Manager -func (s SimpleManager) Get(ctx ThumbnailContext) ([]byte, error) { - key := buildCacheKey(ctx) +func (s SimpleManager) Get(ctx ThumbnailContext, img image.Image) ([]byte, error) { + thumbnail := s.generate(ctx, img) - cached := s.Cache.Get(key) - if cached == nil { - thumbnail := s.generate(ctx) - s.Cache.Set(key, thumbnail) - cached = thumbnail - } + key := buildCacheKey(ctx) + s.Cache.Set(key, thumbnail) buf := new(bytes.Buffer) - err := ctx.Encoder.Encode(buf, cached) + err := ctx.Encoder.Encode(buf, thumbnail) if err != nil { return nil, err } @@ -64,15 +58,11 @@ func (s SimpleManager) GetCached(ctx ThumbnailContext) []byte { return buf.Bytes() } -func (s SimpleManager) generate(ctx ThumbnailContext) image.Image { +func (s SimpleManager) generate(ctx ThumbnailContext, img image.Image) image.Image { // TODO: remove, just for demo purposes time.Sleep(time.Second * 2) - // TODO: get file from reva - reader, _ := os.Open(ctx.ImagePath) - defer reader.Close() - m, _, _ := image.Decode(reader) - thumbnail := resize.Thumbnail(uint(ctx.Width), uint(ctx.Height), m, resize.Lanczos2) + thumbnail := resize.Thumbnail(uint(ctx.Width), uint(ctx.Height), img, resize.Lanczos2) return thumbnail } From 0978653f9077d7ddebbc67beb9fec206b19e9201 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Thu, 5 Mar 2020 15:04:40 +0100 Subject: [PATCH 05/80] rename cache to storage --- pkg/service/v0/service.go | 6 ++--- pkg/thumbnails/cache/inmemory.go | 22 ---------------- .../{cache => storage}/filesystem.go | 2 +- pkg/thumbnails/storage/inmemory.go | 22 ++++++++++++++++ .../{cache/cache.go => storage/storage.go} | 6 ++--- pkg/thumbnails/thumbnails.go | 26 +++++++++---------- 6 files changed, 42 insertions(+), 42 deletions(-) delete mode 100644 pkg/thumbnails/cache/inmemory.go rename pkg/thumbnails/{cache => storage}/filesystem.go (78%) create mode 100644 pkg/thumbnails/storage/inmemory.go rename pkg/thumbnails/{cache/cache.go => storage/storage.go} (54%) diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index d652ecf75..4d3b45e5a 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -7,8 +7,8 @@ import ( "github.com/go-chi/chi" "github.com/owncloud/ocis-thumbnails/pkg/config" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails" - "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/cache" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/imgsource" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" ) // Service defines the extension handlers. @@ -28,7 +28,7 @@ func NewService(opts ...Option) Service { config: options.Config, mux: m, manager: thumbnails.SimpleManager{ - Cache: cache.NewInMemoryCache(), + Storage: storage.NewInMemoryStorage(), }, source: imgsource.WebDav{ Basepath: "http://localhost:9140/remote.php/webdav/", @@ -77,7 +77,7 @@ func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) { Encoder: encoder, } - thumbnail := g.manager.GetCached(ctx) + thumbnail := g.manager.GetStored(ctx) if thumbnail != nil { w.Write(thumbnail) return diff --git a/pkg/thumbnails/cache/inmemory.go b/pkg/thumbnails/cache/inmemory.go deleted file mode 100644 index d9c45fb66..000000000 --- a/pkg/thumbnails/cache/inmemory.go +++ /dev/null @@ -1,22 +0,0 @@ -package cache - -import "image" - -func NewInMemoryCache() InMemoryCache { - return InMemoryCache{ - store: make(map[string]image.Image), - } -} - -type InMemoryCache struct { - store map[string]image.Image -} - -func (fsc InMemoryCache) Get(key string) image.Image { - return fsc.store[key] -} - -func (fsc InMemoryCache) Set(key string, thumbnail image.Image) (image.Image, error) { - fsc.store[key] = thumbnail - return thumbnail, nil -} diff --git a/pkg/thumbnails/cache/filesystem.go b/pkg/thumbnails/storage/filesystem.go similarity index 78% rename from pkg/thumbnails/cache/filesystem.go rename to pkg/thumbnails/storage/filesystem.go index b3e25a4d8..db9db0b03 100644 --- a/pkg/thumbnails/cache/filesystem.go +++ b/pkg/thumbnails/storage/filesystem.go @@ -1,3 +1,3 @@ -package cache +package storage // TODO: implement filesystem cache for longer persistence diff --git a/pkg/thumbnails/storage/inmemory.go b/pkg/thumbnails/storage/inmemory.go new file mode 100644 index 000000000..307a84719 --- /dev/null +++ b/pkg/thumbnails/storage/inmemory.go @@ -0,0 +1,22 @@ +package storage + +import "image" + +func NewInMemoryStorage() InMemoryStorage { + return InMemoryStorage{ + store: make(map[string]image.Image), + } +} + +type InMemoryStorage struct { + store map[string]image.Image +} + +func (fsc InMemoryStorage) Get(key string) image.Image { + return fsc.store[key] +} + +func (fsc InMemoryStorage) Set(key string, thumbnail image.Image) (image.Image, error) { + fsc.store[key] = thumbnail + return thumbnail, nil +} diff --git a/pkg/thumbnails/cache/cache.go b/pkg/thumbnails/storage/storage.go similarity index 54% rename from pkg/thumbnails/cache/cache.go rename to pkg/thumbnails/storage/storage.go index 71db593cd..11a41771e 100644 --- a/pkg/thumbnails/cache/cache.go +++ b/pkg/thumbnails/storage/storage.go @@ -1,11 +1,11 @@ -package cache +package storage import ( "image" ) -// Cache defines the interface for a thumbnail cache. -type Cache interface { +// Storage defines the interface for a thumbnail store. +type Storage interface { Get(key string) image.Image Set(key string, thumbnail image.Image) (image.Image, error) } diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go index 5f1614add..61b93bf6e 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnails/thumbnails.go @@ -7,7 +7,7 @@ import ( "time" "github.com/nfnt/resize" - "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/cache" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" ) // ThumbnailContext bundles information needed to generate a thumbnail for afile @@ -22,20 +22,20 @@ type ThumbnailContext struct { type Manager interface { // Get will return a thumbnail for a file Get(ThumbnailContext, image.Image) ([]byte, error) - GetCached(ThumbnailContext) []byte + GetStored(ThumbnailContext) []byte } // SimpleManager is a simple implementation of Manager type SimpleManager struct { - Cache cache.Cache + Storage storage.Storage } // Get implements the Get Method of Manager func (s SimpleManager) Get(ctx ThumbnailContext, img image.Image) ([]byte, error) { thumbnail := s.generate(ctx, img) - key := buildCacheKey(ctx) - s.Cache.Set(key, thumbnail) + key := buildKey(ctx) + s.Storage.Set(key, thumbnail) buf := new(bytes.Buffer) err := ctx.Encoder.Encode(buf, thumbnail) @@ -45,16 +45,16 @@ func (s SimpleManager) Get(ctx ThumbnailContext, img image.Image) ([]byte, error return buf.Bytes(), nil } -// GetCached tries to get the cached thumbnail and return it. -// If there is no cached thumbnail it will return nil -func (s SimpleManager) GetCached(ctx ThumbnailContext) []byte { - key := buildCacheKey(ctx) - cached := s.Cache.Get(key) - if cached == nil { +// GetStored tries to get the stored thumbnail and return it. +// If there is no stored thumbnail it will return nil +func (s SimpleManager) GetStored(ctx ThumbnailContext) []byte { + key := buildKey(ctx) + stored := s.Storage.Get(key) + if stored == nil { return nil } buf := new(bytes.Buffer) - ctx.Encoder.Encode(buf, cached) + ctx.Encoder.Encode(buf, stored) return buf.Bytes() } @@ -66,7 +66,7 @@ func (s SimpleManager) generate(ctx ThumbnailContext, img image.Image) image.Ima return thumbnail } -func buildCacheKey(ctx ThumbnailContext) string { +func buildKey(ctx ThumbnailContext) string { parts := []string{ ctx.ImagePath, string(ctx.Width) + "x" + string(ctx.Height), From f55199f01be2f928028365a6b0ac52b19593d764 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Thu, 5 Mar 2020 17:13:19 +0100 Subject: [PATCH 06/80] implement prototype filesystem storage --- pkg/service/v0/service.go | 2 + pkg/thumbnails/storage/filesystem.go | 69 +++++++++++++++++++++++++++- pkg/thumbnails/storage/inmemory.go | 33 ++++++++----- pkg/thumbnails/storage/storage.go | 14 ++++-- pkg/thumbnails/thumbnails.go | 30 ++++++------ 5 files changed, 116 insertions(+), 32 deletions(-) diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 4d3b45e5a..88e4664fd 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -62,6 +62,7 @@ func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) { height, _ := strconv.Atoi(query.Get("height")) fileType := query.Get("type") filePath := query.Get("file_path") + etag := query.Get("etag") encoder := thumbnails.EncoderForType(fileType) if encoder == nil { @@ -75,6 +76,7 @@ func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) { Height: height, ImagePath: filePath, Encoder: encoder, + ETag: etag, } thumbnail := g.manager.GetStored(ctx) diff --git a/pkg/thumbnails/storage/filesystem.go b/pkg/thumbnails/storage/filesystem.go index db9db0b03..0466a1f3f 100644 --- a/pkg/thumbnails/storage/filesystem.go +++ b/pkg/thumbnails/storage/filesystem.go @@ -1,3 +1,70 @@ package storage -// TODO: implement filesystem cache for longer persistence +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" +) + +const BasePath = "/home/corby/tmp/thumbnails/fs/" + +type FileSystem struct { +} + +func (s FileSystem) Get(key string) []byte { + content, err := ioutil.ReadFile(BasePath + key) + if err != nil { + return nil + } + + return content +} + +func (s FileSystem) Set(key string, img []byte) error { + folder := filepath.Dir(BasePath + key) + if err := createFolderIfNotExists(folder); err != nil { + return fmt.Errorf("error while creating folder %s", folder) + } + + f, err := os.Create(BasePath + key) + if err != nil { + fmt.Println(err.Error()) + return err + } + defer f.Close() + _, err = f.Write(img) + if err != nil { + return err + } + return nil +} + +func (s FileSystem) BuildKey(ctx StorageContext) string { + etag := ctx.ETag + filetype := ctx.Types[0] + filename := strconv.Itoa(ctx.Width) + "x" + strconv.Itoa(ctx.Height) + "." + filetype + + key := new(bytes.Buffer) + key.WriteString(etag[:2]) + key.WriteRune('/') + key.WriteString(etag[2:4]) + key.WriteRune('/') + key.WriteString(etag[4:]) + key.WriteRune('/') + key.WriteString(filename) + + return key.String() +} + +func createFolderIfNotExists(folder string) error { + if _, err := os.Stat(folder); os.IsNotExist(err) { + err := os.MkdirAll(folder, 0700) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/thumbnails/storage/inmemory.go b/pkg/thumbnails/storage/inmemory.go index 307a84719..d66d0712e 100644 --- a/pkg/thumbnails/storage/inmemory.go +++ b/pkg/thumbnails/storage/inmemory.go @@ -1,22 +1,33 @@ package storage -import "image" +import ( + "strings" +) -func NewInMemoryStorage() InMemoryStorage { - return InMemoryStorage{ - store: make(map[string]image.Image), +func NewInMemoryStorage() InMemory { + return InMemory{ + store: make(map[string][]byte), } } -type InMemoryStorage struct { - store map[string]image.Image +type InMemory struct { + store map[string][]byte } -func (fsc InMemoryStorage) Get(key string) image.Image { - return fsc.store[key] +func (s InMemory) Get(key string) []byte { + return s.store[key] } -func (fsc InMemoryStorage) Set(key string, thumbnail image.Image) (image.Image, error) { - fsc.store[key] = thumbnail - return thumbnail, nil +func (s InMemory) Set(key string, thumbnail []byte) error { + s.store[key] = thumbnail + return nil +} + +func (s InMemory) BuildKey(ctx StorageContext) string { + parts := []string{ + ctx.ETag, + string(ctx.Width) + "x" + string(ctx.Height), + strings.Join(ctx.Types, ","), + } + return strings.Join(parts, "+") } diff --git a/pkg/thumbnails/storage/storage.go b/pkg/thumbnails/storage/storage.go index 11a41771e..4582433f2 100644 --- a/pkg/thumbnails/storage/storage.go +++ b/pkg/thumbnails/storage/storage.go @@ -1,11 +1,15 @@ package storage -import ( - "image" -) +type StorageContext struct { + ETag string + Types []string + Width int + Height int +} // Storage defines the interface for a thumbnail store. type Storage interface { - Get(key string) image.Image - Set(key string, thumbnail image.Image) (image.Image, error) + Get(string) []byte + Set(string, []byte) error + BuildKey(StorageContext) string } diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go index 61b93bf6e..10235f562 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnails/thumbnails.go @@ -3,7 +3,6 @@ package thumbnails import ( "bytes" "image" - "strings" "time" "github.com/nfnt/resize" @@ -16,6 +15,7 @@ type ThumbnailContext struct { Height int ImagePath string Encoder Encoder + ETag string } // Manager is responsible for generating thumbnails @@ -34,28 +34,27 @@ type SimpleManager struct { func (s SimpleManager) Get(ctx ThumbnailContext, img image.Image) ([]byte, error) { thumbnail := s.generate(ctx, img) - key := buildKey(ctx) - s.Storage.Set(key, thumbnail) + key := s.Storage.BuildKey(mapToStorageContext(ctx)) buf := new(bytes.Buffer) err := ctx.Encoder.Encode(buf, thumbnail) if err != nil { return nil, err } - return buf.Bytes(), nil + bytes := buf.Bytes() + s.Storage.Set(key, bytes) + return bytes, nil } // GetStored tries to get the stored thumbnail and return it. -// If there is no stored thumbnail it will return nil +// If there is no cached thumbnail it will return nil func (s SimpleManager) GetStored(ctx ThumbnailContext) []byte { - key := buildKey(ctx) + key := s.Storage.BuildKey(mapToStorageContext(ctx)) stored := s.Storage.Get(key) if stored == nil { return nil } - buf := new(bytes.Buffer) - ctx.Encoder.Encode(buf, stored) - return buf.Bytes() + return stored } func (s SimpleManager) generate(ctx ThumbnailContext, img image.Image) image.Image { @@ -66,11 +65,12 @@ func (s SimpleManager) generate(ctx ThumbnailContext, img image.Image) image.Ima return thumbnail } -func buildKey(ctx ThumbnailContext) string { - parts := []string{ - ctx.ImagePath, - string(ctx.Width) + "x" + string(ctx.Height), - strings.Join(ctx.Encoder.Types(), ","), +func mapToStorageContext(ctx ThumbnailContext) storage.StorageContext { + sCtx := storage.StorageContext{ + ETag: ctx.ETag, + Width: ctx.Width, + Height: ctx.Height, + Types: ctx.Encoder.Types(), } - return strings.Join(parts, "+") + return sCtx } From 1274efba21bddcff10ca6cb0044293cdac0e144e Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Mon, 9 Mar 2020 11:30:56 +0100 Subject: [PATCH 07/80] Fix Formatting --- .github/config.yml | 1 + CHANGELOG.md | 15 ++++++++++ Makefile | 6 ++-- changelog/CHANGELOG.tmpl | 8 ++--- changelog/unreleased/.keep | 0 changelog/unreleased/initial-version | 7 +++++ go.mod | 2 ++ go.sum | 45 ++++++++++++++++++++++++++++ tools.go | 8 +++++ 9 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 changelog/unreleased/.keep create mode 100644 changelog/unreleased/initial-version create mode 100644 tools.go diff --git a/.github/config.yml b/.github/config.yml index c723ec7fe..229466c7c 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -3,6 +3,7 @@ # Comment to be posted to on PRs that don't update documentation updateDocsComment: > Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a [changelog](https://github.com/owncloud/ocis-thumbnails/blob/master/changelog/README.md) item based on your changes. + updateDocsWhiteList: - Tests-only - tests-only diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..1f8ea9e43 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changes in unreleased + +## Summary + +* Change - Initial release of basic version: [#1](https://github.com/owncloud/ocis-thumbnails/issues/1) + +## Details + +* Change - Initial release of basic version: [#1](https://github.com/owncloud/ocis-thumbnails/issues/1) + + Just prepare an initial basic version to embed a thumbnailer into our microservice + infrastructure in the scope of the ownCloud Infinite Scale project. + + https://github.com/owncloud/ocis-thumbnails/issues/1 + diff --git a/Makefile b/Makefile index 8e45e1524..bbfafe3cf 100644 --- a/Makefile +++ b/Makefile @@ -110,15 +110,15 @@ release-dirs: .PHONY: release-linux release-linux: release-dirs - go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'linux' -arch 'amd64 386 arm64 arm' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)--' ./cmd/$(NAME) + go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'linux' -arch 'amd64 386 arm64 arm' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME) .PHONY: release-windows release-windows: release-dirs - go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'windows' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)--' ./cmd/$(NAME) + go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'windows' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME) .PHONY: release-darwin release-darwin: release-dirs - go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -os 'darwin' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)--' ./cmd/$(NAME) + go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -os 'darwin' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME) .PHONY: release-copy release-copy: diff --git a/changelog/CHANGELOG.tmpl b/changelog/CHANGELOG.tmpl index 886288f44..17f148fb9 100644 --- a/changelog/CHANGELOG.tmpl +++ b/changelog/CHANGELOG.tmpl @@ -3,7 +3,7 @@ {{ if gt (len $allVersions) 1 -}} # Changelog for [{{ .Version }}] ({{ .Date }}) -The following sections list the changes in ownCloudocis-thumbnails{{ .Version }}. +The following sections list the changes in ownCloudocis-thumbnails {{ .Version }}. {{/* creating version compare links */ -}} {{ $next := add1 $index -}} @@ -30,12 +30,12 @@ The following sections list the changes in ownCloudocis-thumbnails{{ .Version }} ## Summary {{ range $entry := .Entries }}{{ with $entry }} - * {{ .TypeShort }} #{{ .PrimaryID }}: {{ .Title }} +* {{ .Type }} - {{ .Title }}: [#{{ .PrimaryID }}]({{ .PrimaryURL }}) {{- end }}{{ end }} ## Details {{ range $entry := .Entries }}{{ with $entry }} - * {{ .Type }} #{{ .PrimaryID }}: {{ .Title }} +* {{ .Type }} - {{ .Title }}: [#{{ .PrimaryID }}]({{ .PrimaryURL }}) {{ range $par := .Paragraphs }} {{ wrapIndent $par 80 3 }} {{ end -}} @@ -50,4 +50,4 @@ The following sections list the changes in ownCloudocis-thumbnails{{ .Version }} {{ end }} {{ end }}{{ end -}} -{{ end }}{{ end -}} \ No newline at end of file +{{ end }}{{ end -}} diff --git a/changelog/unreleased/.keep b/changelog/unreleased/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/changelog/unreleased/initial-version b/changelog/unreleased/initial-version new file mode 100644 index 000000000..eeb21e0af --- /dev/null +++ b/changelog/unreleased/initial-version @@ -0,0 +1,7 @@ +Change: Initial release of basic version + +Just prepare an initial basic version to embed a thumbnailer into our +microservice infrastructure in the scope of the ownCloud Infinite Scale +project. + +https://github.com/owncloud/ocis-thumbnails/issues/1 diff --git a/go.mod b/go.mod index 7069238a2..3286cbb5f 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( contrib.go.opencensus.io/exporter/jaeger v0.2.0 contrib.go.opencensus.io/exporter/ocagent v0.6.0 contrib.go.opencensus.io/exporter/zipkin v0.1.1 + github.com/UnnoTed/fileb0x v1.1.4 // indirect github.com/cespare/reflex v0.2.0 // indirect github.com/go-chi/chi v4.0.2+incompatible github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect @@ -14,6 +15,7 @@ require ( github.com/oklog/run v1.0.0 github.com/openzipkin/zipkin-go v0.2.2 github.com/owncloud/ocis-pkg/v2 v2.0.1 + github.com/restic/calens v0.2.0 // indirect github.com/spf13/viper v1.5.0 go.opencensus.io v0.22.2 ) diff --git a/go.sum b/go.sum index bde3bff73..2d1d75231 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190605020000-c4ba1fdf4d36/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo= +github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.0.2 h1:tRi7ENs+AaOUCH+j6qwNQgPYfV26dX3JNonq+V4mhqc= +github.com/Masterminds/semver/v3 v3.0.2/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.0.1 h1:RuaOafp+8qOLUPX1lInLfUrLc1MEVbnz7a40RLoixKY= +github.com/Masterminds/sprig/v3 v3.0.1/go.mod h1:Cp7HwZjmqKrC+Y7XqSJOU2yRvAJRGLiohfgz5ZJj8+4= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= @@ -64,6 +70,8 @@ github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.24.1/go.mod h1:fGP8eQ6PugKEI0iUETYYtnP6d1pH/bdDMTel1X5ajsU= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/UnnoTed/fileb0x v1.1.4 h1:IUgFzgBipF/ujNx9wZgkrKOF3oltUuXMSoaejrBws+A= +github.com/UnnoTed/fileb0x v1.1.4/go.mod h1:X59xXT18tdNk/D6j+KZySratBsuKJauMtVuJ9cgOiZs= github.com/abbot/go-http-auth v0.4.1-0.20181019201920-860ed7f246ff/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= @@ -108,6 +116,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= @@ -168,6 +178,7 @@ github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/decker502/dnspod-go v0.2.0/go.mod h1:qsurYu1FgxcDwfSwXJdLt4kRsBLZeosEb9uq4Sy+08g= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= @@ -212,6 +223,8 @@ github.com/fsouza/go-dockerclient v1.4.4/go.mod h1:PrwszSL5fbmsESocROrOGq/NULMXR github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc= +github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= @@ -245,6 +258,7 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stomp/stomp v2.0.3+incompatible/go.mod h1:VqCtqNZv1226A1/79yh+rMiFUcfY3R109np+7ke4n0c= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= +github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg= github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -365,10 +379,13 @@ github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5 h1:FdBGmSkD2Qp github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5/go.mod h1:0YZ2wQSuwviXXXGUiK6zXzskyBLAbLXhamxzcFHSLoM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= +github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84= +github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= @@ -393,6 +410,8 @@ github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSg github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= +github.com/karrick/godirwalk v1.7.8 h1:VfG72pyIxgtC7+3X9CMHI0AOl4LwyRAg98WAgsvffi8= +github.com/karrick/godirwalk v1.7.8/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= 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/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -412,6 +431,10 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= +github.com/labstack/echo v3.2.1+incompatible h1:J2M7YArHx4gi8p/3fDw8tX19SXhBCoRpviyAZSN3I88= +github.com/labstack/echo v3.2.1+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.2.7 h1:2qOPq/twXDrQ6ooBGrn3mrmVOC+biLlatwgIu8lbzRM= +github.com/labstack/gommon v0.2.7/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -435,11 +458,16 @@ github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGD github.com/marten-seemann/qtls v0.3.2/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/marten-seemann/qtls v0.4.1 h1:YlT8QP3WCCvvok7MGEZkMldXbyqgr8oFg5/n8Gtbkks= github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -473,12 +501,15 @@ github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= @@ -489,6 +520,7 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -528,6 +560,8 @@ github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/Hzq github.com/nrdcg/dnspod-go v0.3.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ= github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= +github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840= +github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/nsqio/go-nsq v1.0.7/go.mod h1:XP5zaUs3pqf+Q71EqUJs3HYfBIqfK6G83WQMdNN+Ito= github.com/ogier/pflag v0.0.1 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750= github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g= @@ -584,6 +618,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= @@ -620,6 +655,8 @@ github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKc github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/restic/calens v0.1.0 h1:RHGokdZ72dICyIz1EjEsfZwUhvNZz/zy2SawxJktdWA= github.com/restic/calens v0.1.0/go.mod h1:u67f5msOjCTDYNzOf/NoAUSdmXP03YXPCwIQLYADy5M= +github.com/restic/calens v0.2.0 h1:LVNAtmFc+Pb4ODX66qdX1T3Di1P0OTLyUsVyvM/xD7E= +github.com/restic/calens v0.2.0/go.mod h1:UXwyAKS4wsgUZGEc7NrzzygJbLsQZIo3wl+62Q1wvmU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= @@ -675,6 +712,7 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= @@ -695,6 +733,10 @@ github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMW github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8= +github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw= github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= @@ -737,6 +779,7 @@ gocloud.dev v0.17.0/go.mod h1:tIHTRdR1V5dlD8sTkzYdTGizBJ314BDykJ8KmadEXwo= gocloud.dev/pubsub/rabbitpubsub v0.17.0/go.mod h1:7o1XYDiIC+b0mmcwJuofsDg08t0DtU2ubfn7C/Uz7Y0= golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -785,6 +828,7 @@ golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180921000356-2f5d2388922f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -834,6 +878,7 @@ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181019160139-8e24a49d80f8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/tools.go b/tools.go new file mode 100644 index 000000000..8a77af67c --- /dev/null +++ b/tools.go @@ -0,0 +1,8 @@ +// +build tools + +package main + +import ( + _ "github.com/UnnoTed/fileb0x" + _ "github.com/restic/calens" +) From 2460db25109c07992cb8aaa7d8d05a9c7cac3248 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Mon, 9 Mar 2020 12:04:02 +0100 Subject: [PATCH 08/80] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ae2d1e635..7d6963a34 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # ownCloud Infinite Scale: THUMBNAILS -== Badges need to be provided manually == +[![Build Status](https://cloud.drone.io/api/badges/owncloud/ocis-thumbnails/status.svg)](https://cloud.drone.io/owncloud/ocis-thumbnails) +[![Gitter chat](https://badges.gitter.im/cs3org/reva.svg)](https://gitter.im/cs3org/reva) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/5dcc0c9b2319462dbbe517d90219062c)](https://www.codacy.com/gh/owncloud/ocis-thumbnails?utm_source=github.com&utm_medium=referral&utm_content=owncloud/ocis-thumbnails&utm_campaign=Badge_Grade) +[![Go Doc](https://godoc.org/github.com/owncloud/ocis-thumbnails?status.svg)](http://godoc.org/github.com/owncloud/ocis-thumbnails) +[![Go Report](http://goreportcard.com/badge/github.com/owncloud/ocis-thumbnails)](http://goreportcard.com/report/github.com/owncloud/ocis-thumbnails) +[![](https://images.microbadger.com/badges/image/owncloud/ocis-thumbnails.svg)](http://microbadger.com/images/owncloud/ocis-thumbnails "Get your own image badge on microbadger.com") **This project is under heavy development, it's not in a working state yet!** From d60177a094ee3c745f00006d6df1210478307dcf Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Mon, 9 Mar 2020 12:05:31 +0100 Subject: [PATCH 09/80] Update manifest.tmpl --- docker/manifest.tmpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/manifest.tmpl b/docker/manifest.tmpl index 254456d9b..cddd65a91 100644 --- a/docker/manifest.tmpl +++ b/docker/manifest.tmpl @@ -1,4 +1,4 @@ -image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} +image: owncloud/ocis-thumbnails:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} {{#if build.tags}} tags: {{#each build.tags}} @@ -6,16 +6,16 @@ tags: {{/each}} {{/if}} manifests: - - image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 + - image: owncloud/ocis-thumbnails:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 platform: architecture: amd64 os: linux - - image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 + - image: owncloud/ocis-thumbnails:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 platform: architecture: arm64 variant: v8 os: linux - - image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm + - image: owncloud/ocis-thumbnails:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm platform: architecture: arm variant: v6 From 30746ab36a8d8bc4d919f3dc44b75c59bc8a2e68 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Mon, 9 Mar 2020 12:42:56 +0100 Subject: [PATCH 10/80] Typo in CHANGELOG.tmpl --- changelog/CHANGELOG.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/CHANGELOG.tmpl b/changelog/CHANGELOG.tmpl index 17f148fb9..caa3daa89 100644 --- a/changelog/CHANGELOG.tmpl +++ b/changelog/CHANGELOG.tmpl @@ -3,7 +3,7 @@ {{ if gt (len $allVersions) 1 -}} # Changelog for [{{ .Version }}] ({{ .Date }}) -The following sections list the changes in ownCloudocis-thumbnails {{ .Version }}. +The following sections list the changes in ocis-thumbnails {{ .Version }}. {{/* creating version compare links */ -}} {{ $next := add1 $index -}} From 975387822b537f2be4bdf95f4dbe70f78b5fcf41 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 9 Mar 2020 16:01:20 +0100 Subject: [PATCH 11/80] add config for filesystem storage --- pkg/config/config.go | 16 +++++++++++----- pkg/flagset/flagset.go | 7 +++++++ pkg/thumbnails/imgsource/webdav.go | 4 ++-- pkg/thumbnails/storage/filesystem.go | 12 +++++++----- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index c92c9820c..13b7a8ea3 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -33,11 +33,17 @@ type Tracing struct { // Config combines all available configuration parts. type Config struct { - File string - Log Log - Debug Debug - HTTP HTTP - Tracing Tracing + File string + Log Log + Debug Debug + HTTP HTTP + Tracing Tracing + FilesystemStorage FilesystemStorage +} + +// FilesystemStorage defines the available filesystem storage configuration. +type FilesystemStorage struct { + RootDirectory string } // New initializes a new configuration with or without defaults. diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 39d4005b7..b9f4f30b1 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -134,5 +134,12 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { EnvVars: []string{"THUMBNAILS_HTTP_ROOT"}, Destination: &cfg.HTTP.Root, }, + &cli.StringFlag{ + Name: "filesystemstorage-root", + Value: "/tmp/ocis-thumbnails/", + Usage: "Root path of the filesystem storage directory", + EnvVars: []string{"THUMBNAILS_FILESYSTEMSTORAGE_ROOT"}, + Destination: &cfg.FilesystemStorage.RootDirectory, + }, } } diff --git a/pkg/thumbnails/imgsource/webdav.go b/pkg/thumbnails/imgsource/webdav.go index 5637d943d..b3c1ba8a2 100644 --- a/pkg/thumbnails/imgsource/webdav.go +++ b/pkg/thumbnails/imgsource/webdav.go @@ -24,7 +24,7 @@ func (s WebDav) Get(file string, ctx SourceContext) (image.Image, error) { u.Path = path.Join(u.Path, file) req, err := http.NewRequest(http.MethodGet, u.String(), nil) if err != nil { - return nil, fmt.Errorf("could not get the file %s error: %s", file, err.Error()) + return nil, fmt.Errorf("could not get the file \"%s\" error: %s", file, err.Error()) } auth := ctx.GetString(WebDavAuth) @@ -33,7 +33,7 @@ func (s WebDav) Get(file string, ctx SourceContext) (image.Image, error) { client := &http.Client{} resp, err := client.Do(req) if err != nil { - return nil, fmt.Errorf("could not get the file %s error: %s", file, err.Error()) + return nil, fmt.Errorf("could not get the file \"%s\" error: %s", file, err.Error()) } img, _, _ := image.Decode(resp.Body) diff --git a/pkg/thumbnails/storage/filesystem.go b/pkg/thumbnails/storage/filesystem.go index 0466a1f3f..e306a1149 100644 --- a/pkg/thumbnails/storage/filesystem.go +++ b/pkg/thumbnails/storage/filesystem.go @@ -7,15 +7,16 @@ import ( "os" "path/filepath" "strconv" + + "github.com/owncloud/ocis-thumbnails/pkg/config" ) -const BasePath = "/home/corby/tmp/thumbnails/fs/" - type FileSystem struct { + cfg config.FilesystemStorage } func (s FileSystem) Get(key string) []byte { - content, err := ioutil.ReadFile(BasePath + key) + content, err := ioutil.ReadFile(filepath.Join(s.cfg.RootDirectory, key)) if err != nil { return nil } @@ -24,12 +25,13 @@ func (s FileSystem) Get(key string) []byte { } func (s FileSystem) Set(key string, img []byte) error { - folder := filepath.Dir(BasePath + key) + path := filepath.Join(s.cfg.RootDirectory, key) + folder := filepath.Dir(path) if err := createFolderIfNotExists(folder); err != nil { return fmt.Errorf("error while creating folder %s", folder) } - f, err := os.Create(BasePath + key) + f, err := os.Create(path) if err != nil { fmt.Println(err.Error()) return err From 4647168160876160f8f307abe4c1e6dd204aaf2c Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 9 Mar 2020 17:03:33 +0100 Subject: [PATCH 12/80] clean up and add comments --- pkg/config/config.go | 6 ++-- pkg/service/v0/service.go | 4 +-- pkg/thumbnails/encoding.go | 11 +++++++ pkg/thumbnails/storage/filesystem.go | 44 ++++++++++++++++------------ pkg/thumbnails/storage/storage.go | 5 ++-- pkg/thumbnails/thumbnails.go | 27 +++++++---------- 6 files changed, 56 insertions(+), 41 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 13b7a8ea3..3d66ec935 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -38,11 +38,11 @@ type Config struct { Debug Debug HTTP HTTP Tracing Tracing - FilesystemStorage FilesystemStorage + FileSystemStorage FileSystemStorage } -// FilesystemStorage defines the available filesystem storage configuration. -type FilesystemStorage struct { +// FileSystemStorage defines the available filesystem storage configuration. +type FileSystemStorage struct { RootDirectory string } diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 88e4664fd..8c88cd612 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -28,7 +28,7 @@ func NewService(opts ...Option) Service { config: options.Config, mux: m, manager: thumbnails.SimpleManager{ - Storage: storage.NewInMemoryStorage(), + Storage: storage.NewFileSystemStorage(options.Config.FileSystemStorage), }, source: imgsource.WebDav{ Basepath: "http://localhost:9140/remote.php/webdav/", @@ -71,7 +71,7 @@ func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) { w.Write([]byte("can't encode that")) return } - ctx := thumbnails.ThumbnailContext{ + ctx := thumbnails.Context{ Width: width, Height: height, ImagePath: filePath, diff --git a/pkg/thumbnails/encoding.go b/pkg/thumbnails/encoding.go index 60a02f59b..8deb12871 100644 --- a/pkg/thumbnails/encoding.go +++ b/pkg/thumbnails/encoding.go @@ -8,31 +8,42 @@ import ( "strings" ) +// Encoder encodes the thumbnail to a specific format. type Encoder interface { + // Encode encodes the image to a format. Encode(io.Writer, image.Image) error + // Types returns the formats suffixes. Types() []string } +// PngEncoder encodes to png type PngEncoder struct{} +// Encode encodes to png format func (e PngEncoder) Encode(w io.Writer, i image.Image) error { return png.Encode(w, i) } +// Types returns the png suffix func (e PngEncoder) Types() []string { return []string{"png"} } +// JpegEncoder encodes to jpg. type JpegEncoder struct{} +// Encode encodes to jpg func (e JpegEncoder) Encode(w io.Writer, i image.Image) error { return jpeg.Encode(w, i, nil) } +// Types returns the jpg suffixes. func (e JpegEncoder) Types() []string { return []string{"jpeg", "jpg"} } +// EncoderForType returns the encoder for a given file type +// or nil if the type is not supported. func EncoderForType(fileType string) Encoder { switch strings.ToLower(fileType) { case "png": diff --git a/pkg/thumbnails/storage/filesystem.go b/pkg/thumbnails/storage/filesystem.go index e306a1149..e92e91bdf 100644 --- a/pkg/thumbnails/storage/filesystem.go +++ b/pkg/thumbnails/storage/filesystem.go @@ -11,12 +11,21 @@ import ( "github.com/owncloud/ocis-thumbnails/pkg/config" ) -type FileSystem struct { - cfg config.FilesystemStorage +// NewFileSystemStorage creates a new instanz of FileSystem +func NewFileSystemStorage(cfg config.FileSystemStorage) FileSystem { + return FileSystem{ + dir: cfg.RootDirectory, + } } +// FileSystem represents a storage for the thumbnails using the local file system. +type FileSystem struct { + dir string +} + +// Get loads the image from the file system. func (s FileSystem) Get(key string) []byte { - content, err := ioutil.ReadFile(filepath.Join(s.cfg.RootDirectory, key)) + content, err := ioutil.ReadFile(filepath.Join(s.dir, key)) if err != nil { return nil } @@ -24,11 +33,12 @@ func (s FileSystem) Get(key string) []byte { return content } +// Set writes the image to the file system. func (s FileSystem) Set(key string, img []byte) error { - path := filepath.Join(s.cfg.RootDirectory, key) - folder := filepath.Dir(path) - if err := createFolderIfNotExists(folder); err != nil { - return fmt.Errorf("error while creating folder %s", folder) + path := filepath.Join(s.dir, key) + dir := filepath.Dir(path) + if err := os.MkdirAll(dir, 0700); err != nil { + return fmt.Errorf("error while creating directory %s", dir) } f, err := os.Create(path) @@ -44,7 +54,15 @@ func (s FileSystem) Set(key string, img []byte) error { return nil } -func (s FileSystem) BuildKey(ctx StorageContext) string { +// BuildKey generate the unique key for a thumbnail. +// The key is structure as follows: +// +// ///x. +// +// e.g. 97/9f/4c8db98f7b82e768ef478d3c8612/500x300.png +// +// The key also represents the path to the thumbnail in the filesystem under the configured root directory. +func (s FileSystem) BuildKey(ctx Context) string { etag := ctx.ETag filetype := ctx.Types[0] filename := strconv.Itoa(ctx.Width) + "x" + strconv.Itoa(ctx.Height) + "." + filetype @@ -60,13 +78,3 @@ func (s FileSystem) BuildKey(ctx StorageContext) string { return key.String() } - -func createFolderIfNotExists(folder string) error { - if _, err := os.Stat(folder); os.IsNotExist(err) { - err := os.MkdirAll(folder, 0700) - if err != nil { - return err - } - } - return nil -} diff --git a/pkg/thumbnails/storage/storage.go b/pkg/thumbnails/storage/storage.go index 4582433f2..8f5f3d9de 100644 --- a/pkg/thumbnails/storage/storage.go +++ b/pkg/thumbnails/storage/storage.go @@ -1,6 +1,7 @@ package storage -type StorageContext struct { +// Context combines different attributes needed for storage operations. +type Context struct { ETag string Types []string Width int @@ -11,5 +12,5 @@ type StorageContext struct { type Storage interface { Get(string) []byte Set(string, []byte) error - BuildKey(StorageContext) string + BuildKey(Context) string } diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go index 10235f562..7172bacd4 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnails/thumbnails.go @@ -3,14 +3,13 @@ package thumbnails import ( "bytes" "image" - "time" "github.com/nfnt/resize" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" ) -// ThumbnailContext bundles information needed to generate a thumbnail for afile -type ThumbnailContext struct { +// Context bundles information needed to generate a thumbnail for afile +type Context struct { Width int Height int ImagePath string @@ -21,8 +20,10 @@ type ThumbnailContext struct { // Manager is responsible for generating thumbnails type Manager interface { // Get will return a thumbnail for a file - Get(ThumbnailContext, image.Image) ([]byte, error) - GetStored(ThumbnailContext) []byte + Get(Context, image.Image) ([]byte, error) + // GetStored loads the thumbnail from the storage. + // It will return nil if no image is stored for the given context. + GetStored(Context) []byte } // SimpleManager is a simple implementation of Manager @@ -31,7 +32,7 @@ type SimpleManager struct { } // Get implements the Get Method of Manager -func (s SimpleManager) Get(ctx ThumbnailContext, img image.Image) ([]byte, error) { +func (s SimpleManager) Get(ctx Context, img image.Image) ([]byte, error) { thumbnail := s.generate(ctx, img) key := s.Storage.BuildKey(mapToStorageContext(ctx)) @@ -48,25 +49,19 @@ func (s SimpleManager) Get(ctx ThumbnailContext, img image.Image) ([]byte, error // GetStored tries to get the stored thumbnail and return it. // If there is no cached thumbnail it will return nil -func (s SimpleManager) GetStored(ctx ThumbnailContext) []byte { +func (s SimpleManager) GetStored(ctx Context) []byte { key := s.Storage.BuildKey(mapToStorageContext(ctx)) stored := s.Storage.Get(key) - if stored == nil { - return nil - } return stored } -func (s SimpleManager) generate(ctx ThumbnailContext, img image.Image) image.Image { - // TODO: remove, just for demo purposes - time.Sleep(time.Second * 2) - +func (s SimpleManager) generate(ctx Context, img image.Image) image.Image { thumbnail := resize.Thumbnail(uint(ctx.Width), uint(ctx.Height), img, resize.Lanczos2) return thumbnail } -func mapToStorageContext(ctx ThumbnailContext) storage.StorageContext { - sCtx := storage.StorageContext{ +func mapToStorageContext(ctx Context) storage.Context { + sCtx := storage.Context{ ETag: ctx.ETag, Width: ctx.Width, Height: ctx.Height, From d8c359332b8d31f1a2acc10f9c7a4e2a86f8a312 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 10 Mar 2020 10:57:30 +0100 Subject: [PATCH 13/80] rename Context --- pkg/thumbnails/storage/inmemory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/thumbnails/storage/inmemory.go b/pkg/thumbnails/storage/inmemory.go index d66d0712e..aa9e84aec 100644 --- a/pkg/thumbnails/storage/inmemory.go +++ b/pkg/thumbnails/storage/inmemory.go @@ -23,7 +23,7 @@ func (s InMemory) Set(key string, thumbnail []byte) error { return nil } -func (s InMemory) BuildKey(ctx StorageContext) string { +func (s InMemory) BuildKey(ctx Context) string { parts := []string{ ctx.ETag, string(ctx.Width) + "x" + string(ctx.Height), From 2a587b37c85691dba88bd816ea752069410c8c21 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 10 Mar 2020 14:47:42 +0100 Subject: [PATCH 14/80] clean up and add comments --- pkg/config/config.go | 6 ++++++ pkg/flagset/flagset.go | 9 ++++++++- pkg/service/v0/service.go | 4 +--- pkg/thumbnails/imgsource/webdav.go | 26 +++++++++++++++++++++----- pkg/thumbnails/storage/inmemory.go | 6 ++++++ 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 3d66ec935..a5bc260e0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -39,6 +39,7 @@ type Config struct { HTTP HTTP Tracing Tracing FileSystemStorage FileSystemStorage + WebDavSource WebDavSource } // FileSystemStorage defines the available filesystem storage configuration. @@ -46,6 +47,11 @@ type FileSystemStorage struct { RootDirectory string } +// WebDavSource defines the available webdav source configuration. +type WebDavSource struct { + BaseURL string +} + // New initializes a new configuration with or without defaults. func New() *Config { return &Config{} diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index b9f4f30b1..75af98d5b 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -139,7 +139,14 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { Value: "/tmp/ocis-thumbnails/", Usage: "Root path of the filesystem storage directory", EnvVars: []string{"THUMBNAILS_FILESYSTEMSTORAGE_ROOT"}, - Destination: &cfg.FilesystemStorage.RootDirectory, + Destination: &cfg.FileSystemStorage.RootDirectory, + }, + &cli.StringFlag{ + Name: "webdavsource-baseurl", + Value: "http://localhost:9140/remote.php/webdav/", + Usage: "Base url for a webdav api", + EnvVars: []string{"THUMBNAILS_WEBDAVSOURCE_BASEURL"}, + Destination: &cfg.WebDavSource.BaseURL, }, } } diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 8c88cd612..8740f5acd 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -30,9 +30,7 @@ func NewService(opts ...Option) Service { manager: thumbnails.SimpleManager{ Storage: storage.NewFileSystemStorage(options.Config.FileSystemStorage), }, - source: imgsource.WebDav{ - Basepath: "http://localhost:9140/remote.php/webdav/", - }, + source: imgsource.NewWebDavSource(options.Config.WebDavSource), } m.Route(options.Config.HTTP.Root, func(r chi.Router) { diff --git a/pkg/thumbnails/imgsource/webdav.go b/pkg/thumbnails/imgsource/webdav.go index b3c1ba8a2..a6d8fdb50 100644 --- a/pkg/thumbnails/imgsource/webdav.go +++ b/pkg/thumbnails/imgsource/webdav.go @@ -6,11 +6,20 @@ import ( "net/http" "net/url" "path" + + "github.com/owncloud/ocis-thumbnails/pkg/config" ) +// NewWebDavSource creates a new webdav instance. +func NewWebDavSource(cfg config.WebDavSource) WebDav { + return WebDav{ + baseURL: cfg.BaseURL, + } +} + // WebDav implements the Source interface for webdav services type WebDav struct { - Basepath string + baseURL string } const ( @@ -20,11 +29,11 @@ const ( // Get downloads the file from a webdav service func (s WebDav) Get(file string, ctx SourceContext) (image.Image, error) { - u, _ := url.Parse(s.Basepath) + u, _ := url.Parse(s.baseURL) u.Path = path.Join(u.Path, file) req, err := http.NewRequest(http.MethodGet, u.String(), nil) if err != nil { - return nil, fmt.Errorf("could not get the file \"%s\" error: %s", file, err.Error()) + return nil, fmt.Errorf("could not get the image \"%s\" error: %s", file, err.Error()) } auth := ctx.GetString(WebDavAuth) @@ -33,9 +42,16 @@ func (s WebDav) Get(file string, ctx SourceContext) (image.Image, error) { client := &http.Client{} resp, err := client.Do(req) if err != nil { - return nil, fmt.Errorf("could not get the file \"%s\" error: %s", file, err.Error()) + return nil, fmt.Errorf("could not get the image \"%s\" error: %s", file, err.Error()) } - img, _, _ := image.Decode(resp.Body) + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("could not get the image \"%s\". Request returned with statuscode %d ", file, resp.StatusCode) + } + + img, _, err := image.Decode(resp.Body) + if err != nil { + return nil, fmt.Errorf("could not decode the image \"%s\". error: %s", file, err.Error()) + } return img, nil } diff --git a/pkg/thumbnails/storage/inmemory.go b/pkg/thumbnails/storage/inmemory.go index aa9e84aec..c8f02a9ae 100644 --- a/pkg/thumbnails/storage/inmemory.go +++ b/pkg/thumbnails/storage/inmemory.go @@ -4,25 +4,31 @@ import ( "strings" ) +// NewInMemoryStorage creates a new InMemory instance. func NewInMemoryStorage() InMemory { return InMemory{ store: make(map[string][]byte), } } +// InMemory represents an in memory storage for thumbnails +// Can be used during development type InMemory struct { store map[string][]byte } +// Get loads the thumbnail from memory. func (s InMemory) Get(key string) []byte { return s.store[key] } +// Set stores the thumbnail in memory. func (s InMemory) Set(key string, thumbnail []byte) error { s.store[key] = thumbnail return nil } +// BuildKey generates a unique key to store and retrieve the thumbnail. func (s InMemory) BuildKey(ctx Context) string { parts := []string{ ctx.ETag, From e0354f2ce9fceaae1d066e72994289c9eac9307b Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Mon, 9 Mar 2020 11:30:56 +0100 Subject: [PATCH 15/80] Fix Formatting --- .github/config.yml | 1 + CHANGELOG.md | 15 ++++++++++ Makefile | 6 ++-- changelog/CHANGELOG.tmpl | 8 ++--- changelog/unreleased/.keep | 0 changelog/unreleased/initial-version | 7 +++++ go.mod | 2 ++ go.sum | 45 ++++++++++++++++++++++++++++ tools.go | 8 +++++ 9 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 changelog/unreleased/.keep create mode 100644 changelog/unreleased/initial-version create mode 100644 tools.go diff --git a/.github/config.yml b/.github/config.yml index c723ec7fe..229466c7c 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -3,6 +3,7 @@ # Comment to be posted to on PRs that don't update documentation updateDocsComment: > Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a [changelog](https://github.com/owncloud/ocis-thumbnails/blob/master/changelog/README.md) item based on your changes. + updateDocsWhiteList: - Tests-only - tests-only diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..1f8ea9e43 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changes in unreleased + +## Summary + +* Change - Initial release of basic version: [#1](https://github.com/owncloud/ocis-thumbnails/issues/1) + +## Details + +* Change - Initial release of basic version: [#1](https://github.com/owncloud/ocis-thumbnails/issues/1) + + Just prepare an initial basic version to embed a thumbnailer into our microservice + infrastructure in the scope of the ownCloud Infinite Scale project. + + https://github.com/owncloud/ocis-thumbnails/issues/1 + diff --git a/Makefile b/Makefile index 8e45e1524..bbfafe3cf 100644 --- a/Makefile +++ b/Makefile @@ -110,15 +110,15 @@ release-dirs: .PHONY: release-linux release-linux: release-dirs - go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'linux' -arch 'amd64 386 arm64 arm' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)--' ./cmd/$(NAME) + go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'linux' -arch 'amd64 386 arm64 arm' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME) .PHONY: release-windows release-windows: release-dirs - go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'windows' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)--' ./cmd/$(NAME) + go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'windows' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME) .PHONY: release-darwin release-darwin: release-dirs - go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -os 'darwin' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)--' ./cmd/$(NAME) + go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -os 'darwin' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME) .PHONY: release-copy release-copy: diff --git a/changelog/CHANGELOG.tmpl b/changelog/CHANGELOG.tmpl index 886288f44..17f148fb9 100644 --- a/changelog/CHANGELOG.tmpl +++ b/changelog/CHANGELOG.tmpl @@ -3,7 +3,7 @@ {{ if gt (len $allVersions) 1 -}} # Changelog for [{{ .Version }}] ({{ .Date }}) -The following sections list the changes in ownCloudocis-thumbnails{{ .Version }}. +The following sections list the changes in ownCloudocis-thumbnails {{ .Version }}. {{/* creating version compare links */ -}} {{ $next := add1 $index -}} @@ -30,12 +30,12 @@ The following sections list the changes in ownCloudocis-thumbnails{{ .Version }} ## Summary {{ range $entry := .Entries }}{{ with $entry }} - * {{ .TypeShort }} #{{ .PrimaryID }}: {{ .Title }} +* {{ .Type }} - {{ .Title }}: [#{{ .PrimaryID }}]({{ .PrimaryURL }}) {{- end }}{{ end }} ## Details {{ range $entry := .Entries }}{{ with $entry }} - * {{ .Type }} #{{ .PrimaryID }}: {{ .Title }} +* {{ .Type }} - {{ .Title }}: [#{{ .PrimaryID }}]({{ .PrimaryURL }}) {{ range $par := .Paragraphs }} {{ wrapIndent $par 80 3 }} {{ end -}} @@ -50,4 +50,4 @@ The following sections list the changes in ownCloudocis-thumbnails{{ .Version }} {{ end }} {{ end }}{{ end -}} -{{ end }}{{ end -}} \ No newline at end of file +{{ end }}{{ end -}} diff --git a/changelog/unreleased/.keep b/changelog/unreleased/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/changelog/unreleased/initial-version b/changelog/unreleased/initial-version new file mode 100644 index 000000000..eeb21e0af --- /dev/null +++ b/changelog/unreleased/initial-version @@ -0,0 +1,7 @@ +Change: Initial release of basic version + +Just prepare an initial basic version to embed a thumbnailer into our +microservice infrastructure in the scope of the ownCloud Infinite Scale +project. + +https://github.com/owncloud/ocis-thumbnails/issues/1 diff --git a/go.mod b/go.mod index 0cde5ba97..3972a7db8 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( contrib.go.opencensus.io/exporter/jaeger v0.2.0 contrib.go.opencensus.io/exporter/ocagent v0.6.0 contrib.go.opencensus.io/exporter/zipkin v0.1.1 + github.com/UnnoTed/fileb0x v1.1.4 // indirect github.com/cespare/reflex v0.2.0 // indirect github.com/go-chi/chi v4.0.2+incompatible github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect @@ -15,6 +16,7 @@ require ( github.com/oklog/run v1.0.0 github.com/openzipkin/zipkin-go v0.2.2 github.com/owncloud/ocis-pkg/v2 v2.0.1 + github.com/restic/calens v0.2.0 // indirect github.com/spf13/viper v1.5.0 go.opencensus.io v0.22.2 ) diff --git a/go.sum b/go.sum index 0767e8baf..4dc73902a 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190605020000-c4ba1fdf4d36/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo= +github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.0.2 h1:tRi7ENs+AaOUCH+j6qwNQgPYfV26dX3JNonq+V4mhqc= +github.com/Masterminds/semver/v3 v3.0.2/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.0.1 h1:RuaOafp+8qOLUPX1lInLfUrLc1MEVbnz7a40RLoixKY= +github.com/Masterminds/sprig/v3 v3.0.1/go.mod h1:Cp7HwZjmqKrC+Y7XqSJOU2yRvAJRGLiohfgz5ZJj8+4= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= @@ -64,6 +70,8 @@ github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.24.1/go.mod h1:fGP8eQ6PugKEI0iUETYYtnP6d1pH/bdDMTel1X5ajsU= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/UnnoTed/fileb0x v1.1.4 h1:IUgFzgBipF/ujNx9wZgkrKOF3oltUuXMSoaejrBws+A= +github.com/UnnoTed/fileb0x v1.1.4/go.mod h1:X59xXT18tdNk/D6j+KZySratBsuKJauMtVuJ9cgOiZs= github.com/abbot/go-http-auth v0.4.1-0.20181019201920-860ed7f246ff/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= @@ -108,6 +116,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= @@ -168,6 +178,7 @@ github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/decker502/dnspod-go v0.2.0/go.mod h1:qsurYu1FgxcDwfSwXJdLt4kRsBLZeosEb9uq4Sy+08g= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= @@ -212,6 +223,8 @@ github.com/fsouza/go-dockerclient v1.4.4/go.mod h1:PrwszSL5fbmsESocROrOGq/NULMXR github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc= +github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= @@ -245,6 +258,7 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stomp/stomp v2.0.3+incompatible/go.mod h1:VqCtqNZv1226A1/79yh+rMiFUcfY3R109np+7ke4n0c= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= +github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg= github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -365,10 +379,13 @@ github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5 h1:FdBGmSkD2Qp github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5/go.mod h1:0YZ2wQSuwviXXXGUiK6zXzskyBLAbLXhamxzcFHSLoM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= +github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84= +github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= @@ -393,6 +410,8 @@ github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSg github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= +github.com/karrick/godirwalk v1.7.8 h1:VfG72pyIxgtC7+3X9CMHI0AOl4LwyRAg98WAgsvffi8= +github.com/karrick/godirwalk v1.7.8/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= 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/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -412,6 +431,10 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= +github.com/labstack/echo v3.2.1+incompatible h1:J2M7YArHx4gi8p/3fDw8tX19SXhBCoRpviyAZSN3I88= +github.com/labstack/echo v3.2.1+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.2.7 h1:2qOPq/twXDrQ6ooBGrn3mrmVOC+biLlatwgIu8lbzRM= +github.com/labstack/gommon v0.2.7/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -435,11 +458,16 @@ github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGD github.com/marten-seemann/qtls v0.3.2/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/marten-seemann/qtls v0.4.1 h1:YlT8QP3WCCvvok7MGEZkMldXbyqgr8oFg5/n8Gtbkks= github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -473,12 +501,15 @@ github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= @@ -489,6 +520,7 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -530,6 +562,8 @@ github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/Hzq github.com/nrdcg/dnspod-go v0.3.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ= github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= +github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840= +github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/nsqio/go-nsq v1.0.7/go.mod h1:XP5zaUs3pqf+Q71EqUJs3HYfBIqfK6G83WQMdNN+Ito= github.com/ogier/pflag v0.0.1 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750= github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g= @@ -586,6 +620,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= @@ -622,6 +657,8 @@ github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKc github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/restic/calens v0.1.0 h1:RHGokdZ72dICyIz1EjEsfZwUhvNZz/zy2SawxJktdWA= github.com/restic/calens v0.1.0/go.mod h1:u67f5msOjCTDYNzOf/NoAUSdmXP03YXPCwIQLYADy5M= +github.com/restic/calens v0.2.0 h1:LVNAtmFc+Pb4ODX66qdX1T3Di1P0OTLyUsVyvM/xD7E= +github.com/restic/calens v0.2.0/go.mod h1:UXwyAKS4wsgUZGEc7NrzzygJbLsQZIo3wl+62Q1wvmU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= @@ -677,6 +714,7 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= @@ -697,6 +735,10 @@ github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMW github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8= +github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw= github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= @@ -739,6 +781,7 @@ gocloud.dev v0.17.0/go.mod h1:tIHTRdR1V5dlD8sTkzYdTGizBJ314BDykJ8KmadEXwo= gocloud.dev/pubsub/rabbitpubsub v0.17.0/go.mod h1:7o1XYDiIC+b0mmcwJuofsDg08t0DtU2ubfn7C/Uz7Y0= golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -787,6 +830,7 @@ golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180921000356-2f5d2388922f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -836,6 +880,7 @@ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181019160139-8e24a49d80f8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/tools.go b/tools.go new file mode 100644 index 000000000..8a77af67c --- /dev/null +++ b/tools.go @@ -0,0 +1,8 @@ +// +build tools + +package main + +import ( + _ "github.com/UnnoTed/fileb0x" + _ "github.com/restic/calens" +) From 61d0e5263c22e7fc9b6c0db5152f520853ad7cec Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Mon, 9 Mar 2020 12:04:02 +0100 Subject: [PATCH 16/80] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ae2d1e635..7d6963a34 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # ownCloud Infinite Scale: THUMBNAILS -== Badges need to be provided manually == +[![Build Status](https://cloud.drone.io/api/badges/owncloud/ocis-thumbnails/status.svg)](https://cloud.drone.io/owncloud/ocis-thumbnails) +[![Gitter chat](https://badges.gitter.im/cs3org/reva.svg)](https://gitter.im/cs3org/reva) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/5dcc0c9b2319462dbbe517d90219062c)](https://www.codacy.com/gh/owncloud/ocis-thumbnails?utm_source=github.com&utm_medium=referral&utm_content=owncloud/ocis-thumbnails&utm_campaign=Badge_Grade) +[![Go Doc](https://godoc.org/github.com/owncloud/ocis-thumbnails?status.svg)](http://godoc.org/github.com/owncloud/ocis-thumbnails) +[![Go Report](http://goreportcard.com/badge/github.com/owncloud/ocis-thumbnails)](http://goreportcard.com/report/github.com/owncloud/ocis-thumbnails) +[![](https://images.microbadger.com/badges/image/owncloud/ocis-thumbnails.svg)](http://microbadger.com/images/owncloud/ocis-thumbnails "Get your own image badge on microbadger.com") **This project is under heavy development, it's not in a working state yet!** From 78b5231ac2f3b1da825deb26d9c999104de3365e Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Mon, 9 Mar 2020 12:05:31 +0100 Subject: [PATCH 17/80] Update manifest.tmpl --- docker/manifest.tmpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/manifest.tmpl b/docker/manifest.tmpl index 254456d9b..cddd65a91 100644 --- a/docker/manifest.tmpl +++ b/docker/manifest.tmpl @@ -1,4 +1,4 @@ -image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} +image: owncloud/ocis-thumbnails:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} {{#if build.tags}} tags: {{#each build.tags}} @@ -6,16 +6,16 @@ tags: {{/each}} {{/if}} manifests: - - image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 + - image: owncloud/ocis-thumbnails:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 platform: architecture: amd64 os: linux - - image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 + - image: owncloud/ocis-thumbnails:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 platform: architecture: arm64 variant: v8 os: linux - - image: owncloud/ocis-thumbnails :{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm + - image: owncloud/ocis-thumbnails:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm platform: architecture: arm variant: v6 From e36a7ef0cc368665e51b4f732de3129ea5a56603 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Mon, 9 Mar 2020 12:42:56 +0100 Subject: [PATCH 18/80] Typo in CHANGELOG.tmpl --- changelog/CHANGELOG.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/CHANGELOG.tmpl b/changelog/CHANGELOG.tmpl index 17f148fb9..caa3daa89 100644 --- a/changelog/CHANGELOG.tmpl +++ b/changelog/CHANGELOG.tmpl @@ -3,7 +3,7 @@ {{ if gt (len $allVersions) 1 -}} # Changelog for [{{ .Version }}] ({{ .Date }}) -The following sections list the changes in ownCloudocis-thumbnails {{ .Version }}. +The following sections list the changes in ocis-thumbnails {{ .Version }}. {{/* creating version compare links */ -}} {{ $next := add1 $index -}} From 985c11896fa3effe98c2c3f8601a9ee2d3ac22b0 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 11 Mar 2020 14:18:25 +0100 Subject: [PATCH 19/80] refactoring: implement review feedback --- pkg/flagset/flagset.go | 6 ++--- pkg/service/v0/service.go | 17 +++++++------- pkg/thumbnails/imgsource/imgsource.go | 32 +++++---------------------- pkg/thumbnails/imgsource/webdav.go | 13 +++++++++-- 4 files changed, 27 insertions(+), 41 deletions(-) diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 75af98d5b..8512b2d77 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -42,7 +42,7 @@ func HealthWithConfig(cfg *config.Config) []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Name: "debug-addr", - Value: "0.0.0.0:9114", + Value: "0.0.0.0:9189", Usage: "Address to debug endpoint", EnvVars: []string{"THUMBNAILS_DEBUG_ADDR"}, Destination: &cfg.Debug.Addr, @@ -89,7 +89,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { }, &cli.StringFlag{ Name: "debug-addr", - Value: "0.0.0.0:9194", + Value: "0.0.0.0:9189", Usage: "Address to bind debug server", EnvVars: []string{"THUMBNAILS_DEBUG_ADDR"}, Destination: &cfg.Debug.Addr, @@ -115,7 +115,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { }, &cli.StringFlag{ Name: "http-addr", - Value: "0.0.0.0:9190", + Value: "0.0.0.0:9185", Usage: "Address to bind http server", EnvVars: []string{"THUMBNAILS_HTTP_ADDR"}, Destination: &cfg.HTTP.Addr, diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 8740f5acd..28b3052e8 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -1,6 +1,7 @@ package svc import ( + "context" "net/http" "strconv" @@ -24,7 +25,7 @@ func NewService(opts ...Option) Service { m := chi.NewMux() m.Use(options.Middleware...) - svc := Thumbnails{ + svc := Thumbnail{ config: options.Config, mux: m, manager: thumbnails.SimpleManager{ @@ -40,8 +41,8 @@ func NewService(opts ...Option) Service { return svc } -// Thumbnails defines implements the business logic for Service. -type Thumbnails struct { +// Thumbnail implements the business logic for Service. +type Thumbnail struct { config *config.Config mux *chi.Mux manager thumbnails.Manager @@ -49,12 +50,12 @@ type Thumbnails struct { } // ServeHTTP implements the Service interface. -func (g Thumbnails) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (g Thumbnail) ServeHTTP(w http.ResponseWriter, r *http.Request) { g.mux.ServeHTTP(w, r) } // Thumbnails provides the endpoint to retrieve a thumbnail for an image -func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) { +func (g Thumbnail) Thumbnails(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() width, _ := strconv.Atoi(query.Get("width")) height, _ := strconv.Atoi(query.Get("height")) @@ -84,11 +85,9 @@ func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) { } auth := r.Header.Get("Authorization") - - sCtx := imgsource.NewContext() - sCtx.Set(imgsource.WebDavAuth, auth) + sCtx := context.WithValue(r.Context(), imgsource.WebDavAuth, auth) // TODO: clean up error handling - img, err := g.source.Get(ctx.ImagePath, sCtx) + img, err := g.source.Get(sCtx, ctx.ImagePath) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) diff --git a/pkg/thumbnails/imgsource/imgsource.go b/pkg/thumbnails/imgsource/imgsource.go index 6697c9bdc..030a966d0 100644 --- a/pkg/thumbnails/imgsource/imgsource.go +++ b/pkg/thumbnails/imgsource/imgsource.go @@ -1,33 +1,11 @@ package imgsource -import "image" +import ( + "context" + "image" +) // Source defines the interface for image sources type Source interface { - Get(path string, ctx SourceContext) (image.Image, error) -} - -// NewContext creates a new SourceContext instance -func NewContext() SourceContext { - return SourceContext{ - m: make(map[string]interface{}), - } -} - -// SourceContext is used to pass source specific parameters -type SourceContext struct { - m map[string]interface{} -} - -// GetString tries to cast the value to a string -func (s SourceContext) GetString(key string) string { - if s, ok := s.m[key].(string); ok { - return s - } - return "" -} - -// Set sets a value -func (s SourceContext) Set(key string, val interface{}) { - s.m[key] = val + Get(ctx context.Context, path string) (image.Image, error) } diff --git a/pkg/thumbnails/imgsource/webdav.go b/pkg/thumbnails/imgsource/webdav.go index a6d8fdb50..ecb3832e1 100644 --- a/pkg/thumbnails/imgsource/webdav.go +++ b/pkg/thumbnails/imgsource/webdav.go @@ -1,6 +1,7 @@ package imgsource import ( + "context" "fmt" "image" "net/http" @@ -28,7 +29,7 @@ const ( ) // Get downloads the file from a webdav service -func (s WebDav) Get(file string, ctx SourceContext) (image.Image, error) { +func (s WebDav) Get(ctx context.Context, file string) (image.Image, error) { u, _ := url.Parse(s.baseURL) u.Path = path.Join(u.Path, file) req, err := http.NewRequest(http.MethodGet, u.String(), nil) @@ -36,7 +37,10 @@ func (s WebDav) Get(file string, ctx SourceContext) (image.Image, error) { return nil, fmt.Errorf("could not get the image \"%s\" error: %s", file, err.Error()) } - auth := ctx.GetString(WebDavAuth) + auth, ok := authorization(ctx) + if !ok { + return nil, fmt.Errorf("could not get image \"%s\" error: authorization is missing", file) + } req.Header.Add("Authorization", auth) client := &http.Client{} @@ -55,3 +59,8 @@ func (s WebDav) Get(file string, ctx SourceContext) (image.Image, error) { } return img, nil } + +func authorization(ctx context.Context) (string, bool) { + auth, ok := ctx.Value(WebDavAuth).(string) + return auth, ok +} From 1389cd742ee37a69b2abe836a456bf38dbe62971 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 11 Mar 2020 15:11:12 +0100 Subject: [PATCH 20/80] add proper logging --- pkg/service/v0/service.go | 13 ++++++++++--- pkg/thumbnails/storage/filesystem.go | 15 +++++++++------ pkg/thumbnails/thumbnails.go | 22 +++++++++++++++++----- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 28b3052e8..5ae4cb095 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/go-chi/chi" + "github.com/owncloud/ocis-pkg/v2/log" "github.com/owncloud/ocis-thumbnails/pkg/config" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/imgsource" @@ -28,10 +29,15 @@ func NewService(opts ...Option) Service { svc := Thumbnail{ config: options.Config, mux: m, - manager: thumbnails.SimpleManager{ - Storage: storage.NewFileSystemStorage(options.Config.FileSystemStorage), - }, + manager: thumbnails.NewSimpleManager( + storage.NewFileSystemStorage( + options.Config.FileSystemStorage, + options.Logger, + ), + options.Logger, + ), source: imgsource.NewWebDavSource(options.Config.WebDavSource), + logger: options.Logger, } m.Route(options.Config.HTTP.Root, func(r chi.Router) { @@ -47,6 +53,7 @@ type Thumbnail struct { mux *chi.Mux manager thumbnails.Manager source imgsource.Source + logger log.Logger } // ServeHTTP implements the Service interface. diff --git a/pkg/thumbnails/storage/filesystem.go b/pkg/thumbnails/storage/filesystem.go index e92e91bdf..e59b77170 100644 --- a/pkg/thumbnails/storage/filesystem.go +++ b/pkg/thumbnails/storage/filesystem.go @@ -8,25 +8,29 @@ import ( "path/filepath" "strconv" + "github.com/owncloud/ocis-pkg/v2/log" "github.com/owncloud/ocis-thumbnails/pkg/config" ) // NewFileSystemStorage creates a new instanz of FileSystem -func NewFileSystemStorage(cfg config.FileSystemStorage) FileSystem { +func NewFileSystemStorage(cfg config.FileSystemStorage, logger log.Logger) FileSystem { return FileSystem{ - dir: cfg.RootDirectory, + dir: cfg.RootDirectory, + logger: logger, } } // FileSystem represents a storage for the thumbnails using the local file system. type FileSystem struct { - dir string + dir string + logger log.Logger } // Get loads the image from the file system. func (s FileSystem) Get(key string) []byte { content, err := ioutil.ReadFile(filepath.Join(s.dir, key)) if err != nil { + s.logger.Warn().Err(err).Msgf("could not read file %s", key) return nil } @@ -43,13 +47,12 @@ func (s FileSystem) Set(key string, img []byte) error { f, err := os.Create(path) if err != nil { - fmt.Println(err.Error()) - return err + return fmt.Errorf("could not create file \"%s\" error: %s", key, err.Error()) } defer f.Close() _, err = f.Write(img) if err != nil { - return err + return fmt.Errorf("could not write to file \"%s\" error: %s", key, err.Error()) } return nil } diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go index 7172bacd4..c13f4c5b0 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnails/thumbnails.go @@ -5,6 +5,7 @@ import ( "image" "github.com/nfnt/resize" + "github.com/owncloud/ocis-pkg/v2/log" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" ) @@ -26,16 +27,24 @@ type Manager interface { GetStored(Context) []byte } +func NewSimpleManager(storage storage.Storage, logger log.Logger) SimpleManager { + return SimpleManager{ + storage: storage, + logger: logger, + } +} + // SimpleManager is a simple implementation of Manager type SimpleManager struct { - Storage storage.Storage + storage storage.Storage + logger log.Logger } // Get implements the Get Method of Manager func (s SimpleManager) Get(ctx Context, img image.Image) ([]byte, error) { thumbnail := s.generate(ctx, img) - key := s.Storage.BuildKey(mapToStorageContext(ctx)) + key := s.storage.BuildKey(mapToStorageContext(ctx)) buf := new(bytes.Buffer) err := ctx.Encoder.Encode(buf, thumbnail) @@ -43,15 +52,18 @@ func (s SimpleManager) Get(ctx Context, img image.Image) ([]byte, error) { return nil, err } bytes := buf.Bytes() - s.Storage.Set(key, bytes) + err = s.storage.Set(key, bytes) + if err != nil { + s.logger.Warn().Err(err).Msg("could not store thumbnail") + } return bytes, nil } // GetStored tries to get the stored thumbnail and return it. // If there is no cached thumbnail it will return nil func (s SimpleManager) GetStored(ctx Context) []byte { - key := s.Storage.BuildKey(mapToStorageContext(ctx)) - stored := s.Storage.Get(key) + key := s.storage.BuildKey(mapToStorageContext(ctx)) + stored := s.storage.Get(key) return stored } From f9d0f7e413afca734ac068861f446c5269243f03 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 11 Mar 2020 15:18:32 +0100 Subject: [PATCH 21/80] add comment --- pkg/thumbnails/thumbnails.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go index c13f4c5b0..76223228d 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnails/thumbnails.go @@ -27,6 +27,7 @@ type Manager interface { GetStored(Context) []byte } +// NewSimpleManager creates a new instance of SimpleManager func NewSimpleManager(storage storage.Storage, logger log.Logger) SimpleManager { return SimpleManager{ storage: storage, From 016471532a669e42e90c3aed7b13e08397961bf8 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 11 Mar 2020 15:46:52 +0100 Subject: [PATCH 22/80] add changelog --- .../unreleased/{initial-version => initial-version.md} | 0 changelog/unreleased/thumbnail-service.md | 6 ++++++ go.sum | 2 ++ 3 files changed, 8 insertions(+) rename changelog/unreleased/{initial-version => initial-version.md} (100%) create mode 100644 changelog/unreleased/thumbnail-service.md diff --git a/changelog/unreleased/initial-version b/changelog/unreleased/initial-version.md similarity index 100% rename from changelog/unreleased/initial-version rename to changelog/unreleased/initial-version.md diff --git a/changelog/unreleased/thumbnail-service.md b/changelog/unreleased/thumbnail-service.md new file mode 100644 index 000000000..b67eb4988 --- /dev/null +++ b/changelog/unreleased/thumbnail-service.md @@ -0,0 +1,6 @@ +Change: implement the first working version + +We implemented the first simple version. +It can load images via webdav and store them locally in the filesystem. + +https://github.com/owncloud/ocis-thumbnails/pull/3 diff --git a/go.sum b/go.sum index 4dc73902a..3083637f6 100644 --- a/go.sum +++ b/go.sum @@ -819,6 +819,7 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -952,6 +953,7 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361 h1:RIIXAeV6GvDBuADKumTODatUqANFZ+5BPMnzsy4hulY= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From f5f68e27dc648d392bcd2352f0448d58a62a3605 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 11 Mar 2020 15:58:40 +0100 Subject: [PATCH 23/80] use os.TempDir() for portable temporary directory --- pkg/flagset/flagset.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 8512b2d77..7d97e9923 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -1,6 +1,9 @@ package flagset import ( + "os" + "path" + "github.com/micro/cli/v2" "github.com/owncloud/ocis-thumbnails/pkg/config" ) @@ -136,7 +139,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { }, &cli.StringFlag{ Name: "filesystemstorage-root", - Value: "/tmp/ocis-thumbnails/", + Value: path.Join(os.TempDir(), "ocis-thumbnails/"), Usage: "Root path of the filesystem storage directory", EnvVars: []string{"THUMBNAILS_FILESYSTEMSTORAGE_ROOT"}, Destination: &cfg.FileSystemStorage.RootDirectory, From 78a92cf7223dac61834583c1ec500a359f2d8606 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 11 Mar 2020 16:00:51 +0100 Subject: [PATCH 24/80] use filepath.Join instead of path.Join --- pkg/flagset/flagset.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 7d97e9923..04eae4706 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -2,7 +2,7 @@ package flagset import ( "os" - "path" + "path/filepath" "github.com/micro/cli/v2" "github.com/owncloud/ocis-thumbnails/pkg/config" @@ -139,7 +139,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { }, &cli.StringFlag{ Name: "filesystemstorage-root", - Value: path.Join(os.TempDir(), "ocis-thumbnails/"), + Value: filepath.Join(os.TempDir(), "ocis-thumbnails/"), Usage: "Root path of the filesystem storage directory", EnvVars: []string{"THUMBNAILS_FILESYSTEMSTORAGE_ROOT"}, Destination: &cfg.FileSystemStorage.RootDirectory, From 35141c8e0979679218a23dc5594ec061e54a6cb7 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 13 Mar 2020 14:37:52 +0100 Subject: [PATCH 25/80] implement grpc service and remove http service --- go.mod | 2 + go.sum | 1 + pkg/command/server.go | 89 +++---------- pkg/config/config.go | 10 +- pkg/flagset/flagset.go | 32 ++--- pkg/proto/v0/thumbnails.pb.go | 195 ++++++++++++++++++++++++++++ pkg/proto/v0/thumbnails.pb.micro.go | 85 ++++++++++++ pkg/proto/v0/thumbnails.proto | 25 ++++ pkg/server/debug/option.go | 50 ------- pkg/server/debug/server.go | 51 -------- pkg/server/{http => grpc}/option.go | 33 +++-- pkg/server/grpc/server.go | 39 ++++++ pkg/server/http/server.go | 56 -------- pkg/service/v0/instrument.go | 18 +-- pkg/service/v0/logging.go | 18 +-- pkg/service/v0/service.go | 88 ++++--------- pkg/service/v0/tracing.go | 19 ++- 17 files changed, 452 insertions(+), 359 deletions(-) create mode 100644 pkg/proto/v0/thumbnails.pb.go create mode 100644 pkg/proto/v0/thumbnails.pb.micro.go create mode 100644 pkg/proto/v0/thumbnails.proto delete mode 100644 pkg/server/debug/option.go delete mode 100644 pkg/server/debug/server.go rename pkg/server/{http => grpc}/option.go (80%) create mode 100644 pkg/server/grpc/server.go delete mode 100644 pkg/server/http/server.go diff --git a/go.mod b/go.mod index 3972a7db8..1a9250e43 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,10 @@ require ( github.com/UnnoTed/fileb0x v1.1.4 // indirect github.com/cespare/reflex v0.2.0 // indirect github.com/go-chi/chi v4.0.2+incompatible + github.com/golang/protobuf v1.3.2 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/micro/cli/v2 v2.1.1 + github.com/micro/go-micro/v2 v2.0.0 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/ogier/pflag v0.0.1 // indirect github.com/oklog/run v1.0.0 diff --git a/go.sum b/go.sum index 3083637f6..c44798de3 100644 --- a/go.sum +++ b/go.sum @@ -487,6 +487,7 @@ github.com/micro/go-micro v1.18.0/go.mod h1:klwUJL1gkdY1MHFyz+fFJXn52dKcty4hoe95 github.com/micro/go-micro/v2 v2.0.0 h1:bMx549RwJ9Yuiui8cDVlfYhVNP8I8KBJTMyLthEXpRw= github.com/micro/go-micro/v2 v2.0.0/go.mod h1:v7QP5UhKRt37ixjJe8DouWmg0/eE6dltr5h0idJ9BpE= github.com/micro/go-plugins v1.5.1/go.mod h1:jcxejzJCAMH731cQHbS/hncyKe0rxAbzKkibj8glad4= +github.com/micro/go-plugins/wrapper/trace/opencensus/v2 v2.0.1 h1:7IkXfl94MdLZQwk0lNmu9Cg5WP42Zak9EtQMeN4SvVs= github.com/micro/go-plugins/wrapper/trace/opencensus/v2 v2.0.1/go.mod h1:QrkcwcDtIs2hIJpIEhozekyf6Rfz5C36kFI8+zzCpX0= github.com/micro/mdns v0.3.0 h1:bYycYe+98AXR3s8Nq5qvt6C573uFTDPIYzJemWON0QE= github.com/micro/mdns v0.3.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc= diff --git a/pkg/command/server.go b/pkg/command/server.go index 31f8dce9d..af6cafecf 100644 --- a/pkg/command/server.go +++ b/pkg/command/server.go @@ -2,9 +2,9 @@ package command import ( "context" + "fmt" "os" "os/signal" - "strings" "time" "contrib.go.opencensus.io/exporter/jaeger" @@ -17,8 +17,7 @@ import ( "github.com/owncloud/ocis-thumbnails/pkg/config" "github.com/owncloud/ocis-thumbnails/pkg/flagset" "github.com/owncloud/ocis-thumbnails/pkg/metrics" - "github.com/owncloud/ocis-thumbnails/pkg/server/debug" - "github.com/owncloud/ocis-thumbnails/pkg/server/http" + "github.com/owncloud/ocis-thumbnails/pkg/server/grpc" "go.opencensus.io/stats/view" "go.opencensus.io/trace" ) @@ -30,10 +29,6 @@ func Server(cfg *config.Config) *cli.Command { Usage: "Start integrated server", Flags: flagset.ServerWithConfig(cfg), Before: func(c *cli.Context) error { - if cfg.HTTP.Root != "/" { - cfg.HTTP.Root = strings.TrimSuffix(cfg.HTTP.Root, "/") - } - return nil }, Action: func(c *cli.Context) error { @@ -131,72 +126,22 @@ func Server(cfg *config.Config) *cli.Command { defer cancel() - { - server, err := http.Server( - http.Logger(logger), - http.Context(ctx), - http.Config(cfg), - http.Metrics(metrics), - http.Flags(flagset.RootWithConfig(cfg)), - http.Flags(flagset.ServerWithConfig(cfg)), - ) + service := grpc.NewService( + grpc.Logger(logger), + grpc.Context(ctx), + grpc.Config(cfg), + grpc.Name(cfg.Server.Name), + grpc.Namespace(cfg.Server.Namespace), + grpc.Address(cfg.Server.Address), + grpc.Metrics(metrics), + ) - if err != nil { - logger.Info(). - Err(err). - Str("transport", "http"). - Msg("Failed to initialize server") - - return err - } - - gr.Add(func() error { - return server.Run() - }, func(_ error) { - logger.Info(). - Str("transport", "http"). - Msg("Shutting down server") - - cancel() - }) - } - - { - server, err := debug.Server( - debug.Logger(logger), - debug.Context(ctx), - debug.Config(cfg), - ) - - if err != nil { - logger.Info(). - Err(err). - Str("transport", "debug"). - Msg("Failed to initialize server") - - return err - } - - gr.Add(func() error { - return server.ListenAndServe() - }, func(_ error) { - ctx, timeout := context.WithTimeout(ctx, 5*time.Second) - - defer timeout() - defer cancel() - - if err := server.Shutdown(ctx); err != nil { - logger.Info(). - Err(err). - Str("transport", "debug"). - Msg("Failed to shutdown server") - } else { - logger.Info(). - Str("transport", "debug"). - Msg("Shutting down server") - } - }) - } + gr.Add(func() error { + return service.Run() + }, func(_ error) { + fmt.Println("shutting down grpc server") + cancel() + }) { stop := make(chan os.Signal, 1) diff --git a/pkg/config/config.go b/pkg/config/config.go index a5bc260e0..e84b514b9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -15,11 +15,11 @@ type Debug struct { Zpages bool } -// HTTP defines the available http configuration. -type HTTP struct { - Addr string +// Server defines the available server configuration. +type Server struct { + Name string Namespace string - Root string + Address string } // Tracing defines the available tracing configuration. @@ -36,7 +36,7 @@ type Config struct { File string Log Log Debug Debug - HTTP HTTP + Server Server Tracing Tracing FileSystemStorage FileSystemStorage WebDavSource WebDavSource diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 04eae4706..2f34817b1 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -117,25 +117,25 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { Destination: &cfg.Debug.Zpages, }, &cli.StringFlag{ - Name: "http-addr", + Name: "grpc-name", + Value: "thumbnails", + Usage: "Name of the service", + EnvVars: []string{"THUMBNAILS_GRPC_NAME"}, + Destination: &cfg.Server.Name, + }, + &cli.StringFlag{ + Name: "grpc-addr", Value: "0.0.0.0:9185", - Usage: "Address to bind http server", - EnvVars: []string{"THUMBNAILS_HTTP_ADDR"}, - Destination: &cfg.HTTP.Addr, + Usage: "Address to bind grpc server", + EnvVars: []string{"THUMBNAILS_GRPC_ADDR"}, + Destination: &cfg.Server.Address, }, &cli.StringFlag{ - Name: "http-namespace", - Value: "com.owncloud.web", - Usage: "Set the base namespace for the http namespace", - EnvVars: []string{"THUMBNAILS_HTTP_NAMESPACE"}, - Destination: &cfg.HTTP.Namespace, - }, - &cli.StringFlag{ - Name: "http-root", - Value: "/", - Usage: "Root path of http server", - EnvVars: []string{"THUMBNAILS_HTTP_ROOT"}, - Destination: &cfg.HTTP.Root, + Name: "grpc-namespace", + Value: "com.owncloud.api", + Usage: "Set the base namespace for the grpc namespace", + EnvVars: []string{"THUMBNAILS_GRPC_NAMESPACE"}, + Destination: &cfg.Server.Namespace, }, &cli.StringFlag{ Name: "filesystemstorage-root", diff --git a/pkg/proto/v0/thumbnails.pb.go b/pkg/proto/v0/thumbnails.pb.go new file mode 100644 index 000000000..8fb6ad6da --- /dev/null +++ b/pkg/proto/v0/thumbnails.pb.go @@ -0,0 +1,195 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pkg/proto/v0/thumbnails.proto + +package proto + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type GetRequest_FileType int32 + +const ( + GetRequest_PNG GetRequest_FileType = 0 + GetRequest_JPG GetRequest_FileType = 1 +) + +var GetRequest_FileType_name = map[int32]string{ + 0: "PNG", + 1: "JPG", +} + +var GetRequest_FileType_value = map[string]int32{ + "PNG": 0, + "JPG": 1, +} + +func (x GetRequest_FileType) String() string { + return proto.EnumName(GetRequest_FileType_name, int32(x)) +} + +func (GetRequest_FileType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_e354cb4f8a62b6c2, []int{0, 0} +} + +type GetRequest struct { + Filepath string `protobuf:"bytes,1,opt,name=filepath,proto3" json:"filepath,omitempty"` + Filetype GetRequest_FileType `protobuf:"varint,2,opt,name=filetype,proto3,enum=com.owncloud.ocis.thumbnails.v0.GetRequest_FileType" json:"filetype,omitempty"` + Etag string `protobuf:"bytes,3,opt,name=etag,proto3" json:"etag,omitempty"` + Width int32 `protobuf:"varint,4,opt,name=width,proto3" json:"width,omitempty"` + Height int32 `protobuf:"varint,5,opt,name=height,proto3" json:"height,omitempty"` + Authorization string `protobuf:"bytes,6,opt,name=authorization,proto3" json:"authorization,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetRequest) Reset() { *m = GetRequest{} } +func (m *GetRequest) String() string { return proto.CompactTextString(m) } +func (*GetRequest) ProtoMessage() {} +func (*GetRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e354cb4f8a62b6c2, []int{0} +} + +func (m *GetRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetRequest.Unmarshal(m, b) +} +func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic) +} +func (m *GetRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetRequest.Merge(m, src) +} +func (m *GetRequest) XXX_Size() int { + return xxx_messageInfo_GetRequest.Size(m) +} +func (m *GetRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetRequest proto.InternalMessageInfo + +func (m *GetRequest) GetFilepath() string { + if m != nil { + return m.Filepath + } + return "" +} + +func (m *GetRequest) GetFiletype() GetRequest_FileType { + if m != nil { + return m.Filetype + } + return GetRequest_PNG +} + +func (m *GetRequest) GetEtag() string { + if m != nil { + return m.Etag + } + return "" +} + +func (m *GetRequest) GetWidth() int32 { + if m != nil { + return m.Width + } + return 0 +} + +func (m *GetRequest) GetHeight() int32 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *GetRequest) GetAuthorization() string { + if m != nil { + return m.Authorization + } + return "" +} + +type GetResponse struct { + Thumbnail []byte `protobuf:"bytes,1,opt,name=thumbnail,proto3" json:"thumbnail,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetResponse) Reset() { *m = GetResponse{} } +func (m *GetResponse) String() string { return proto.CompactTextString(m) } +func (*GetResponse) ProtoMessage() {} +func (*GetResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e354cb4f8a62b6c2, []int{1} +} + +func (m *GetResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetResponse.Unmarshal(m, b) +} +func (m *GetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetResponse.Marshal(b, m, deterministic) +} +func (m *GetResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetResponse.Merge(m, src) +} +func (m *GetResponse) XXX_Size() int { + return xxx_messageInfo_GetResponse.Size(m) +} +func (m *GetResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetResponse proto.InternalMessageInfo + +func (m *GetResponse) GetThumbnail() []byte { + if m != nil { + return m.Thumbnail + } + return nil +} + +func init() { + proto.RegisterEnum("com.owncloud.ocis.thumbnails.v0.GetRequest_FileType", GetRequest_FileType_name, GetRequest_FileType_value) + proto.RegisterType((*GetRequest)(nil), "com.owncloud.ocis.thumbnails.v0.GetRequest") + proto.RegisterType((*GetResponse)(nil), "com.owncloud.ocis.thumbnails.v0.GetResponse") +} + +func init() { proto.RegisterFile("pkg/proto/v0/thumbnails.proto", fileDescriptor_e354cb4f8a62b6c2) } + +var fileDescriptor_e354cb4f8a62b6c2 = []byte{ + // 300 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x91, 0x4f, 0x4f, 0xc2, 0x40, + 0x10, 0xc5, 0x2d, 0x50, 0xfe, 0x8c, 0x68, 0xc8, 0xc4, 0x98, 0x86, 0x60, 0x24, 0xc4, 0x03, 0x09, + 0x66, 0x21, 0xe8, 0x27, 0xf0, 0x60, 0x13, 0x0f, 0x86, 0x54, 0x4e, 0xde, 0x4a, 0x19, 0xd9, 0x8d, + 0xa5, 0xbb, 0xd2, 0x29, 0x04, 0x13, 0x13, 0x3f, 0xba, 0x71, 0x2b, 0x45, 0x4f, 0xea, 0x69, 0xe7, + 0xfd, 0x76, 0xf2, 0x66, 0xf6, 0x2d, 0x9c, 0x99, 0xe7, 0xc5, 0xd0, 0xac, 0x34, 0xeb, 0xe1, 0x7a, + 0x34, 0x64, 0x99, 0x2d, 0x67, 0x49, 0xa8, 0xe2, 0x54, 0x58, 0x86, 0xe7, 0x91, 0x5e, 0x0a, 0xbd, + 0x49, 0xa2, 0x58, 0x67, 0x73, 0xa1, 0x23, 0x95, 0x8a, 0x6f, 0x3d, 0xeb, 0x51, 0xef, 0xbd, 0x04, + 0xe0, 0x13, 0x07, 0xf4, 0x92, 0x51, 0xca, 0xd8, 0x86, 0xfa, 0x93, 0x8a, 0xc9, 0x84, 0x2c, 0x3d, + 0xa7, 0xeb, 0xf4, 0x1b, 0x41, 0xa1, 0x71, 0x92, 0xdf, 0xf1, 0xd6, 0x90, 0x57, 0xea, 0x3a, 0xfd, + 0xe3, 0xf1, 0xb5, 0xf8, 0xc5, 0x5e, 0xec, 0xad, 0xc5, 0xad, 0x8a, 0x69, 0xba, 0x35, 0x14, 0x14, + 0x2e, 0x88, 0x50, 0x21, 0x0e, 0x17, 0x5e, 0xd9, 0x4e, 0xb2, 0x35, 0x9e, 0x80, 0xbb, 0x51, 0x73, + 0x96, 0x5e, 0xa5, 0xeb, 0xf4, 0xdd, 0x20, 0x17, 0x78, 0x0a, 0x55, 0x49, 0x6a, 0x21, 0xd9, 0x73, + 0x2d, 0xfe, 0x52, 0x78, 0x01, 0x47, 0x61, 0xc6, 0x52, 0xaf, 0xd4, 0x6b, 0xc8, 0x4a, 0x27, 0x5e, + 0xd5, 0x5a, 0xfd, 0x84, 0xbd, 0x0e, 0xd4, 0x77, 0xd3, 0xb1, 0x06, 0xe5, 0xc9, 0xbd, 0xdf, 0x3a, + 0xf8, 0x2c, 0xee, 0x26, 0x7e, 0xcb, 0xe9, 0x0d, 0xe0, 0xd0, 0xae, 0x99, 0x1a, 0x9d, 0xa4, 0x84, + 0x1d, 0x68, 0x14, 0x6f, 0xb0, 0x19, 0x34, 0x83, 0x3d, 0x18, 0xbf, 0x41, 0x6b, 0xba, 0x13, 0x0f, + 0xb4, 0x5a, 0xab, 0x88, 0x50, 0x41, 0xd3, 0x27, 0x2e, 0x30, 0x0e, 0xfe, 0x11, 0x4b, 0xfb, 0xf2, + 0x6f, 0xcd, 0xf9, 0x72, 0x37, 0xb5, 0x47, 0xd7, 0x7e, 0xec, 0xac, 0x6a, 0x8f, 0xab, 0x8f, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x54, 0x8e, 0xdc, 0xb1, 0x00, 0x02, 0x00, 0x00, +} diff --git a/pkg/proto/v0/thumbnails.pb.micro.go b/pkg/proto/v0/thumbnails.pb.micro.go new file mode 100644 index 000000000..9533ba8fa --- /dev/null +++ b/pkg/proto/v0/thumbnails.pb.micro.go @@ -0,0 +1,85 @@ +// Code generated by protoc-gen-micro. DO NOT EDIT. +// source: pkg/proto/v0/thumbnails.proto + +package proto + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +import ( + context "context" + client "github.com/micro/go-micro/v2/client" + server "github.com/micro/go-micro/v2/server" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ client.Option +var _ server.Option + +// Client API for ThumbnailService service + +type ThumbnailService interface { + GetThumbnail(ctx context.Context, in *GetRequest, opts ...client.CallOption) (*GetResponse, error) +} + +type thumbnailService struct { + c client.Client + name string +} + +func NewThumbnailService(name string, c client.Client) ThumbnailService { + return &thumbnailService{ + c: c, + name: name, + } +} + +func (c *thumbnailService) GetThumbnail(ctx context.Context, in *GetRequest, opts ...client.CallOption) (*GetResponse, error) { + req := c.c.NewRequest(c.name, "ThumbnailService.GetThumbnail", in) + out := new(GetResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for ThumbnailService service + +type ThumbnailServiceHandler interface { + GetThumbnail(context.Context, *GetRequest, *GetResponse) error +} + +func RegisterThumbnailServiceHandler(s server.Server, hdlr ThumbnailServiceHandler, opts ...server.HandlerOption) error { + type thumbnailService interface { + GetThumbnail(ctx context.Context, in *GetRequest, out *GetResponse) error + } + type ThumbnailService struct { + thumbnailService + } + h := &thumbnailServiceHandler{hdlr} + return s.Handle(s.NewHandler(&ThumbnailService{h}, opts...)) +} + +type thumbnailServiceHandler struct { + ThumbnailServiceHandler +} + +func (h *thumbnailServiceHandler) GetThumbnail(ctx context.Context, in *GetRequest, out *GetResponse) error { + return h.ThumbnailServiceHandler.GetThumbnail(ctx, in, out) +} diff --git a/pkg/proto/v0/thumbnails.proto b/pkg/proto/v0/thumbnails.proto new file mode 100644 index 000000000..94f252914 --- /dev/null +++ b/pkg/proto/v0/thumbnails.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package com.owncloud.ocis.thumbnails.v0; +option go_package = "proto"; + +service ThumbnailService { + rpc GetThumbnail(GetRequest) returns (GetResponse); +} + +message GetRequest { + string filepath = 1; + enum FileType { + PNG = 0; + JPG = 1; + } + FileType filetype = 2; + string etag = 3; + int32 width = 4; + int32 height = 5; + string authorization = 6; +} + +message GetResponse { + bytes thumbnail = 1; +} \ No newline at end of file diff --git a/pkg/server/debug/option.go b/pkg/server/debug/option.go deleted file mode 100644 index b07f8b6ad..000000000 --- a/pkg/server/debug/option.go +++ /dev/null @@ -1,50 +0,0 @@ -package debug - -import ( - "context" - - "github.com/owncloud/ocis-thumbnails/pkg/config" - "github.com/owncloud/ocis-pkg/v2/log" -) - -// Option defines a single option function. -type Option func(o *Options) - -// Options defines the available options for this package. -type Options struct { - Logger log.Logger - Context context.Context - Config *config.Config -} - -// newOptions initializes the available default options. -func newOptions(opts ...Option) Options { - opt := Options{} - - for _, o := range opts { - o(&opt) - } - - return opt -} - -// Logger provides a function to set the logger option. -func Logger(val log.Logger) Option { - return func(o *Options) { - o.Logger = val - } -} - -// Context provides a function to set the context option. -func Context(val context.Context) Option { - return func(o *Options) { - o.Context = val - } -} - -// Config provides a function to set the config option. -func Config(val *config.Config) Option { - return func(o *Options) { - o.Config = val - } -} diff --git a/pkg/server/debug/server.go b/pkg/server/debug/server.go deleted file mode 100644 index 0ac9f253a..000000000 --- a/pkg/server/debug/server.go +++ /dev/null @@ -1,51 +0,0 @@ -package debug - -import ( - "io" - "net/http" - - "github.com/owncloud/ocis-thumbnails/pkg/config" - "github.com/owncloud/ocis-thumbnails/pkg/version" - "github.com/owncloud/ocis-pkg/v2/service/debug" -) - -// Server initializes the debug service and server. -func Server(opts ...Option) (*http.Server, error) { - options := newOptions(opts...) - - return debug.NewService( - debug.Logger(options.Logger), - debug.Name("ocis-thumbnails"), - debug.Version(version.String), - debug.Address(options.Config.Debug.Addr), - debug.Token(options.Config.Debug.Token), - debug.Pprof(options.Config.Debug.Pprof), - debug.Zpages(options.Config.Debug.Zpages), - debug.Health(health(options.Config)), - debug.Ready(ready(options.Config)), - ), nil -} - -// health implements the health check. -func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusOK) - - // TODO(tboerger): check if services are up and running - - io.WriteString(w, http.StatusText(http.StatusOK)) - } -} - -// ready implements the ready check. -func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusOK) - - // TODO(tboerger): check if services are up and running - - io.WriteString(w, http.StatusText(http.StatusOK)) - } -} diff --git a/pkg/server/http/option.go b/pkg/server/grpc/option.go similarity index 80% rename from pkg/server/http/option.go rename to pkg/server/grpc/option.go index 5712bc5f2..569480ff6 100644 --- a/pkg/server/http/option.go +++ b/pkg/server/grpc/option.go @@ -1,12 +1,11 @@ -package http +package grpc import ( "context" - "github.com/micro/cli/v2" + "github.com/owncloud/ocis-pkg/v2/log" "github.com/owncloud/ocis-thumbnails/pkg/config" "github.com/owncloud/ocis-thumbnails/pkg/metrics" - "github.com/owncloud/ocis-pkg/v2/log" ) // Option defines a single option function. @@ -14,12 +13,13 @@ type Option func(o *Options) // Options defines the available options for this package. type Options struct { - Namespace string + Name string + Address string Logger log.Logger Context context.Context Config *config.Config Metrics *metrics.Metrics - Flags []cli.Flag + Namespace string } // newOptions initializes the available default options. @@ -40,6 +40,20 @@ func Logger(val log.Logger) Option { } } +// Name provides a name for the service. +func Name(val string) Option { + return func(o *Options) { + o.Name = val + } +} + +// Address provides an address for the service. +func Address(val string) Option { + return func(o *Options) { + o.Address = val + } +} + // Context provides a function to set the context option. func Context(val context.Context) Option { return func(o *Options) { @@ -61,14 +75,7 @@ func Metrics(val *metrics.Metrics) Option { } } -// Flags provides a function to set the flags option. -func Flags(val []cli.Flag) Option { - return func(o *Options) { - o.Flags = append(o.Flags, val...) - } -} - -// Namespace provides a function to set the Namespace option. +// Namespace provides a function to set the namespace option. func Namespace(val string) Option { return func(o *Options) { o.Namespace = val diff --git a/pkg/server/grpc/server.go b/pkg/server/grpc/server.go new file mode 100644 index 000000000..96c64e1d0 --- /dev/null +++ b/pkg/server/grpc/server.go @@ -0,0 +1,39 @@ +package grpc + +import ( + "github.com/owncloud/ocis-pkg/v2/service/grpc" + "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" + svc "github.com/owncloud/ocis-thumbnails/pkg/service/v0" + "github.com/owncloud/ocis-thumbnails/pkg/version" +) + +// NewService initializes the grpc service and server. +func NewService(opts ...Option) grpc.Service { + options := newOptions(opts...) + + service := grpc.NewService( + grpc.Logger(options.Logger), + grpc.Namespace(options.Namespace), + grpc.Name(options.Name), + grpc.Version(version.String), + grpc.Address(options.Address), + grpc.Context(options.Context), + ) + + var thumbnail proto.ThumbnailServiceHandler + { + thumbnail = svc.NewService( + svc.Config(options.Config), + ) + thumbnail = svc.NewInstrument(thumbnail, options.Metrics) + thumbnail = svc.NewLogging(thumbnail, options.Logger) + } + + proto.RegisterThumbnailServiceHandler( + service.Server(), + thumbnail, + ) + + service.Init() + return service +} diff --git a/pkg/server/http/server.go b/pkg/server/http/server.go deleted file mode 100644 index 66607a2fc..000000000 --- a/pkg/server/http/server.go +++ /dev/null @@ -1,56 +0,0 @@ -package http - -import ( - svc "github.com/owncloud/ocis-thumbnails/pkg/service/v0" - "github.com/owncloud/ocis-thumbnails/pkg/version" - "github.com/owncloud/ocis-pkg/v2/middleware" - "github.com/owncloud/ocis-pkg/v2/service/http" -) - -// Server initializes the http service and server. -func Server(opts ...Option) (http.Service, error) { - options := newOptions(opts...) - - service := http.NewService( - http.Logger(options.Logger), - http.Name("thumbnails"), - http.Version(version.String), - http.Namespace(options.Config.HTTP.Namespace), - http.Address(options.Config.HTTP.Addr), - http.Context(options.Context), - http.Flags(options.Flags...), - ) - - handle := svc.NewService( - svc.Logger(options.Logger), - svc.Config(options.Config), - svc.Middleware( - middleware.RealIP, - middleware.RequestID, - middleware.Cache, - middleware.Cors, - middleware.Secure, - middleware.Version( - "thumbnails", - version.String, - ), - middleware.Logger( - options.Logger, - ), - ), - ) - - { - handle = svc.NewInstrument(handle, options.Metrics) - handle = svc.NewLogging(handle, options.Logger) - handle = svc.NewTracing(handle) - } - - service.Handle( - "/", - handle, - ) - - service.Init() - return service, nil -} diff --git a/pkg/service/v0/instrument.go b/pkg/service/v0/instrument.go index 9c3a6d999..86ab4e6a4 100644 --- a/pkg/service/v0/instrument.go +++ b/pkg/service/v0/instrument.go @@ -1,13 +1,14 @@ package svc import ( - "net/http" + "context" "github.com/owncloud/ocis-thumbnails/pkg/metrics" + v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" ) // NewInstrument returns a service that instruments metrics. -func NewInstrument(next Service, metrics *metrics.Metrics) Service { +func NewInstrument(next v0proto.ThumbnailServiceHandler, metrics *metrics.Metrics) v0proto.ThumbnailServiceHandler { return instrument{ next: next, metrics: metrics, @@ -15,16 +16,11 @@ func NewInstrument(next Service, metrics *metrics.Metrics) Service { } type instrument struct { - next Service + next v0proto.ThumbnailServiceHandler metrics *metrics.Metrics } -// ServeHTTP implements the Service interface. -func (i instrument) ServeHTTP(w http.ResponseWriter, r *http.Request) { - i.next.ServeHTTP(w, r) -} - -// Dummy implements the Service interface. -func (i instrument) Thumbnails(w http.ResponseWriter, r *http.Request) { - i.next.Thumbnails(w, r) +// GetThumbnail implements the ThumbnailServiceHandler interface. +func (i instrument) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error { + return i.next.GetThumbnail(ctx, req, rsp) } diff --git a/pkg/service/v0/logging.go b/pkg/service/v0/logging.go index a439f1efd..90f2916d9 100644 --- a/pkg/service/v0/logging.go +++ b/pkg/service/v0/logging.go @@ -1,13 +1,14 @@ package svc import ( - "net/http" + "context" "github.com/owncloud/ocis-pkg/v2/log" + v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" ) // NewLogging returns a service that logs messages. -func NewLogging(next Service, logger log.Logger) Service { +func NewLogging(next v0proto.ThumbnailServiceHandler, logger log.Logger) v0proto.ThumbnailServiceHandler { return logging{ next: next, logger: logger, @@ -15,16 +16,11 @@ func NewLogging(next Service, logger log.Logger) Service { } type logging struct { - next Service + next v0proto.ThumbnailServiceHandler logger log.Logger } -// ServeHTTP implements the Service interface. -func (l logging) ServeHTTP(w http.ResponseWriter, r *http.Request) { - l.next.ServeHTTP(w, r) -} - -// Dummy implements the Service interface. -func (l logging) Thumbnails(w http.ResponseWriter, r *http.Request) { - l.next.Thumbnails(w, r) +// GetThumbnail implements the ThumbnailServiceHandler interface. +func (l logging) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error { + return l.next.GetThumbnail(ctx, req, rsp) } diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 5ae4cb095..b9a6700ac 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -2,33 +2,20 @@ package svc import ( "context" - "net/http" - "strconv" + "fmt" - "github.com/go-chi/chi" "github.com/owncloud/ocis-pkg/v2/log" - "github.com/owncloud/ocis-thumbnails/pkg/config" + v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/imgsource" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" ) -// Service defines the extension handlers. -type Service interface { - ServeHTTP(http.ResponseWriter, *http.Request) - Thumbnails(http.ResponseWriter, *http.Request) -} - // NewService returns a service implementation for Service. -func NewService(opts ...Option) Service { +func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { options := newOptions(opts...) - m := chi.NewMux() - m.Use(options.Middleware...) - svc := Thumbnail{ - config: options.Config, - mux: m, manager: thumbnails.NewSimpleManager( storage.NewFileSystemStorage( options.Config.FileSystemStorage, @@ -40,77 +27,52 @@ func NewService(opts ...Option) Service { logger: options.Logger, } - m.Route(options.Config.HTTP.Root, func(r chi.Router) { - r.Get("/thumbnails", svc.Thumbnails) - }) - return svc } -// Thumbnail implements the business logic for Service. +// Thumbnail implements the GRPC handler. type Thumbnail struct { - config *config.Config - mux *chi.Mux manager thumbnails.Manager source imgsource.Source logger log.Logger } -// ServeHTTP implements the Service interface. -func (g Thumbnail) ServeHTTP(w http.ResponseWriter, r *http.Request) { - g.mux.ServeHTTP(w, r) -} - -// Thumbnails provides the endpoint to retrieve a thumbnail for an image -func (g Thumbnail) Thumbnails(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query() - width, _ := strconv.Atoi(query.Get("width")) - height, _ := strconv.Atoi(query.Get("height")) - fileType := query.Get("type") - filePath := query.Get("file_path") - etag := query.Get("etag") - - encoder := thumbnails.EncoderForType(fileType) +// GetThumbnail retrieves a thumbnail for an image +func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error { + encoder := thumbnails.EncoderForType(req.Filetype.String()) if encoder == nil { // TODO: better error responses - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("can't encode that")) - return + return fmt.Errorf("can't be encoded. filetype %s not supported", req.Filetype.String()) } - ctx := thumbnails.Context{ - Width: width, - Height: height, - ImagePath: filePath, + tCtx := thumbnails.Context{ + Width: int(req.Width), + Height: int(req.Height), + ImagePath: req.Filepath, Encoder: encoder, - ETag: etag, + ETag: req.Etag, } - thumbnail := g.manager.GetStored(ctx) + thumbnail := g.manager.GetStored(tCtx) if thumbnail != nil { - w.Write(thumbnail) - return + rsp.Thumbnail = thumbnail + return nil } - auth := r.Header.Get("Authorization") - sCtx := context.WithValue(r.Context(), imgsource.WebDavAuth, auth) + auth := req.Authorization + sCtx := context.WithValue(ctx, imgsource.WebDavAuth, auth) // TODO: clean up error handling - img, err := g.source.Get(sCtx, ctx.ImagePath) + img, err := g.source.Get(sCtx, tCtx.ImagePath) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return + return err } if img == nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("img is nil")) - return + return fmt.Errorf("could not retrieve image") } - thumbnail, err = g.manager.Get(ctx, img) + thumbnail, err = g.manager.Get(tCtx, img) if err != nil { - w.Write([]byte(err.Error())) - return + return err } - w.WriteHeader(http.StatusCreated) - w.Write(thumbnail) + rsp.Thumbnail = thumbnail + return nil } diff --git a/pkg/service/v0/tracing.go b/pkg/service/v0/tracing.go index 444afd3ec..33d4f01cc 100644 --- a/pkg/service/v0/tracing.go +++ b/pkg/service/v0/tracing.go @@ -1,26 +1,23 @@ package svc import ( - "net/http" + "context" + + v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" ) // NewTracing returns a service that instruments traces. -func NewTracing(next Service) Service { +func NewTracing(next v0proto.ThumbnailServiceHandler) v0proto.ThumbnailServiceHandler { return tracing{ next: next, } } type tracing struct { - next Service + next v0proto.ThumbnailServiceHandler } -// ServeHTTP implements the Service interface. -func (t tracing) ServeHTTP(w http.ResponseWriter, r *http.Request) { - t.next.ServeHTTP(w, r) -} - -// Dummy implements the Service interface. -func (t tracing) Thumbnails(w http.ResponseWriter, r *http.Request) { - t.next.Thumbnails(w, r) +// GetThumbnail implements the ThumbnailServiceHandler interface. +func (t tracing) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error { + return t.next.GetThumbnail(ctx, req, rsp) } From 06151de3f467d731cf3eb8d6aee533e5f374ec36 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 16 Mar 2020 16:55:10 +0100 Subject: [PATCH 26/80] implement metrics, logging and tracing --- go.mod | 12 +- go.sum | 296 +++-------------------------------- pkg/metrics/metrics.go | 44 ++++-- pkg/service/v0/instrument.go | 15 +- pkg/service/v0/logging.go | 19 ++- pkg/service/v0/tracing.go | 12 ++ tools.go | 3 + 7 files changed, 110 insertions(+), 291 deletions(-) diff --git a/go.mod b/go.mod index 1a9250e43..3c986aa07 100644 --- a/go.mod +++ b/go.mod @@ -6,19 +6,19 @@ require ( contrib.go.opencensus.io/exporter/jaeger v0.2.0 contrib.go.opencensus.io/exporter/ocagent v0.6.0 contrib.go.opencensus.io/exporter/zipkin v0.1.1 - github.com/UnnoTed/fileb0x v1.1.4 // indirect - github.com/cespare/reflex v0.2.0 // indirect - github.com/go-chi/chi v4.0.2+incompatible + github.com/UnnoTed/fileb0x v1.1.4 + github.com/cespare/reflex v0.2.0 github.com/golang/protobuf v1.3.2 - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/micro/cli/v2 v2.1.1 github.com/micro/go-micro/v2 v2.0.0 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 - github.com/ogier/pflag v0.0.1 // indirect + github.com/ogier/pflag v0.0.1 github.com/oklog/run v1.0.0 github.com/openzipkin/zipkin-go v0.2.2 github.com/owncloud/ocis-pkg/v2 v2.0.1 - github.com/restic/calens v0.2.0 // indirect + github.com/prometheus/client_golang v1.2.1 + github.com/restic/calens v0.2.0 github.com/spf13/viper v1.5.0 go.opencensus.io v0.22.2 ) diff --git a/go.sum b/go.sum index c44798de3..bec1dd4f8 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,6 @@ cloud.google.com/go v0.26.0/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= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= @@ -11,29 +10,16 @@ cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbf cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= contrib.go.opencensus.io/exporter/jaeger v0.2.0 h1:nhTv/Ry3lGmqbJ/JGvCjWxBl5ozRfqo86Ngz59UAlfk= contrib.go.opencensus.io/exporter/jaeger v0.2.0/go.mod h1:ukdzwIYYHgZ7QYtwVFQUjiT28BJHiMhTERo32s6qVgM= contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= -contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= contrib.go.opencensus.io/exporter/ocagent v0.6.0 h1:Z1n6UAyr0QwM284yUuh5Zd8JlvxUGAhFZcgMJkMPrGM= contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs= -contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw= contrib.go.opencensus.io/exporter/zipkin v0.1.1 h1:PR+1zWqY8ceXs1qDQQIlgXe+sdiwCf0n32bH4+Epk8g= contrib.go.opencensus.io/exporter/zipkin v0.1.1/go.mod h1:GMvdSl3eJ2gapOaLKzTKE3qDgUkJ86k9k3yY2eqwkzc= -contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE= -contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU= -github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg= -github.com/Azure/azure-pipeline-go v0.1.9/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg= -github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0= -github.com/Azure/azure-storage-blob-go v0.6.0/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw= github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= @@ -49,8 +35,6 @@ github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvd github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190605020000-c4ba1fdf4d36/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.0.2 h1:tRi7ENs+AaOUCH+j6qwNQgPYfV26dX3JNonq+V4mhqc= @@ -61,19 +45,12 @@ github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jB github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/sarama v1.24.1/go.mod h1:fGP8eQ6PugKEI0iUETYYtnP6d1pH/bdDMTel1X5ajsU= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/UnnoTed/fileb0x v1.1.4 h1:IUgFzgBipF/ujNx9wZgkrKOF3oltUuXMSoaejrBws+A= github.com/UnnoTed/fileb0x v1.1.4/go.mod h1:X59xXT18tdNk/D6j+KZySratBsuKJauMtVuJ9cgOiZs= -github.com/abbot/go-http-auth v0.4.1-0.20181019201920-860ed7f246ff/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= @@ -83,51 +60,27 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= -github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= -github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= -github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y= -github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ= -github.com/anacrolix/sync v0.2.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g= -github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= -github.com/anacrolix/utp v0.0.0-20180219060659-9e0e1d1d0572/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee h1:3T/l+vMotQ7cDSLWNAn2Vg1SAQ3mdyLgBWWBitSS3uU= github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee/go.mod h1:u7Wtt4WATGGgae9mURNGQQqxAudPKrxfsbSDSGOso+g= -github.com/asim/go-awsxray v0.0.0-20161209120537-0d8a60b6e205/go.mod h1:frVmN4PtXUuL1EbZn0uL4PHSTKNKFnbMpBIhngqMuNQ= -github.com/asim/go-bson v0.0.0-20160318195205-84522947cabd/go.mod h1:L59ZX7HuzTbNzFBt8g3SJkRraj+GBOgvLAfJYJUcQ5w= -github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.25.31/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= -github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= -github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/bwmarrin/discordgo v0.20.1/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= @@ -140,11 +93,8 @@ github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReG github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= -github.com/cloudflare/cloudflare-go v0.10.6/go.mod h1:dcRl7AXBH5Bf7QFTBVc3TRzwvotSeO4AlnMhuxORAX8= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -156,6 +106,7 @@ github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.17+incompatible h1:f/Z3EoDSx1yjaIjLQGo1diYUlQYSBrrAQ5vP8NjwXwo= @@ -165,6 +116,7 @@ github.com/coreos/etcd v3.3.18+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= @@ -176,12 +128,11 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSY github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/decker502/dnspod-go v0.2.0/go.mod h1:qsurYu1FgxcDwfSwXJdLt4kRsBLZeosEb9uq4Sy+08g= -github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 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= @@ -192,42 +143,26 @@ github.com/docker/docker v1.4.2-0.20190710153559-aa8249ae1b8b/go.mod h1:eEKB0N0r github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/eknkc/basex v1.0.0/go.mod h1:k/F/exNEHFdbs3ZHuasoP2E7zeWwZblG84Y7Z59vQRo= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c/go.mod h1:pFdJbAhRf7rh6YYMUdIQGyzne6zYL1tCUW8QV2B3UfY= -github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsouza/go-dockerclient v1.4.4/go.mod h1:PrwszSL5fbmsESocROrOGq/NULMXRw+bajY0ltzD6MA= github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc= github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-acme/lego/v3 v3.1.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE= github.com/go-acme/lego/v3 v3.3.0/go.mod h1:iGSY2vQrvQs3WezicSB/oVbO2eCrD88dpWPwb1qLqu0= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= @@ -235,32 +170,21 @@ github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxm github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-log/log v0.1.0 h1:wudGTNsiGzrD5ZjgIkVZ517ugi2XRe9Q/xRCzwEO4/U= github.com/go-log/log v0.1.0/go.mod h1:4mBwpdRMFLiuXZDCwU2lKQFsoSCo72j3HqBK9d81N2M= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-redsync/redsync v1.3.1/go.mod h1:qxZwM5JOimfq8y98Wk2+c8dKtxJgG5/yIl2ODz2E5Dk= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stomp/stomp v2.0.3+incompatible/go.mod h1:VqCtqNZv1226A1/79yh+rMiFUcfY3R109np+7ke4n0c= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg= github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= 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= @@ -268,11 +192,9 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc h1:55rEp52jU6bkyslZ1+C/7NGfpQsEc6pxGLAGDOctqbw= @@ -282,120 +204,73 @@ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4er github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= -github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= -github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.4 h1:5xLhQjsk4zqPf9EHCrja2qFZMx+yBqkO3XgJ14bNnU0= github.com/grpc-ecosystem/grpc-gateway v1.9.4/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hako/branca v0.0.0-20180808000428-10b799466ada/go.mod h1:tOPn4gvKEUWqIJNE+zpTeTALaRAXnrRqqSnPlO3VpEo= -github.com/hashicorp/consul/api v1.2.0/go.mod h1:1SIkFYi2ZTXUE5Kgt179+4hH33djo11+0Eo2XgTAtkw= -github.com/hashicorp/consul/sdk v0.2.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= -github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= -github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.1.5/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= -github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5 h1:FdBGmSkD2QpQzRWup//SGObvWf2nq89zj9+ta9OvI3A= github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5/go.mod h1:0YZ2wQSuwviXXXGUiK6zXzskyBLAbLXhamxzcFHSLoM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1/go.mod h1:DFXrEwSRX0p/aSvxE21319menCBFeQO0jXpRj7LEZUA= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -404,9 +279,7 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= @@ -416,18 +289,19 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= @@ -437,7 +311,6 @@ github.com/labstack/gommon v0.2.7 h1:2qOPq/twXDrQ6ooBGrn3mrmVOC+biLlatwgIu8lbzRM github.com/labstack/gommon v0.2.7/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -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/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= @@ -450,8 +323,6 @@ github.com/lucas-clemente/quic-go v0.14.1/go.mod h1:Vn3/Fb0/77b02SGhQk36KzOUmXgV github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/chacha20 v0.2.0 h1:f40vqzzx+3GdOmzQoItkLX5WLvHgPgyYqFFIO5Gh4hQ= github.com/marten-seemann/chacha20 v0.2.0/go.mod h1:HSdjFau7GzYRj+ahFNwsO3ouVJr1HFkWoEwNDb4TMtE= github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI= @@ -460,11 +331,9 @@ github.com/marten-seemann/qtls v0.4.1 h1:YlT8QP3WCCvvok7MGEZkMldXbyqgr8oFg5/n8Gt github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= @@ -480,45 +349,30 @@ github.com/micro/cli v0.2.0/go.mod h1:jRT9gmfVKWSS6pkKcXQ8YhUyj6bzwxK8Fp5b0Y7qNn github.com/micro/cli/v2 v2.1.1 h1:uFw0SMIKmGuyHIm8lXns/NOn7V62bM5y7DnlxUM+BEQ= github.com/micro/cli/v2 v2.1.1/go.mod h1:EguNh6DAoWKm9nmk+k/Rg0H3lQnDxqzu5x5srOtGtYg= github.com/micro/go-micro v1.16.0/go.mod h1:A0F58bHLh2m0LAI9QyhvmbN8c1cxhAZo3cM6s+iDsrM= -github.com/micro/go-micro v1.17.1 h1:BhwC4Lnwr3hdci9T8kN5MEabKS+CT0QT0YH4hinTdNs= -github.com/micro/go-micro v1.17.1/go.mod h1:klwUJL1gkdY1MHFyz+fFJXn52dKcty4hoe95Mp571AA= github.com/micro/go-micro v1.18.0 h1:gP70EZVHpJuUIT0YWth192JmlIci+qMOEByHm83XE9E= github.com/micro/go-micro v1.18.0/go.mod h1:klwUJL1gkdY1MHFyz+fFJXn52dKcty4hoe95Mp571AA= github.com/micro/go-micro/v2 v2.0.0 h1:bMx549RwJ9Yuiui8cDVlfYhVNP8I8KBJTMyLthEXpRw= github.com/micro/go-micro/v2 v2.0.0/go.mod h1:v7QP5UhKRt37ixjJe8DouWmg0/eE6dltr5h0idJ9BpE= -github.com/micro/go-plugins v1.5.1/go.mod h1:jcxejzJCAMH731cQHbS/hncyKe0rxAbzKkibj8glad4= github.com/micro/go-plugins/wrapper/trace/opencensus/v2 v2.0.1 h1:7IkXfl94MdLZQwk0lNmu9Cg5WP42Zak9EtQMeN4SvVs= github.com/micro/go-plugins/wrapper/trace/opencensus/v2 v2.0.1/go.mod h1:QrkcwcDtIs2hIJpIEhozekyf6Rfz5C36kFI8+zzCpX0= github.com/micro/mdns v0.3.0 h1:bYycYe+98AXR3s8Nq5qvt6C573uFTDPIYzJemWON0QE= github.com/micro/mdns v0.3.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc= -github.com/micro/micro v1.16.0/go.mod h1:TO5Ng0KidbfRYIxVM4Q3deZ0A+qwRyP9WeXp+k2fWNA= github.com/micro/protoc-gen-micro v1.0.0/go.mod h1:C8ij4DJhapBmypcT00AXdb0cZ675/3PqUO02buWWqbE= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.22 h1:Jm64b3bO9kP43ddLjL2EY3Io6bmy1qGb9Xxz6TqS6rc= github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= -github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= @@ -526,15 +380,11 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= @@ -553,7 +403,6 @@ github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nats-io/stan.go v0.5.0/go.mod h1:dYqB+vMN3C2F9pT1FRQpg9eHbjPj6mP0yYuyBNuXHZE= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= @@ -565,21 +414,15 @@ github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2 github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840= github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= -github.com/nsqio/go-nsq v1.0.7/go.mod h1:XP5zaUs3pqf+Q71EqUJs3HYfBIqfK6G83WQMdNN+Ito= github.com/ogier/pflag v0.0.1 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750= github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= @@ -593,40 +436,25 @@ github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= -github.com/owncloud/ocis-pkg v1.1.0 h1:cwhzqQAHLA8fz94YKdmqSJguSCswx5TBNkgLAU8DbJI= -github.com/owncloud/ocis-pkg v1.1.0/go.mod h1:EfbeXoe60Me2lB/AWjYl8UFNv4isqCPP6lokd5R7nyM= -github.com/owncloud/ocis-pkg v1.3.0 h1:2fkgvfd/spTjschuulYMHRuzxkCGGXae9ocebVYkm74= -github.com/owncloud/ocis-pkg v1.3.0/go.mod h1:Wo0QfOmhadh2vNcUoQIsw2yaOT3zeftk+xaOOwP3y88= -github.com/owncloud/ocis-pkg/v2 v2.0.0 h1:RJAe5wYsNZrDjbRX8iKpVf5pnzKbFAoVa23oApv4mEE= -github.com/owncloud/ocis-pkg/v2 v2.0.0/go.mod h1:7bVnn3VUaqdmvpMkXF0QVEF1fRugs35hSkuVTAq9yjk= github.com/owncloud/ocis-pkg/v2 v2.0.1 h1:3ISEtfjAz4pDFczTggIJwKuft3bVsAp1C7dFY9BBPEs= github.com/owncloud/ocis-pkg/v2 v2.0.1/go.mod h1:7bVnn3VUaqdmvpMkXF0QVEF1fRugs35hSkuVTAq9yjk= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -662,44 +490,32 @@ github.com/restic/calens v0.2.0 h1:LVNAtmFc+Pb4ODX66qdX1T3Di1P0OTLyUsVyvM/xD7E= github.com/restic/calens v0.2.0/go.mod h1:UXwyAKS4wsgUZGEc7NrzzygJbLsQZIo3wl+62Q1wvmU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.17.2 h1:RMRHFw2+wF7LO0QqtELQwo8hqSmqISyCJeFeAAuWcRo= github.com/rs/zerolog v1.17.2/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.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 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= -github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -707,25 +523,20 @@ github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4= github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= -github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc h1:yUaosFVTJwnltaHbSNC3i82I92quFs+OFPRl8kNMVwo= github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= @@ -741,21 +552,18 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8= github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw= github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= -github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= 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 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= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -771,19 +579,16 @@ go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= -go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.12.0 h1:dySoUQPFBGj6xwjmBzageVL8jGi8uxc6bEmJQjA06bw= go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -gocloud.dev v0.17.0/go.mod h1:tIHTRdR1V5dlD8sTkzYdTGizBJ314BDykJ8KmadEXwo= -gocloud.dev/pubsub/rabbitpubsub v0.17.0/go.mod h1:7o1XYDiIC+b0mmcwJuofsDg08t0DtU2ubfn7C/Uz7Y0= golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -791,7 +596,6 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/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-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -827,15 +631,12 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180921000356-2f5d2388922f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -848,24 +649,19 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/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-20191011234655-491137f69257/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/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-20191109021931-daa7c04131f5 h1:bHNaocaoJxYBo5cw41UyTMLjYlb8wPY7+WFrnklbHOM= golang.org/x/net v0.0.0-20191109021931-daa7c04131f5/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -876,23 +672,18 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEha golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181019160139-8e24a49d80f8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -902,13 +693,11 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -916,27 +705,23 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM= golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w= golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -960,13 +745,9 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -976,13 +757,9 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= @@ -992,7 +769,6 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba h1:pRj9OXZbwNtbtZtOB4dLwfK4u+EVRMvP+e9zKkg2grM= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1007,34 +783,19 @@ google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -gopkg.in/DataDog/dd-trace-go.v1 v1.19.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= -gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= -gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= -gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= -gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= -gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= -gopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= -gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= -gopkg.in/ldap.v3 v3.1.0/go.mod h1:dQjCc0R0kfyFjIlWNMH1DORwUASZyDxo2Ry1B51dXaQ= gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= -gopkg.in/olivere/elastic.v5 v5.0.82/go.mod h1:uhHoB4o3bvX5sorxBU29rPcmBQdV2Qfg0FBrx5D6pV0= -gopkg.in/redis.v3 v3.6.4/go.mod h1:6XeGv/CrsUFDU9aVbUdNykN7k1zVmoeg83KC9RbQfiU= gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= @@ -1058,18 +819,7 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.0.0-20191109101513-0171b7c15da1/go.mod h1:VJq7+38rpM4TSUbRiZX4P5UVAKK2UQpNQLZClkFQkpE= -k8s.io/apimachinery v0.0.0-20191109100837-dffb012825f2/go.mod h1:+6CX7hP4aLfX2sb91JYDMIp0VqDSog2kZu0BHe+lP+s= -k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc/go.mod h1:+6CX7hP4aLfX2sb91JYDMIp0VqDSog2kZu0BHe+lP+s= -k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index f08a93a98..0ecf2f55c 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -1,5 +1,7 @@ package metrics +import "github.com/prometheus/client_golang/prometheus" + var ( // Namespace defines the namespace for the defines metrics. Namespace = "ocis" @@ -10,23 +12,45 @@ var ( // Metrics defines the available metrics of this service. type Metrics struct { - // Counter *prometheus.CounterVec + Counter *prometheus.CounterVec + Latency *prometheus.SummaryVec + Duration *prometheus.HistogramVec } // New initializes the available metrics. func New() *Metrics { m := &Metrics{ - // Counter: prometheus.NewCounterVec(prometheus.CounterOpts{ - // Namespace: Namespace, - // Subsystem: Subsystem, - // Name: "greet_total", - // Help: "How many greeting requests processed", - // }, []string{}), + Counter: prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: Namespace, + Subsystem: Subsystem, + Name: "getthumbnail_total", + Help: "How many GetThumbnail requests processed", + }, []string{}), + Latency: prometheus.NewSummaryVec(prometheus.SummaryOpts{ + Namespace: Namespace, + Subsystem: Subsystem, + Name: "getthumbnail_latency_microseconds", + Help: "GetThumbnail request latencies in microseconds", + }, []string{}), + Duration: prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: Namespace, + Subsystem: Subsystem, + Name: "getthumbnail_duration_seconds", + Help: "GetThumbnail method requests time in seconds", + }, []string{}), } - // prometheus.Register( - // m.Counter, - // ) + prometheus.Register( + m.Counter, + ) + + prometheus.Register( + m.Latency, + ) + + prometheus.Register( + m.Duration, + ) return m } diff --git a/pkg/service/v0/instrument.go b/pkg/service/v0/instrument.go index 86ab4e6a4..dbd9026c0 100644 --- a/pkg/service/v0/instrument.go +++ b/pkg/service/v0/instrument.go @@ -5,6 +5,7 @@ import ( "github.com/owncloud/ocis-thumbnails/pkg/metrics" v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" + "github.com/prometheus/client_golang/prometheus" ) // NewInstrument returns a service that instruments metrics. @@ -22,5 +23,17 @@ type instrument struct { // GetThumbnail implements the ThumbnailServiceHandler interface. func (i instrument) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error { - return i.next.GetThumbnail(ctx, req, rsp) + timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) { + us := v * 1000_000 + i.metrics.Latency.WithLabelValues().Observe(us) + i.metrics.Duration.WithLabelValues().Observe(v) + })) + defer timer.ObserveDuration() + + err := i.next.GetThumbnail(ctx, req, rsp) + + if err != nil { + i.metrics.Counter.WithLabelValues().Inc() + } + return err } diff --git a/pkg/service/v0/logging.go b/pkg/service/v0/logging.go index 90f2916d9..0972efadb 100644 --- a/pkg/service/v0/logging.go +++ b/pkg/service/v0/logging.go @@ -2,6 +2,7 @@ package svc import ( "context" + "time" "github.com/owncloud/ocis-pkg/v2/log" v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" @@ -22,5 +23,21 @@ type logging struct { // GetThumbnail implements the ThumbnailServiceHandler interface. func (l logging) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error { - return l.next.GetThumbnail(ctx, req, rsp) + start := time.Now() + err := l.next.GetThumbnail(ctx, req, rsp) + + logger := l.logger.With(). + Str("method", "Thumbnails.GetThumbnail"). + Dur("duration", time.Since(start)). + Logger() + + if err != nil { + logger.Warn(). + Err(err). + Msg("Failed to execute") + } else { + logger.Debug(). + Msg("") + } + return err } diff --git a/pkg/service/v0/tracing.go b/pkg/service/v0/tracing.go index 33d4f01cc..cf88e0f01 100644 --- a/pkg/service/v0/tracing.go +++ b/pkg/service/v0/tracing.go @@ -4,6 +4,7 @@ import ( "context" v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" + "go.opencensus.io/trace" ) // NewTracing returns a service that instruments traces. @@ -19,5 +20,16 @@ type tracing struct { // GetThumbnail implements the ThumbnailServiceHandler interface. func (t tracing) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error { + ctx, span := trace.StartSpan(ctx, "Thumbnails.GetThumbnail") + defer span.End() + + span.Annotate([]trace.Attribute{ + trace.StringAttribute("filepath", req.Filepath), + trace.StringAttribute("filetype", req.Filetype.String()), + trace.StringAttribute("etag", req.Etag), + trace.Int64Attribute("width", int64(req.Width)), + trace.Int64Attribute("height", int64(req.Height)), + }, "Execute Thumbnails.GetThumbnail handler") + return t.next.GetThumbnail(ctx, req, rsp) } diff --git a/tools.go b/tools.go index 8a77af67c..2d5012ca2 100644 --- a/tools.go +++ b/tools.go @@ -4,5 +4,8 @@ package main import ( _ "github.com/UnnoTed/fileb0x" + _ "github.com/cespare/reflex" + _ "github.com/kballard/go-shellquote" + _ "github.com/ogier/pflag" _ "github.com/restic/calens" ) From 024e4661e2b07bcdc5843699929a5e81764b4a7a Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 18 Mar 2020 11:13:09 +0100 Subject: [PATCH 27/80] add mimetype to service response --- pkg/proto/v0/thumbnails.pb.go | 45 +++++++++++++++++++++-------------- pkg/proto/v0/thumbnails.proto | 1 + pkg/service/v0/service.go | 2 ++ pkg/thumbnails/encoding.go | 12 ++++++++++ 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/pkg/proto/v0/thumbnails.pb.go b/pkg/proto/v0/thumbnails.pb.go index 8fb6ad6da..db7c9109a 100644 --- a/pkg/proto/v0/thumbnails.pb.go +++ b/pkg/proto/v0/thumbnails.pb.go @@ -126,6 +126,7 @@ func (m *GetRequest) GetAuthorization() string { type GetResponse struct { Thumbnail []byte `protobuf:"bytes,1,opt,name=thumbnail,proto3" json:"thumbnail,omitempty"` + Mimetype string `protobuf:"bytes,2,opt,name=mimetype,proto3" json:"mimetype,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -163,6 +164,13 @@ func (m *GetResponse) GetThumbnail() []byte { return nil } +func (m *GetResponse) GetMimetype() string { + if m != nil { + return m.Mimetype + } + return "" +} + func init() { proto.RegisterEnum("com.owncloud.ocis.thumbnails.v0.GetRequest_FileType", GetRequest_FileType_name, GetRequest_FileType_value) proto.RegisterType((*GetRequest)(nil), "com.owncloud.ocis.thumbnails.v0.GetRequest") @@ -172,24 +180,25 @@ func init() { func init() { proto.RegisterFile("pkg/proto/v0/thumbnails.proto", fileDescriptor_e354cb4f8a62b6c2) } var fileDescriptor_e354cb4f8a62b6c2 = []byte{ - // 300 bytes of a gzipped FileDescriptorProto + // 309 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x91, 0x4f, 0x4f, 0xc2, 0x40, - 0x10, 0xc5, 0x2d, 0x50, 0xfe, 0x8c, 0x68, 0xc8, 0xc4, 0x98, 0x86, 0x60, 0x24, 0xc4, 0x03, 0x09, + 0x10, 0xc5, 0x2d, 0x50, 0xfe, 0x8c, 0x68, 0xc8, 0xc4, 0x98, 0x86, 0x60, 0x24, 0xc4, 0x03, 0x89, 0x66, 0x21, 0xe8, 0x27, 0xf0, 0x60, 0x13, 0x0f, 0x86, 0x54, 0x4e, 0xde, 0x4a, 0x19, 0xd9, 0x8d, - 0xa5, 0xbb, 0xd2, 0x29, 0x04, 0x13, 0x13, 0x3f, 0xba, 0x71, 0x2b, 0x45, 0x4f, 0xea, 0x69, 0xe7, - 0xfd, 0x76, 0xf2, 0x66, 0xf6, 0x2d, 0x9c, 0x99, 0xe7, 0xc5, 0xd0, 0xac, 0x34, 0xeb, 0xe1, 0x7a, - 0x34, 0x64, 0x99, 0x2d, 0x67, 0x49, 0xa8, 0xe2, 0x54, 0x58, 0x86, 0xe7, 0x91, 0x5e, 0x0a, 0xbd, - 0x49, 0xa2, 0x58, 0x67, 0x73, 0xa1, 0x23, 0x95, 0x8a, 0x6f, 0x3d, 0xeb, 0x51, 0xef, 0xbd, 0x04, - 0xe0, 0x13, 0x07, 0xf4, 0x92, 0x51, 0xca, 0xd8, 0x86, 0xfa, 0x93, 0x8a, 0xc9, 0x84, 0x2c, 0x3d, - 0xa7, 0xeb, 0xf4, 0x1b, 0x41, 0xa1, 0x71, 0x92, 0xdf, 0xf1, 0xd6, 0x90, 0x57, 0xea, 0x3a, 0xfd, - 0xe3, 0xf1, 0xb5, 0xf8, 0xc5, 0x5e, 0xec, 0xad, 0xc5, 0xad, 0x8a, 0x69, 0xba, 0x35, 0x14, 0x14, - 0x2e, 0x88, 0x50, 0x21, 0x0e, 0x17, 0x5e, 0xd9, 0x4e, 0xb2, 0x35, 0x9e, 0x80, 0xbb, 0x51, 0x73, - 0x96, 0x5e, 0xa5, 0xeb, 0xf4, 0xdd, 0x20, 0x17, 0x78, 0x0a, 0x55, 0x49, 0x6a, 0x21, 0xd9, 0x73, - 0x2d, 0xfe, 0x52, 0x78, 0x01, 0x47, 0x61, 0xc6, 0x52, 0xaf, 0xd4, 0x6b, 0xc8, 0x4a, 0x27, 0x5e, - 0xd5, 0x5a, 0xfd, 0x84, 0xbd, 0x0e, 0xd4, 0x77, 0xd3, 0xb1, 0x06, 0xe5, 0xc9, 0xbd, 0xdf, 0x3a, - 0xf8, 0x2c, 0xee, 0x26, 0x7e, 0xcb, 0xe9, 0x0d, 0xe0, 0xd0, 0xae, 0x99, 0x1a, 0x9d, 0xa4, 0x84, - 0x1d, 0x68, 0x14, 0x6f, 0xb0, 0x19, 0x34, 0x83, 0x3d, 0x18, 0xbf, 0x41, 0x6b, 0xba, 0x13, 0x0f, - 0xb4, 0x5a, 0xab, 0x88, 0x50, 0x41, 0xd3, 0x27, 0x2e, 0x30, 0x0e, 0xfe, 0x11, 0x4b, 0xfb, 0xf2, - 0x6f, 0xcd, 0xf9, 0x72, 0x37, 0xb5, 0x47, 0xd7, 0x7e, 0xec, 0xac, 0x6a, 0x8f, 0xab, 0x8f, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x54, 0x8e, 0xdc, 0xb1, 0x00, 0x02, 0x00, 0x00, + 0x6d, 0x77, 0xa5, 0x53, 0x08, 0x26, 0x26, 0x7e, 0x74, 0xc3, 0x22, 0x45, 0x4f, 0xea, 0x69, 0xe7, + 0xfd, 0x76, 0xf2, 0x66, 0xf6, 0x2d, 0x9c, 0x99, 0x97, 0xf9, 0xc0, 0x2c, 0x34, 0xeb, 0xc1, 0x72, + 0x38, 0x60, 0x99, 0x27, 0xd3, 0x34, 0x54, 0x71, 0x26, 0x2c, 0xc3, 0xf3, 0x48, 0x27, 0x42, 0xaf, + 0xd2, 0x28, 0xd6, 0xf9, 0x4c, 0xe8, 0x48, 0x65, 0xe2, 0x5b, 0xcf, 0x72, 0xd8, 0xfb, 0x28, 0x01, + 0xf8, 0xc4, 0x01, 0xbd, 0xe6, 0x94, 0x31, 0xb6, 0xa1, 0xfe, 0xac, 0x62, 0x32, 0x21, 0x4b, 0xcf, + 0xe9, 0x3a, 0xfd, 0x46, 0x50, 0x68, 0x1c, 0x6f, 0xef, 0x78, 0x6d, 0xc8, 0x2b, 0x75, 0x9d, 0xfe, + 0xf1, 0xe8, 0x46, 0xfc, 0x62, 0x2f, 0xf6, 0xd6, 0xe2, 0x4e, 0xc5, 0x34, 0x59, 0x1b, 0x0a, 0x0a, + 0x17, 0x44, 0xa8, 0x10, 0x87, 0x73, 0xaf, 0x6c, 0x27, 0xd9, 0x1a, 0x4f, 0xc0, 0x5d, 0xa9, 0x19, + 0x4b, 0xaf, 0xd2, 0x75, 0xfa, 0x6e, 0xb0, 0x15, 0x78, 0x0a, 0x55, 0x49, 0x6a, 0x2e, 0xd9, 0x73, + 0x2d, 0xfe, 0x52, 0x78, 0x01, 0x47, 0x61, 0xce, 0x52, 0x2f, 0xd4, 0x5b, 0xc8, 0x4a, 0xa7, 0x5e, + 0xd5, 0x5a, 0xfd, 0x84, 0xbd, 0x0e, 0xd4, 0x77, 0xd3, 0xb1, 0x06, 0xe5, 0xf1, 0x83, 0xdf, 0x3a, + 0xd8, 0x14, 0xf7, 0x63, 0xbf, 0xe5, 0xf4, 0x7c, 0x38, 0xb4, 0x6b, 0x66, 0x46, 0xa7, 0x19, 0x61, + 0x07, 0x1a, 0xc5, 0x1b, 0x6c, 0x06, 0xcd, 0x60, 0x0f, 0x36, 0x01, 0x25, 0x2a, 0xd9, 0x87, 0xd0, + 0x08, 0x0a, 0x3d, 0x7a, 0x87, 0xd6, 0x64, 0xd7, 0xf8, 0x48, 0x8b, 0xa5, 0x8a, 0x08, 0x15, 0x34, + 0x7d, 0xe2, 0x02, 0xe3, 0xe5, 0x3f, 0x22, 0x6b, 0x5f, 0xfd, 0xad, 0x79, 0xbb, 0xf8, 0x6d, 0xed, + 0xc9, 0xb5, 0x9f, 0x3e, 0xad, 0xda, 0xe3, 0xfa, 0x33, 0x00, 0x00, 0xff, 0xff, 0xb1, 0x29, 0x25, + 0xcc, 0x1c, 0x02, 0x00, 0x00, } diff --git a/pkg/proto/v0/thumbnails.proto b/pkg/proto/v0/thumbnails.proto index 94f252914..261a65b13 100644 --- a/pkg/proto/v0/thumbnails.proto +++ b/pkg/proto/v0/thumbnails.proto @@ -22,4 +22,5 @@ message GetRequest { message GetResponse { bytes thumbnail = 1; + string mimetype = 2; } \ No newline at end of file diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index b9a6700ac..3a2f973ac 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -55,6 +55,7 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rs thumbnail := g.manager.GetStored(tCtx) if thumbnail != nil { rsp.Thumbnail = thumbnail + rsp.Mimetype = tCtx.Encoder.MimeType() return nil } @@ -74,5 +75,6 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rs } rsp.Thumbnail = thumbnail + rsp.Mimetype = tCtx.Encoder.MimeType() return nil } diff --git a/pkg/thumbnails/encoding.go b/pkg/thumbnails/encoding.go index 8deb12871..a4d4647bb 100644 --- a/pkg/thumbnails/encoding.go +++ b/pkg/thumbnails/encoding.go @@ -14,6 +14,8 @@ type Encoder interface { Encode(io.Writer, image.Image) error // Types returns the formats suffixes. Types() []string + // MimeType returns the mimetype used by the encoder. + MimeType() string } // PngEncoder encodes to png @@ -29,6 +31,11 @@ func (e PngEncoder) Types() []string { return []string{"png"} } +// MimeType returns the mimetype for png files. +func (e PngEncoder) MimeType() string { + return "image/png" +} + // JpegEncoder encodes to jpg. type JpegEncoder struct{} @@ -42,6 +49,11 @@ func (e JpegEncoder) Types() []string { return []string{"jpeg", "jpg"} } +// MimeType returns the mimetype for jpg files. +func (e JpegEncoder) MimeType() string { + return "image/jpeg" +} + // EncoderForType returns the encoder for a given file type // or nil if the type is not supported. func EncoderForType(fileType string) Encoder { From e0ab0b37a5a45423483070c8babff7759f6c5c7b Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 18 Mar 2020 13:15:05 +0000 Subject: [PATCH 28/80] Automated changelog update [skip ci] --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f8ea9e43..52142f119 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Summary * Change - Initial release of basic version: [#1](https://github.com/owncloud/ocis-thumbnails/issues/1) +* Change - Implement the first working version: [#3](https://github.com/owncloud/ocis-thumbnails/pull/3) ## Details @@ -13,3 +14,11 @@ https://github.com/owncloud/ocis-thumbnails/issues/1 + +* Change - Implement the first working version: [#3](https://github.com/owncloud/ocis-thumbnails/pull/3) + + We implemented the first simple version. It can load images via webdav and store them locally in + the filesystem. + + https://github.com/owncloud/ocis-thumbnails/pull/3 + From 43bf1072077e1f452d64c8852b368975928d212f Mon Sep 17 00:00:00 2001 From: David Christofas Date: Sat, 21 Mar 2020 18:50:18 +0100 Subject: [PATCH 29/80] implement a resolution handler --- pkg/thumbnails/resolution/resolution.go | 37 ++++++++ pkg/thumbnails/resolution/resolution_test.go | 26 ++++++ pkg/thumbnails/resolution/resolutions.go | 58 ++++++++++++ pkg/thumbnails/resolution/resolutions_test.go | 91 +++++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 pkg/thumbnails/resolution/resolution.go create mode 100644 pkg/thumbnails/resolution/resolution_test.go create mode 100644 pkg/thumbnails/resolution/resolutions.go create mode 100644 pkg/thumbnails/resolution/resolutions_test.go diff --git a/pkg/thumbnails/resolution/resolution.go b/pkg/thumbnails/resolution/resolution.go new file mode 100644 index 000000000..514fe8574 --- /dev/null +++ b/pkg/thumbnails/resolution/resolution.go @@ -0,0 +1,37 @@ +package resolution + +import ( + "fmt" + "strconv" + "strings" +) + +// Parse parses a resolution string in the form x and returns a resolution instance. +func Parse(s string) (Resolution, error) { + parts := strings.Split(s, "x") + if len(parts) != 2 { + return Resolution{}, fmt.Errorf("failed to parse resolution: %s. Expected format x", s) + } + width, err := strconv.Atoi(parts[0]) + if err != nil { + return Resolution{}, fmt.Errorf("width: %s has an invalid value. Expected an integer", parts[0]) + } + height, err := strconv.Atoi(parts[1]) + if err != nil { + return Resolution{}, fmt.Errorf("height: %s has an invalid value. Expected an integer", parts[1]) + } + return Resolution{Width: width, Height: height}, nil +} + +// Resolution defines represents the width and height of a thumbnail. +type Resolution struct { + Width int + Height int +} + +// String returns the resolution in the format: +// +// x +func (r Resolution) String() string { + return strconv.Itoa(r.Width) + "x" + strconv.Itoa(r.Height) +} diff --git a/pkg/thumbnails/resolution/resolution_test.go b/pkg/thumbnails/resolution/resolution_test.go new file mode 100644 index 000000000..18d6e4676 --- /dev/null +++ b/pkg/thumbnails/resolution/resolution_test.go @@ -0,0 +1,26 @@ +package resolution + +import "testing" + +func TestParseWithEmptyString(t *testing.T) { + _, err := Parse("") + if err == nil { + t.Error("Parse with empty string should return an error.") + } +} + +func TestParse(t *testing.T) { + rStr := "42x23" + r, _ := Parse(rStr) + if r.Width != 42 || r.Height != 23 { + t.Errorf("Expected resolution %s got %s", rStr, r.String()) + } +} + +func TestString(t *testing.T) { + r := Resolution{Width: 42, Height: 23} + expected := "42x23" + if r.String() != expected { + t.Errorf("Expected string %s got %s", expected, r.String()) + } +} diff --git a/pkg/thumbnails/resolution/resolutions.go b/pkg/thumbnails/resolution/resolutions.go new file mode 100644 index 000000000..a64bd9eed --- /dev/null +++ b/pkg/thumbnails/resolution/resolutions.go @@ -0,0 +1,58 @@ +package resolution + +import ( + "fmt" + "math" +) + +// Init creates an instance of Resolutions from resolution strings. +func Init(rStrs []string) (Resolutions, error) { + var rs Resolutions + for _, rStr := range rStrs { + r, err := Parse(rStr) + if err != nil { + return nil, fmt.Errorf("failed to initialize resolutions: %s", err.Error()) + } + rs = append(rs, r) + } + return rs, nil +} + +// Resolutions represents the available thumbnail resolutions. +type Resolutions []Resolution + +// ClosestMatch returns the resolution which is closest to the provided resolution. +func (r Resolutions) ClosestMatch(width, height int) Resolution { + if len(r) == 0 { + return Resolution{Width: width, Height: height} + } + + isLandscape := width > height + givenLen := math.Max(float64(width), float64(height)) + + // Initialize with the first resolution + match := r[0] + matchLen := dimensionLength(match, isLandscape) + minDiff := math.Abs(givenLen - float64(matchLen)) + + for i := 1; i < len(r); i++ { + r := r[i] + rLen := dimensionLength(r, isLandscape) + diff := math.Abs(givenLen - float64(rLen)) + + if diff <= minDiff { + minDiff = diff + match = r + continue + } + } + + return match +} + +func dimensionLength(r Resolution, landscape bool) int { + if landscape { + return r.Width + } + return r.Height +} diff --git a/pkg/thumbnails/resolution/resolutions_test.go b/pkg/thumbnails/resolution/resolutions_test.go new file mode 100644 index 000000000..37b0e8367 --- /dev/null +++ b/pkg/thumbnails/resolution/resolutions_test.go @@ -0,0 +1,91 @@ +package resolution + +import ( + "testing" +) + +func TestInitWithEmptyArray(t *testing.T) { + rs, err := Init([]string{}) + if err != nil { + t.Errorf("Init with an empty array should not fail. Error: %s.\n", err.Error()) + } + if len(rs) != 0 { + t.Error("Init with an empty array should return an empty Resolutions instance.\n") + } +} + +func TestInitWithNil(t *testing.T) { + rs, err := Init(nil) + if err != nil { + t.Errorf("Init with nil parameter should not fail. Error: %s.\n", err.Error()) + } + if len(rs) != 0 { + t.Error("Init with nil parameter should return an empty Resolutions instance.\n") + } +} + +func TestInitWithInvalidValuesInArray(t *testing.T) { + _, err := Init([]string{"invalid"}) + if err == nil { + t.Error("Init with invalid parameter should fail.\n") + } +} + +func TestInit(t *testing.T) { + rs, err := Init([]string{"16x16"}) + if err != nil { + t.Errorf("Init with valid parameter should not fail. Error: %s.\n", err.Error()) + } + if len(rs) != 1 { + t.Errorf("resolutions has size %d, expected size %d.\n", len(rs), 1) + } +} + +func TestInitWithMultipleResolutions(t *testing.T) { + rStrs := []string{"16x16", "32x32", "64x64", "128x128"} + rs, err := Init(rStrs) + if err != nil { + t.Errorf("Init with valid parameter should not fail. Error: %s.\n", err.Error()) + } + if len(rs) != len(rStrs) { + t.Errorf("resolutions has size %d, expected size %d.\n", len(rs), len(rStrs)) + } +} + +func TestClosestMatchWithEmptyResolutions(t *testing.T) { + rs, _ := Init(nil) + width := 24 + height := 24 + + r := rs.ClosestMatch(width, height) + if r.Width != width || r.Height != height { + t.Errorf("ClosestMatch from empty resolutions should return the given resolution") + } +} + +func TestClosestMatch(t *testing.T) { + rs, _ := Init([]string{"16x16", "24x24", "32x32", "64x64", "128x128"}) + table := [][]int{ + []int{17, 17, 16, 16}, + []int{12, 17, 16, 16}, + []int{24, 24, 24, 24}, + []int{20, 20, 24, 24}, + []int{20, 80, 64, 64}, + []int{80, 20, 64, 64}, + []int{48, 48, 64, 64}, + []int{1024, 1024, 128, 128}, + } + + for _, row := range table { + width := row[0] + height := row[1] + expectedWidth := row[2] + expectedHeight := row[3] + + match := rs.ClosestMatch(width, height) + + if match.Width != expectedWidth || match.Height != expectedHeight { + t.Errorf("Expected resolution %dx%d got %s", expectedWidth, expectedHeight, match.String()) + } + } +} From 3de4584a45f7c3fe9a1ebd8519b22a23157b0c55 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 23 Mar 2020 11:52:33 +0100 Subject: [PATCH 30/80] use predefined resolutions for thumbnail generation --- pkg/command/server.go | 2 +- pkg/config/config.go | 20 ++++++---- pkg/flagset/flagset.go | 10 ++++- pkg/server/grpc/server.go | 1 + pkg/service/v0/service.go | 35 ++++++++++------- pkg/thumbnails/resolution/resolution_test.go | 14 +++++++ pkg/thumbnails/resolution/resolutions.go | 39 +++++++++++++------ pkg/thumbnails/resolution/resolutions_test.go | 27 +++++++++++-- pkg/thumbnails/storage/filesystem.go | 3 +- pkg/thumbnails/storage/inmemory.go | 2 +- pkg/thumbnails/storage/storage.go | 9 +++-- pkg/thumbnails/thumbnails.go | 19 +++++---- 12 files changed, 125 insertions(+), 56 deletions(-) diff --git a/pkg/command/server.go b/pkg/command/server.go index af6cafecf..9967701ef 100644 --- a/pkg/command/server.go +++ b/pkg/command/server.go @@ -29,11 +29,11 @@ func Server(cfg *config.Config) *cli.Command { Usage: "Start integrated server", Flags: flagset.ServerWithConfig(cfg), Before: func(c *cli.Context) error { + cfg.Thumbnail.Resolutions = c.StringSlice("thumbnail-resolution") return nil }, Action: func(c *cli.Context) error { logger := NewLogger(cfg) - if cfg.Tracing.Enabled { switch t := cfg.Tracing.Type; t { case "agent": diff --git a/pkg/config/config.go b/pkg/config/config.go index e84b514b9..37286518a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -33,13 +33,12 @@ type Tracing struct { // Config combines all available configuration parts. type Config struct { - File string - Log Log - Debug Debug - Server Server - Tracing Tracing - FileSystemStorage FileSystemStorage - WebDavSource WebDavSource + File string + Log Log + Debug Debug + Server Server + Tracing Tracing + Thumbnail Thumbnail } // FileSystemStorage defines the available filesystem storage configuration. @@ -52,6 +51,13 @@ type WebDavSource struct { BaseURL string } +// Thumbnail defines the available thumbnail related configuration. +type Thumbnail struct { + Resolutions []string + FileSystemStorage FileSystemStorage + WebDavSource WebDavSource +} + // New initializes a new configuration with or without defaults. func New() *Config { return &Config{} diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 2f34817b1..19b3f9506 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -142,14 +142,20 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { Value: filepath.Join(os.TempDir(), "ocis-thumbnails/"), Usage: "Root path of the filesystem storage directory", EnvVars: []string{"THUMBNAILS_FILESYSTEMSTORAGE_ROOT"}, - Destination: &cfg.FileSystemStorage.RootDirectory, + Destination: &cfg.Thumbnail.FileSystemStorage.RootDirectory, }, &cli.StringFlag{ Name: "webdavsource-baseurl", Value: "http://localhost:9140/remote.php/webdav/", Usage: "Base url for a webdav api", EnvVars: []string{"THUMBNAILS_WEBDAVSOURCE_BASEURL"}, - Destination: &cfg.WebDavSource.BaseURL, + Destination: &cfg.Thumbnail.WebDavSource.BaseURL, + }, + &cli.StringSliceFlag{ + Name: "thumbnail-resolution", + Value: cli.NewStringSlice("16x16", "32x32", "64x64", "128x128"), + Usage: "--thumbnail-resolution 16x16 [--thumbnail-resolution 32x32]", + EnvVars: []string{"THUMBNAILS_RESOLUTIONs"}, }, } } diff --git a/pkg/server/grpc/server.go b/pkg/server/grpc/server.go index 96c64e1d0..37a79b755 100644 --- a/pkg/server/grpc/server.go +++ b/pkg/server/grpc/server.go @@ -24,6 +24,7 @@ func NewService(opts ...Option) grpc.Service { { thumbnail = svc.NewService( svc.Config(options.Config), + svc.Logger(options.Logger), ) thumbnail = svc.NewInstrument(thumbnail, options.Metrics) thumbnail = svc.NewLogging(thumbnail, options.Logger) diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 3a2f973ac..faf22ad1e 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -8,23 +8,29 @@ import ( v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/imgsource" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolution" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" ) // NewService returns a service implementation for Service. func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { options := newOptions(opts...) - + logger := options.Logger + resolutions, err := resolution.Init(options.Config.Thumbnail.Resolutions) + if err != nil { + logger.Fatal().Err(err).Msg("resolutions not configured correctly") + } svc := Thumbnail{ manager: thumbnails.NewSimpleManager( storage.NewFileSystemStorage( - options.Config.FileSystemStorage, - options.Logger, + options.Config.Thumbnail.FileSystemStorage, + logger, ), - options.Logger, + logger, ), - source: imgsource.NewWebDavSource(options.Config.WebDavSource), - logger: options.Logger, + resolutions: resolutions, + source: imgsource.NewWebDavSource(options.Config.Thumbnail.WebDavSource), + logger: logger, } return svc @@ -32,9 +38,10 @@ func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { // Thumbnail implements the GRPC handler. type Thumbnail struct { - manager thumbnails.Manager - source imgsource.Source - logger log.Logger + manager thumbnails.Manager + resolutions resolution.Resolutions + source imgsource.Source + logger log.Logger } // GetThumbnail retrieves a thumbnail for an image @@ -44,12 +51,12 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rs // TODO: better error responses return fmt.Errorf("can't be encoded. filetype %s not supported", req.Filetype.String()) } + r := g.resolutions.ClosestMatch(int(req.Width), int(req.Height)) tCtx := thumbnails.Context{ - Width: int(req.Width), - Height: int(req.Height), - ImagePath: req.Filepath, - Encoder: encoder, - ETag: req.Etag, + Resolution: r, + ImagePath: req.Filepath, + Encoder: encoder, + ETag: req.Etag, } thumbnail := g.manager.GetStored(tCtx) diff --git a/pkg/thumbnails/resolution/resolution_test.go b/pkg/thumbnails/resolution/resolution_test.go index 18d6e4676..b8c3e6c88 100644 --- a/pkg/thumbnails/resolution/resolution_test.go +++ b/pkg/thumbnails/resolution/resolution_test.go @@ -9,6 +9,20 @@ func TestParseWithEmptyString(t *testing.T) { } } +func TestParseWithInvalidWidth(t *testing.T) { + _, err := Parse("invalidx42") + if err == nil { + t.Error("Parse with invalid width should return an error.") + } +} + +func TestParseWithInvalidHeight(t *testing.T) { + _, err := Parse("42xinvalid") + if err == nil { + t.Error("Parse with invalid height should return an error.") + } +} + func TestParse(t *testing.T) { rStr := "42x23" r, _ := Parse(rStr) diff --git a/pkg/thumbnails/resolution/resolutions.go b/pkg/thumbnails/resolution/resolutions.go index a64bd9eed..c17b47135 100644 --- a/pkg/thumbnails/resolution/resolutions.go +++ b/pkg/thumbnails/resolution/resolutions.go @@ -3,6 +3,7 @@ package resolution import ( "fmt" "math" + "sort" ) // Init creates an instance of Resolutions from resolution strings. @@ -15,6 +16,16 @@ func Init(rStrs []string) (Resolutions, error) { } rs = append(rs, r) } + sort.Slice(rs, func(i, j int) bool { + left := rs[i] + right := rs[j] + + leftSize := left.Width * left.Height + rightSize := right.Width * right.Height + + return leftSize < rightSize + }) + return rs, nil } @@ -22,31 +33,37 @@ func Init(rStrs []string) (Resolutions, error) { type Resolutions []Resolution // ClosestMatch returns the resolution which is closest to the provided resolution. +// If there is no exact match the resolution will be the next higher one. +// If the given resolution is bigger than all available resolutions the biggest available one is used. func (r Resolutions) ClosestMatch(width, height int) Resolution { if len(r) == 0 { return Resolution{Width: width, Height: height} } isLandscape := width > height - givenLen := math.Max(float64(width), float64(height)) + givenLen := int(math.Max(float64(width), float64(height))) // Initialize with the first resolution - match := r[0] - matchLen := dimensionLength(match, isLandscape) - minDiff := math.Abs(givenLen - float64(matchLen)) + var match Resolution + minDiff := math.MaxInt32 for i := 1; i < len(r); i++ { - r := r[i] - rLen := dimensionLength(r, isLandscape) - diff := math.Abs(givenLen - float64(rLen)) - - if diff <= minDiff { - minDiff = diff - match = r + current := r[i] + len := dimensionLength(current, isLandscape) + diff := givenLen - len + if diff > 0 { continue } + absDiff := int(math.Abs(float64(diff))) + if absDiff < minDiff { + minDiff = absDiff + match = current + } } + if match == (Resolution{}) { + match = r[len(r)-1] + } return match } diff --git a/pkg/thumbnails/resolution/resolutions_test.go b/pkg/thumbnails/resolution/resolutions_test.go index 37b0e8367..4ec67c43d 100644 --- a/pkg/thumbnails/resolution/resolutions_test.go +++ b/pkg/thumbnails/resolution/resolutions_test.go @@ -52,6 +52,25 @@ func TestInitWithMultipleResolutions(t *testing.T) { } } +func TestInitWithMultipleResolutionsShouldBeSorted(t *testing.T) { + rStrs := []string{"32x32", "64x64", "16x16", "128x128"} + rs, err := Init(rStrs) + if err != nil { + t.Errorf("Init with valid parameter should not fail. Error: %s.\n", err.Error()) + } + + for i := 0; i < len(rs)-1; i++ { + current := rs[i] + currentSize := current.Width * current.Height + next := rs[i] + nextSize := next.Width * next.Height + + if currentSize > nextSize { + t.Error("Resolutions are not sorted.") + } + + } +} func TestClosestMatchWithEmptyResolutions(t *testing.T) { rs, _ := Init(nil) width := 24 @@ -66,12 +85,12 @@ func TestClosestMatchWithEmptyResolutions(t *testing.T) { func TestClosestMatch(t *testing.T) { rs, _ := Init([]string{"16x16", "24x24", "32x32", "64x64", "128x128"}) table := [][]int{ - []int{17, 17, 16, 16}, - []int{12, 17, 16, 16}, + []int{17, 17, 24, 24}, + []int{12, 17, 24, 24}, []int{24, 24, 24, 24}, []int{20, 20, 24, 24}, - []int{20, 80, 64, 64}, - []int{80, 20, 64, 64}, + []int{20, 80, 128, 128}, + []int{80, 20, 128, 128}, []int{48, 48, 64, 64}, []int{1024, 1024, 128, 128}, } diff --git a/pkg/thumbnails/storage/filesystem.go b/pkg/thumbnails/storage/filesystem.go index e59b77170..c8f604245 100644 --- a/pkg/thumbnails/storage/filesystem.go +++ b/pkg/thumbnails/storage/filesystem.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "os" "path/filepath" - "strconv" "github.com/owncloud/ocis-pkg/v2/log" "github.com/owncloud/ocis-thumbnails/pkg/config" @@ -68,7 +67,7 @@ func (s FileSystem) Set(key string, img []byte) error { func (s FileSystem) BuildKey(ctx Context) string { etag := ctx.ETag filetype := ctx.Types[0] - filename := strconv.Itoa(ctx.Width) + "x" + strconv.Itoa(ctx.Height) + "." + filetype + filename := ctx.Resolution.String() + "." + filetype key := new(bytes.Buffer) key.WriteString(etag[:2]) diff --git a/pkg/thumbnails/storage/inmemory.go b/pkg/thumbnails/storage/inmemory.go index c8f02a9ae..74a78befd 100644 --- a/pkg/thumbnails/storage/inmemory.go +++ b/pkg/thumbnails/storage/inmemory.go @@ -32,7 +32,7 @@ func (s InMemory) Set(key string, thumbnail []byte) error { func (s InMemory) BuildKey(ctx Context) string { parts := []string{ ctx.ETag, - string(ctx.Width) + "x" + string(ctx.Height), + ctx.Resolution.String(), strings.Join(ctx.Types, ","), } return strings.Join(parts, "+") diff --git a/pkg/thumbnails/storage/storage.go b/pkg/thumbnails/storage/storage.go index 8f5f3d9de..68de3c9ea 100644 --- a/pkg/thumbnails/storage/storage.go +++ b/pkg/thumbnails/storage/storage.go @@ -1,11 +1,12 @@ package storage +import "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolution" + // Context combines different attributes needed for storage operations. type Context struct { - ETag string - Types []string - Width int - Height int + ETag string + Types []string + Resolution resolution.Resolution } // Storage defines the interface for a thumbnail store. diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go index 76223228d..289d03462 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnails/thumbnails.go @@ -6,16 +6,16 @@ import ( "github.com/nfnt/resize" "github.com/owncloud/ocis-pkg/v2/log" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolution" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" ) // Context bundles information needed to generate a thumbnail for afile type Context struct { - Width int - Height int - ImagePath string - Encoder Encoder - ETag string + Resolution resolution.Resolution + ImagePath string + Encoder Encoder + ETag string } // Manager is responsible for generating thumbnails @@ -69,16 +69,15 @@ func (s SimpleManager) GetStored(ctx Context) []byte { } func (s SimpleManager) generate(ctx Context, img image.Image) image.Image { - thumbnail := resize.Thumbnail(uint(ctx.Width), uint(ctx.Height), img, resize.Lanczos2) + thumbnail := resize.Thumbnail(uint(ctx.Resolution.Width), uint(ctx.Resolution.Height), img, resize.Lanczos2) return thumbnail } func mapToStorageContext(ctx Context) storage.Context { sCtx := storage.Context{ - ETag: ctx.ETag, - Width: ctx.Width, - Height: ctx.Height, - Types: ctx.Encoder.Types(), + ETag: ctx.ETag, + Resolution: ctx.Resolution, + Types: ctx.Encoder.Types(), } return sCtx } From 3daf985f4cfca6397346d5ab821ec5e2b1620b07 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 23 Mar 2020 12:00:04 +0100 Subject: [PATCH 31/80] add changelog --- changelog/unreleased/predefined-resolutions.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog/unreleased/predefined-resolutions.md diff --git a/changelog/unreleased/predefined-resolutions.md b/changelog/unreleased/predefined-resolutions.md new file mode 100644 index 000000000..ce8d3e198 --- /dev/null +++ b/changelog/unreleased/predefined-resolutions.md @@ -0,0 +1,6 @@ +Change: Use predefined resolutions for thumbnail generation + +We implemented predefined resolutions to prevent attacker from flooding the service with a large number of thumbnails. +The requested resolution gets mapped to the closest matching predefined resolution. + +https://github.com/owncloud/ocis-thumbnails/issues/7 From eb820e1ab79fd8af870b85bcd6a6550648bce9db Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 24 Mar 2020 11:05:08 +0100 Subject: [PATCH 32/80] add comment to test structure --- pkg/thumbnails/resolution/resolutions_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/thumbnails/resolution/resolutions_test.go b/pkg/thumbnails/resolution/resolutions_test.go index 4ec67c43d..cb54e096c 100644 --- a/pkg/thumbnails/resolution/resolutions_test.go +++ b/pkg/thumbnails/resolution/resolutions_test.go @@ -85,6 +85,7 @@ func TestClosestMatchWithEmptyResolutions(t *testing.T) { func TestClosestMatch(t *testing.T) { rs, _ := Init([]string{"16x16", "24x24", "32x32", "64x64", "128x128"}) table := [][]int{ + // width, height, expectedWidth, expectedHeight []int{17, 17, 24, 24}, []int{12, 17, 24, 24}, []int{24, 24, 24, 24}, From cfd6b9cf91d0ebd99bc834b8dd390a69ac4798a6 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 25 Mar 2020 11:56:39 +0100 Subject: [PATCH 33/80] fix type in flagset --- pkg/flagset/flagset.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 19b3f9506..60126809c 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -155,7 +155,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { Name: "thumbnail-resolution", Value: cli.NewStringSlice("16x16", "32x32", "64x64", "128x128"), Usage: "--thumbnail-resolution 16x16 [--thumbnail-resolution 32x32]", - EnvVars: []string{"THUMBNAILS_RESOLUTIONs"}, + EnvVars: []string{"THUMBNAILS_RESOLUTIONS"}, }, } } From 01672d1da56181034491b3c3104beb497c3b9986 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 25 Mar 2020 11:53:59 +0100 Subject: [PATCH 34/80] update documentation --- docs/_index.md | 2 +- docs/getting-started.md | 48 ++++++++++++++++++++++++++++------------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/docs/_index.md b/docs/_index.md index 1ca7e5d18..1007ebc39 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -7,4 +7,4 @@ geekdocEditPath: edit/master/docs geekdocFilePath: _index.md --- -This service provides ... +This service provides an ocis extensions which generates thumbnails for image files. diff --git a/docs/getting-started.md b/docs/getting-started.md index 07394b2a6..fc8988afc 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -72,19 +72,28 @@ THUMBNAILS_DEBUG_PPROF THUMBNAILS_DEBUG_ZPAGES : Enable zpages debugging, defaults to `false` -THUMBNAILS_HTTP_ADDR -: Address to bind http server, defaults to `0.0.0.0:9110` +THUMBNAILS_GRPC_NAME +: Name of the service, defaults to `thumbnails` -THUMBNAILS_HTTP_NAMESPACE -: The http namespace +THUMBNAILS_GRPC_ADDR +: Address to bind grpc server, defaults to `0.0.0.0:9185` -THUMBNAILS_HTTP_ROOT -: Root path of http server, defaults to `/` +THUMBNAILS_GRPC_NAMESPACE +: Set the base namespace for the grpc namespace", defaults to `com.owncloud.api` + +THUMBNAILS_FILESYSTEMSTORAGE_ROOT +: Root path of the filesystem storage directory, defaults to `/ocis-thumbnails/` + +THUMBNAILS_WEBDAVSOURCE_BASEURL +: Base url for a webdav api, defaults to `htp://localhost:9140/remote.php/webdav/` + +THUMBNAILS_RESOLUTIONS +: List of resolutions supported by the service, defaults to `["16x16", "32x32", "64x64", "128x128"] #### Health THUMBNAILS_DEBUG_ADDR -: Address to debug endpoint, defaults to `0.0.0.0:9114` +: Address to debug endpoint, defaults to `0.0.0.0:9189` ### Commandline flags @@ -122,7 +131,7 @@ If you prefer to configure the service with commandline flags you can see the av : Service name for tracing, defaults to `thumbnails` --debug-addr -: Address to bind debug server, defaults to `0.0.0.0:9114` +: Address to bind debug server, defaults to `0.0.0.0:9189` --debug-token : Token to grant metrics access, empty default value @@ -133,19 +142,28 @@ If you prefer to configure the service with commandline flags you can see the av --debug-zpages : Enable zpages debugging, defaults to `false` ---http-addr -: Address to bind http server, defaults to `0.0.0.0:9110` +--grpc-name +: Name of the service, defaults to `thumbnails` ---http-namespace -: Namespace for internal services communication, defaults to `com.owncloud.web` +--grpc-addr +: Address to bind grpc server, defaults to `0.0.0.0:9185` ---http-root -: Root path of http server, defaults to `/` +--grpc-namespace +: Set the base namespace for the grpc namespace", defaults to `com.owncloud.api` + +--filesystemstorage-root +: Root path of the filesystem storage directory, defaults to `/ocis-thumbnails/` + +--webdavsource-baseurl +: Base url for a webdav api, defaults to `htp://localhost:9140/remote.php/webdav/` + +--thumbnail-resolution +: List of resolutions supported by the service, defaults to `["16x16", "32x32", "64x64", "128x128"] #### Health --debug-addr -: Address to debug endpoint, defaults to `0.0.0.0:9114` +: Address to debug endpoint, defaults to `0.0.0.0:9189` ### Configuration file From 39ea10a88637c86dcd92a4fc98018b4ef49bf6eb Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 25 Mar 2020 14:37:13 +0100 Subject: [PATCH 35/80] small refactorings --- pkg/service/v0/service.go | 6 +++--- .../{resolution => resolutions}/resolution.go | 2 +- .../resolution_test.go | 2 +- .../{resolution => resolutions}/resolutions.go | 9 ++++----- .../resolutions_test.go | 18 +++++++++--------- pkg/thumbnails/storage/storage.go | 4 ++-- pkg/thumbnails/thumbnails.go | 4 ++-- 7 files changed, 22 insertions(+), 23 deletions(-) rename pkg/thumbnails/{resolution => resolutions}/resolution.go (98%) rename pkg/thumbnails/{resolution => resolutions}/resolution_test.go (97%) rename pkg/thumbnails/{resolution => resolutions}/resolutions.go (89%) rename pkg/thumbnails/{resolution => resolutions}/resolutions_test.go (90%) diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index faf22ad1e..c480d8ac1 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -8,7 +8,7 @@ import ( v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/imgsource" - "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolution" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolutions" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" ) @@ -16,7 +16,7 @@ import ( func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { options := newOptions(opts...) logger := options.Logger - resolutions, err := resolution.Init(options.Config.Thumbnail.Resolutions) + resolutions, err := resolutions.New(options.Config.Thumbnail.Resolutions) if err != nil { logger.Fatal().Err(err).Msg("resolutions not configured correctly") } @@ -39,7 +39,7 @@ func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { // Thumbnail implements the GRPC handler. type Thumbnail struct { manager thumbnails.Manager - resolutions resolution.Resolutions + resolutions resolutions.Resolutions source imgsource.Source logger log.Logger } diff --git a/pkg/thumbnails/resolution/resolution.go b/pkg/thumbnails/resolutions/resolution.go similarity index 98% rename from pkg/thumbnails/resolution/resolution.go rename to pkg/thumbnails/resolutions/resolution.go index 514fe8574..14acab7e3 100644 --- a/pkg/thumbnails/resolution/resolution.go +++ b/pkg/thumbnails/resolutions/resolution.go @@ -1,4 +1,4 @@ -package resolution +package resolutions import ( "fmt" diff --git a/pkg/thumbnails/resolution/resolution_test.go b/pkg/thumbnails/resolutions/resolution_test.go similarity index 97% rename from pkg/thumbnails/resolution/resolution_test.go rename to pkg/thumbnails/resolutions/resolution_test.go index b8c3e6c88..ae407163f 100644 --- a/pkg/thumbnails/resolution/resolution_test.go +++ b/pkg/thumbnails/resolutions/resolution_test.go @@ -1,4 +1,4 @@ -package resolution +package resolutions import "testing" diff --git a/pkg/thumbnails/resolution/resolutions.go b/pkg/thumbnails/resolutions/resolutions.go similarity index 89% rename from pkg/thumbnails/resolution/resolutions.go rename to pkg/thumbnails/resolutions/resolutions.go index c17b47135..c6858aacf 100644 --- a/pkg/thumbnails/resolution/resolutions.go +++ b/pkg/thumbnails/resolutions/resolutions.go @@ -1,4 +1,4 @@ -package resolution +package resolutions import ( "fmt" @@ -6,8 +6,8 @@ import ( "sort" ) -// Init creates an instance of Resolutions from resolution strings. -func Init(rStrs []string) (Resolutions, error) { +// New creates an instance of Resolutions from resolution strings. +func New(rStrs []string) (Resolutions, error) { var rs Resolutions for _, rStr := range rStrs { r, err := Parse(rStr) @@ -47,8 +47,7 @@ func (r Resolutions) ClosestMatch(width, height int) Resolution { var match Resolution minDiff := math.MaxInt32 - for i := 1; i < len(r); i++ { - current := r[i] + for _, current := range r { len := dimensionLength(current, isLandscape) diff := givenLen - len if diff > 0 { diff --git a/pkg/thumbnails/resolution/resolutions_test.go b/pkg/thumbnails/resolutions/resolutions_test.go similarity index 90% rename from pkg/thumbnails/resolution/resolutions_test.go rename to pkg/thumbnails/resolutions/resolutions_test.go index cb54e096c..9a7583c56 100644 --- a/pkg/thumbnails/resolution/resolutions_test.go +++ b/pkg/thumbnails/resolutions/resolutions_test.go @@ -1,11 +1,11 @@ -package resolution +package resolutions import ( "testing" ) func TestInitWithEmptyArray(t *testing.T) { - rs, err := Init([]string{}) + rs, err := New([]string{}) if err != nil { t.Errorf("Init with an empty array should not fail. Error: %s.\n", err.Error()) } @@ -15,7 +15,7 @@ func TestInitWithEmptyArray(t *testing.T) { } func TestInitWithNil(t *testing.T) { - rs, err := Init(nil) + rs, err := New(nil) if err != nil { t.Errorf("Init with nil parameter should not fail. Error: %s.\n", err.Error()) } @@ -25,14 +25,14 @@ func TestInitWithNil(t *testing.T) { } func TestInitWithInvalidValuesInArray(t *testing.T) { - _, err := Init([]string{"invalid"}) + _, err := New([]string{"invalid"}) if err == nil { t.Error("Init with invalid parameter should fail.\n") } } func TestInit(t *testing.T) { - rs, err := Init([]string{"16x16"}) + rs, err := New([]string{"16x16"}) if err != nil { t.Errorf("Init with valid parameter should not fail. Error: %s.\n", err.Error()) } @@ -43,7 +43,7 @@ func TestInit(t *testing.T) { func TestInitWithMultipleResolutions(t *testing.T) { rStrs := []string{"16x16", "32x32", "64x64", "128x128"} - rs, err := Init(rStrs) + rs, err := New(rStrs) if err != nil { t.Errorf("Init with valid parameter should not fail. Error: %s.\n", err.Error()) } @@ -54,7 +54,7 @@ func TestInitWithMultipleResolutions(t *testing.T) { func TestInitWithMultipleResolutionsShouldBeSorted(t *testing.T) { rStrs := []string{"32x32", "64x64", "16x16", "128x128"} - rs, err := Init(rStrs) + rs, err := New(rStrs) if err != nil { t.Errorf("Init with valid parameter should not fail. Error: %s.\n", err.Error()) } @@ -72,7 +72,7 @@ func TestInitWithMultipleResolutionsShouldBeSorted(t *testing.T) { } } func TestClosestMatchWithEmptyResolutions(t *testing.T) { - rs, _ := Init(nil) + rs, _ := New(nil) width := 24 height := 24 @@ -83,7 +83,7 @@ func TestClosestMatchWithEmptyResolutions(t *testing.T) { } func TestClosestMatch(t *testing.T) { - rs, _ := Init([]string{"16x16", "24x24", "32x32", "64x64", "128x128"}) + rs, _ := New([]string{"16x16", "24x24", "32x32", "64x64", "128x128"}) table := [][]int{ // width, height, expectedWidth, expectedHeight []int{17, 17, 24, 24}, diff --git a/pkg/thumbnails/storage/storage.go b/pkg/thumbnails/storage/storage.go index 68de3c9ea..ca5ddf557 100644 --- a/pkg/thumbnails/storage/storage.go +++ b/pkg/thumbnails/storage/storage.go @@ -1,12 +1,12 @@ package storage -import "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolution" +import "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolutions" // Context combines different attributes needed for storage operations. type Context struct { ETag string Types []string - Resolution resolution.Resolution + Resolution resolutions.Resolution } // Storage defines the interface for a thumbnail store. diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go index 289d03462..fa22d398c 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnails/thumbnails.go @@ -6,13 +6,13 @@ import ( "github.com/nfnt/resize" "github.com/owncloud/ocis-pkg/v2/log" - "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolution" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolutions" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" ) // Context bundles information needed to generate a thumbnail for afile type Context struct { - Resolution resolution.Resolution + Resolution resolutions.Resolution ImagePath string Encoder Encoder ETag string From 0e2223819d37d4280232ad4e672be44da2fe9ac8 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 25 Mar 2020 16:56:45 +0100 Subject: [PATCH 36/80] rename context --- pkg/service/v0/service.go | 12 +++++----- pkg/thumbnails/storage/filesystem.go | 8 +++---- pkg/thumbnails/storage/inmemory.go | 8 +++---- pkg/thumbnails/storage/storage.go | 6 ++--- pkg/thumbnails/thumbnails.go | 36 ++++++++++++++-------------- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index c480d8ac1..ca68414f4 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -52,36 +52,36 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rs return fmt.Errorf("can't be encoded. filetype %s not supported", req.Filetype.String()) } r := g.resolutions.ClosestMatch(int(req.Width), int(req.Height)) - tCtx := thumbnails.Context{ + tr := thumbnails.Request{ Resolution: r, ImagePath: req.Filepath, Encoder: encoder, ETag: req.Etag, } - thumbnail := g.manager.GetStored(tCtx) + thumbnail := g.manager.GetStored(tr) if thumbnail != nil { rsp.Thumbnail = thumbnail - rsp.Mimetype = tCtx.Encoder.MimeType() + rsp.Mimetype = tr.Encoder.MimeType() return nil } auth := req.Authorization sCtx := context.WithValue(ctx, imgsource.WebDavAuth, auth) // TODO: clean up error handling - img, err := g.source.Get(sCtx, tCtx.ImagePath) + img, err := g.source.Get(sCtx, tr.ImagePath) if err != nil { return err } if img == nil { return fmt.Errorf("could not retrieve image") } - thumbnail, err = g.manager.Get(tCtx, img) + thumbnail, err = g.manager.Get(tr, img) if err != nil { return err } rsp.Thumbnail = thumbnail - rsp.Mimetype = tCtx.Encoder.MimeType() + rsp.Mimetype = tr.Encoder.MimeType() return nil } diff --git a/pkg/thumbnails/storage/filesystem.go b/pkg/thumbnails/storage/filesystem.go index c8f604245..dc45d9de0 100644 --- a/pkg/thumbnails/storage/filesystem.go +++ b/pkg/thumbnails/storage/filesystem.go @@ -64,10 +64,10 @@ func (s FileSystem) Set(key string, img []byte) error { // e.g. 97/9f/4c8db98f7b82e768ef478d3c8612/500x300.png // // The key also represents the path to the thumbnail in the filesystem under the configured root directory. -func (s FileSystem) BuildKey(ctx Context) string { - etag := ctx.ETag - filetype := ctx.Types[0] - filename := ctx.Resolution.String() + "." + filetype +func (s FileSystem) BuildKey(r Request) string { + etag := r.ETag + filetype := r.Types[0] + filename := r.Resolution.String() + "." + filetype key := new(bytes.Buffer) key.WriteString(etag[:2]) diff --git a/pkg/thumbnails/storage/inmemory.go b/pkg/thumbnails/storage/inmemory.go index 74a78befd..12017eed0 100644 --- a/pkg/thumbnails/storage/inmemory.go +++ b/pkg/thumbnails/storage/inmemory.go @@ -29,11 +29,11 @@ func (s InMemory) Set(key string, thumbnail []byte) error { } // BuildKey generates a unique key to store and retrieve the thumbnail. -func (s InMemory) BuildKey(ctx Context) string { +func (s InMemory) BuildKey(r Request) string { parts := []string{ - ctx.ETag, - ctx.Resolution.String(), - strings.Join(ctx.Types, ","), + r.ETag, + r.Resolution.String(), + strings.Join(r.Types, ","), } return strings.Join(parts, "+") } diff --git a/pkg/thumbnails/storage/storage.go b/pkg/thumbnails/storage/storage.go index ca5ddf557..48953a329 100644 --- a/pkg/thumbnails/storage/storage.go +++ b/pkg/thumbnails/storage/storage.go @@ -2,8 +2,8 @@ package storage import "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolutions" -// Context combines different attributes needed for storage operations. -type Context struct { +// Request combines different attributes needed for storage operations. +type Request struct { ETag string Types []string Resolution resolutions.Resolution @@ -13,5 +13,5 @@ type Context struct { type Storage interface { Get(string) []byte Set(string, []byte) error - BuildKey(Context) string + BuildKey(Request) string } diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go index fa22d398c..2a0600795 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnails/thumbnails.go @@ -10,8 +10,8 @@ import ( "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" ) -// Context bundles information needed to generate a thumbnail for afile -type Context struct { +// Request bundles information needed to generate a thumbnail for afile +type Request struct { Resolution resolutions.Resolution ImagePath string Encoder Encoder @@ -21,10 +21,10 @@ type Context struct { // Manager is responsible for generating thumbnails type Manager interface { // Get will return a thumbnail for a file - Get(Context, image.Image) ([]byte, error) + Get(Request, image.Image) ([]byte, error) // GetStored loads the thumbnail from the storage. // It will return nil if no image is stored for the given context. - GetStored(Context) []byte + GetStored(Request) []byte } // NewSimpleManager creates a new instance of SimpleManager @@ -42,13 +42,13 @@ type SimpleManager struct { } // Get implements the Get Method of Manager -func (s SimpleManager) Get(ctx Context, img image.Image) ([]byte, error) { - thumbnail := s.generate(ctx, img) +func (s SimpleManager) Get(r Request, img image.Image) ([]byte, error) { + thumbnail := s.generate(r, img) - key := s.storage.BuildKey(mapToStorageContext(ctx)) + key := s.storage.BuildKey(mapToStorageRequest(r)) buf := new(bytes.Buffer) - err := ctx.Encoder.Encode(buf, thumbnail) + err := r.Encoder.Encode(buf, thumbnail) if err != nil { return nil, err } @@ -62,22 +62,22 @@ func (s SimpleManager) Get(ctx Context, img image.Image) ([]byte, error) { // GetStored tries to get the stored thumbnail and return it. // If there is no cached thumbnail it will return nil -func (s SimpleManager) GetStored(ctx Context) []byte { - key := s.storage.BuildKey(mapToStorageContext(ctx)) +func (s SimpleManager) GetStored(r Request) []byte { + key := s.storage.BuildKey(mapToStorageRequest(r)) stored := s.storage.Get(key) return stored } -func (s SimpleManager) generate(ctx Context, img image.Image) image.Image { - thumbnail := resize.Thumbnail(uint(ctx.Resolution.Width), uint(ctx.Resolution.Height), img, resize.Lanczos2) +func (s SimpleManager) generate(r Request, img image.Image) image.Image { + thumbnail := resize.Thumbnail(uint(r.Resolution.Width), uint(r.Resolution.Height), img, resize.Lanczos2) return thumbnail } -func mapToStorageContext(ctx Context) storage.Context { - sCtx := storage.Context{ - ETag: ctx.ETag, - Resolution: ctx.Resolution, - Types: ctx.Encoder.Types(), +func mapToStorageRequest(r Request) storage.Request { + sR := storage.Request{ + ETag: r.ETag, + Resolution: r.Resolution, + Types: r.Encoder.Types(), } - return sCtx + return sR } From e9c2de26a5d56716f46b22517070ad5610e41b04 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Thu, 26 Mar 2020 09:49:37 +0100 Subject: [PATCH 37/80] rename package --- pkg/service/v0/service.go | 20 +++++++++---------- pkg/{thumbnails => thumbnail}/encoding.go | 2 +- .../imgsource/imgsource.go | 0 .../imgsource/webdav.go | 0 .../resolution}/resolution.go | 2 +- .../resolution}/resolution_test.go | 2 +- .../resolution}/resolutions.go | 2 +- .../resolution}/resolutions_test.go | 2 +- .../storage/filesystem.go | 0 .../storage/inmemory.go | 0 .../storage/storage.go | 4 ++-- pkg/{thumbnails => thumbnail}/thumbnails.go | 8 ++++---- 12 files changed, 21 insertions(+), 21 deletions(-) rename pkg/{thumbnails => thumbnail}/encoding.go (98%) rename pkg/{thumbnails => thumbnail}/imgsource/imgsource.go (100%) rename pkg/{thumbnails => thumbnail}/imgsource/webdav.go (100%) rename pkg/{thumbnails/resolutions => thumbnail/resolution}/resolution.go (98%) rename pkg/{thumbnails/resolutions => thumbnail/resolution}/resolution_test.go (97%) rename pkg/{thumbnails/resolutions => thumbnail/resolution}/resolutions.go (98%) rename pkg/{thumbnails/resolutions => thumbnail/resolution}/resolutions_test.go (99%) rename pkg/{thumbnails => thumbnail}/storage/filesystem.go (100%) rename pkg/{thumbnails => thumbnail}/storage/inmemory.go (100%) rename pkg/{thumbnails => thumbnail}/storage/storage.go (74%) rename pkg/{thumbnails => thumbnail}/thumbnails.go (91%) diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index ca68414f4..914968c8d 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -6,22 +6,22 @@ import ( "github.com/owncloud/ocis-pkg/v2/log" v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" - "github.com/owncloud/ocis-thumbnails/pkg/thumbnails" - "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/imgsource" - "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolutions" - "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/imgsource" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/resolution" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/storage" ) // NewService returns a service implementation for Service. func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { options := newOptions(opts...) logger := options.Logger - resolutions, err := resolutions.New(options.Config.Thumbnail.Resolutions) + resolutions, err := resolution.New(options.Config.Thumbnail.Resolutions) if err != nil { logger.Fatal().Err(err).Msg("resolutions not configured correctly") } svc := Thumbnail{ - manager: thumbnails.NewSimpleManager( + manager: thumbnail.NewSimpleManager( storage.NewFileSystemStorage( options.Config.Thumbnail.FileSystemStorage, logger, @@ -38,21 +38,21 @@ func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { // Thumbnail implements the GRPC handler. type Thumbnail struct { - manager thumbnails.Manager - resolutions resolutions.Resolutions + manager thumbnail.Manager + resolutions resolution.Resolutions source imgsource.Source logger log.Logger } // GetThumbnail retrieves a thumbnail for an image func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error { - encoder := thumbnails.EncoderForType(req.Filetype.String()) + encoder := thumbnail.EncoderForType(req.Filetype.String()) if encoder == nil { // TODO: better error responses return fmt.Errorf("can't be encoded. filetype %s not supported", req.Filetype.String()) } r := g.resolutions.ClosestMatch(int(req.Width), int(req.Height)) - tr := thumbnails.Request{ + tr := thumbnail.Request{ Resolution: r, ImagePath: req.Filepath, Encoder: encoder, diff --git a/pkg/thumbnails/encoding.go b/pkg/thumbnail/encoding.go similarity index 98% rename from pkg/thumbnails/encoding.go rename to pkg/thumbnail/encoding.go index a4d4647bb..d8d2d358b 100644 --- a/pkg/thumbnails/encoding.go +++ b/pkg/thumbnail/encoding.go @@ -1,4 +1,4 @@ -package thumbnails +package thumbnail import ( "image" diff --git a/pkg/thumbnails/imgsource/imgsource.go b/pkg/thumbnail/imgsource/imgsource.go similarity index 100% rename from pkg/thumbnails/imgsource/imgsource.go rename to pkg/thumbnail/imgsource/imgsource.go diff --git a/pkg/thumbnails/imgsource/webdav.go b/pkg/thumbnail/imgsource/webdav.go similarity index 100% rename from pkg/thumbnails/imgsource/webdav.go rename to pkg/thumbnail/imgsource/webdav.go diff --git a/pkg/thumbnails/resolutions/resolution.go b/pkg/thumbnail/resolution/resolution.go similarity index 98% rename from pkg/thumbnails/resolutions/resolution.go rename to pkg/thumbnail/resolution/resolution.go index 14acab7e3..514fe8574 100644 --- a/pkg/thumbnails/resolutions/resolution.go +++ b/pkg/thumbnail/resolution/resolution.go @@ -1,4 +1,4 @@ -package resolutions +package resolution import ( "fmt" diff --git a/pkg/thumbnails/resolutions/resolution_test.go b/pkg/thumbnail/resolution/resolution_test.go similarity index 97% rename from pkg/thumbnails/resolutions/resolution_test.go rename to pkg/thumbnail/resolution/resolution_test.go index ae407163f..b8c3e6c88 100644 --- a/pkg/thumbnails/resolutions/resolution_test.go +++ b/pkg/thumbnail/resolution/resolution_test.go @@ -1,4 +1,4 @@ -package resolutions +package resolution import "testing" diff --git a/pkg/thumbnails/resolutions/resolutions.go b/pkg/thumbnail/resolution/resolutions.go similarity index 98% rename from pkg/thumbnails/resolutions/resolutions.go rename to pkg/thumbnail/resolution/resolutions.go index c6858aacf..2a83d45e7 100644 --- a/pkg/thumbnails/resolutions/resolutions.go +++ b/pkg/thumbnail/resolution/resolutions.go @@ -1,4 +1,4 @@ -package resolutions +package resolution import ( "fmt" diff --git a/pkg/thumbnails/resolutions/resolutions_test.go b/pkg/thumbnail/resolution/resolutions_test.go similarity index 99% rename from pkg/thumbnails/resolutions/resolutions_test.go rename to pkg/thumbnail/resolution/resolutions_test.go index 9a7583c56..1190cc942 100644 --- a/pkg/thumbnails/resolutions/resolutions_test.go +++ b/pkg/thumbnail/resolution/resolutions_test.go @@ -1,4 +1,4 @@ -package resolutions +package resolution import ( "testing" diff --git a/pkg/thumbnails/storage/filesystem.go b/pkg/thumbnail/storage/filesystem.go similarity index 100% rename from pkg/thumbnails/storage/filesystem.go rename to pkg/thumbnail/storage/filesystem.go diff --git a/pkg/thumbnails/storage/inmemory.go b/pkg/thumbnail/storage/inmemory.go similarity index 100% rename from pkg/thumbnails/storage/inmemory.go rename to pkg/thumbnail/storage/inmemory.go diff --git a/pkg/thumbnails/storage/storage.go b/pkg/thumbnail/storage/storage.go similarity index 74% rename from pkg/thumbnails/storage/storage.go rename to pkg/thumbnail/storage/storage.go index 48953a329..a23a757d3 100644 --- a/pkg/thumbnails/storage/storage.go +++ b/pkg/thumbnail/storage/storage.go @@ -1,12 +1,12 @@ package storage -import "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolutions" +import "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/resolution" // Request combines different attributes needed for storage operations. type Request struct { ETag string Types []string - Resolution resolutions.Resolution + Resolution resolution.Resolution } // Storage defines the interface for a thumbnail store. diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnail/thumbnails.go similarity index 91% rename from pkg/thumbnails/thumbnails.go rename to pkg/thumbnail/thumbnails.go index 2a0600795..846cd6013 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnail/thumbnails.go @@ -1,4 +1,4 @@ -package thumbnails +package thumbnail import ( "bytes" @@ -6,13 +6,13 @@ import ( "github.com/nfnt/resize" "github.com/owncloud/ocis-pkg/v2/log" - "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolutions" - "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/resolution" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/storage" ) // Request bundles information needed to generate a thumbnail for afile type Request struct { - Resolution resolutions.Resolution + Resolution resolution.Resolution ImagePath string Encoder Encoder ETag string From cc0837ec3ca36f1cf929007e0a8b385a0416e9ab Mon Sep 17 00:00:00 2001 From: David Christofas Date: Thu, 26 Mar 2020 09:00:33 +0000 Subject: [PATCH 38/80] Automated changelog update [skip ci] --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52142f119..943e57e02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Summary * Change - Initial release of basic version: [#1](https://github.com/owncloud/ocis-thumbnails/issues/1) +* Change - Use predefined resolutions for thumbnail generation: [#7](https://github.com/owncloud/ocis-thumbnails/issues/7) * Change - Implement the first working version: [#3](https://github.com/owncloud/ocis-thumbnails/pull/3) ## Details @@ -15,6 +16,15 @@ https://github.com/owncloud/ocis-thumbnails/issues/1 +* Change - Use predefined resolutions for thumbnail generation: [#7](https://github.com/owncloud/ocis-thumbnails/issues/7) + + We implemented predefined resolutions to prevent attacker from flooding the service with a + large number of thumbnails. The requested resolution gets mapped to the closest matching + predefined resolution. + + https://github.com/owncloud/ocis-thumbnails/issues/7 + + * Change - Implement the first working version: [#3](https://github.com/owncloud/ocis-thumbnails/pull/3) We implemented the first simple version. It can load images via webdav and store them locally in From 336565fe24af058d36a0662f0ea04852602fd27e Mon Sep 17 00:00:00 2001 From: David Christofas Date: Thu, 26 Mar 2020 11:25:57 +0100 Subject: [PATCH 39/80] add protobuf documentation --- Makefile | 52 ++++----------- docs/grpc.md | 119 ++++++++++++++++++++++++++++++++++ docs/grpc.tmpl | 112 ++++++++++++++++++++++++++++++++ docs/license.md | 2 +- pkg/proto/v0/thumbnails.proto | 17 ++++- 5 files changed, 259 insertions(+), 43 deletions(-) create mode 100644 docs/grpc.md create mode 100644 docs/grpc.tmpl diff --git a/Makefile b/Makefile index bbfafe3cf..0fd89d2e7 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,8 @@ IMPORT := github.com/owncloud/$(NAME) BIN := bin DIST := dist HUGO := hugo +PROTO_VERSION := v0 +PROTO_SRC := pkg/proto/$(PROTO_VERSION)/*.proto ifeq ($(OS), Windows_NT) EXECUTABLE := $(NAME).exe @@ -131,6 +133,11 @@ release-check: .PHONY: release-finish release-finish: release-copy release-check +.PHONY: proto-docs-build +proto-docs-build: + go get -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc; \ + protoc --doc_out=./docs --doc_opt=./docs/grpc.tmpl,grpc.md $(PROTO_SRC) + .PHONY: docs-copy docs-copy: mkdir -p $(HUGO); \ @@ -141,54 +148,19 @@ docs-copy: git remote add origin https://github.com/owncloud/owncloud.github.io; \ git fetch; \ git checkout origin/source -f; \ - rsync --delete -ax ../docs/ content/extensions/$(NAME) + rsync --delete -ax ../docs/*.md content/extensions/$(NAME) .PHONY: docs-build docs-build: cd $(HUGO); hugo .PHONY: docs -docs: docs-copy docs-build +docs: proto-docs-build docs-copy docs-build .PHONY: watch watch: go run github.com/cespare/reflex -c reflex.conf -# $(GOPATH)/bin/protoc-gen-go: -# GO111MODULE=off go get -v github.com/golang/protobuf/protoc-gen-go - -# $(GOPATH)/bin/protoc-gen-micro: -# GO111MODULE=off go get -v github.com/micro/protoc-gen-micro - -# $(GOPATH)/bin/protoc-gen-microweb: -# GO111MODULE=off go get -v github.com/webhippie/protoc-gen-microweb - -# $(GOPATH)/bin/protoc-gen-swagger: -# GO111MODULE=off go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger - -# pkg/proto/v0/example.pb.go: pkg/proto/v0/example.proto -# protoc \ -# -I=third_party/ \ -# -I=pkg/proto/v0/ \ -# --go_out=logtostderr=true:pkg/proto/v0 example.proto - -# pkg/proto/v0/example.pb.micro.go: pkg/proto/v0/example.proto -# protoc \ -# -I=third_party/ \ -# -I=pkg/proto/v0/ \ -# --micro_out=logtostderr=true:pkg/proto/v0 example.proto - -# pkg/proto/v0/example.pb.web.go: pkg/proto/v0/example.proto -# protoc \ -# -I=third_party/ \ -# -I=pkg/proto/v0/ \ -# --microweb_out=logtostderr=true:pkg/proto/v0 example.proto - -# pkg/proto/v0/example.swagger.json: pkg/proto/v0/example.proto -# protoc \ -# -I=third_party/ \ -# -I=pkg/proto/v0/ \ -# --swagger_out=logtostderr=true:pkg/proto/v0 example.proto - -# .PHONY: protobuf -# protobuf: $(GOPATH)/bin/protoc-gen-go $(GOPATH)/bin/protoc-gen-micro $(GOPATH)/bin/protoc-gen-microweb $(GOPATH)/bin/protoc-gen-swagger pkg/proto/v0/example.pb.go pkg/proto/v0/example.pb.micro.go pkg/proto/v0/example.pb.web.go pkg/proto/v0/example.swagger.json +.PHONY: pb +pb: + protoc --go_out=. --micro_out=. $(PROTO_SRC) diff --git a/docs/grpc.md b/docs/grpc.md new file mode 100644 index 000000000..ccb334b79 --- /dev/null +++ b/docs/grpc.md @@ -0,0 +1,119 @@ +--- +title: "GRPC API" +date: 2018-05-02T00:00:00+00:00 +weight: 40 +geekdocRepo: https://github.com/owncloud/ocis-thumbnails +geekdocEditPath: edit/master/docs +geekdocFilePath: grpc.md +--- + + + +## Table of Contents + +- [pkg/proto/v0/thumbnails.proto](#pkg/proto/v0/thumbnails.proto) + - [GetRequest](#com.owncloud.ocis.thumbnails.v0.GetRequest) + - [GetResponse](#com.owncloud.ocis.thumbnails.v0.GetResponse) + + - [GetRequest.FileType](#com.owncloud.ocis.thumbnails.v0.GetRequest.FileType) + + - [ThumbnailService](#com.owncloud.ocis.thumbnails.v0.ThumbnailService) + +- [Scalar Value Types](#scalar-value-types) + + + + +

Top

+ +## pkg/proto/v0/thumbnails.proto + + + + + +### GetRequest +A request to retrieve a thumbnail + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| filepath | [string](#string) | | The path to the source image | +| filetype | [GetRequest.FileType](#com.owncloud.ocis.thumbnails.v0.GetRequest.FileType) | | The type to which the thumbnail should get encoded to. | +| etag | [string](#string) | | The etag of the source image | +| width | [int32](#int32) | | The width of the thumbnail | +| height | [int32](#int32) | | The height of the thumbnail | +| authorization | [string](#string) | | The authorization token | + + + + + + + + +### GetResponse +The service response + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| thumbnail | [bytes](#bytes) | | The thumbnail as a binary | +| mimetype | [string](#string) | | The mimetype of the thumbnail | + + + + + + + + + + +### GetRequest.FileType +The file types to which the thumbnail cna get encoded to. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| PNG | 0 | Represents PNG type | +| JPG | 1 | Represents JPG type | + + + + + + + + + +### ThumbnailService +A Service for handling thumbnail generation + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| GetThumbnail | [GetRequest](#com.owncloud.ocis.thumbnails.v0.GetRequest) | [GetResponse](#com.owncloud.ocis.thumbnails.v0.GetResponse) | Generates the thumbnail and returns it. | + + + + + +## Scalar Value Types + +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | +| double | | double | double | float | float64 | double | float | Float | +| float | | float | float | float | float32 | float | float | Float | +| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | +| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | +| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | +| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | +| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | +| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | +| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | + diff --git a/docs/grpc.tmpl b/docs/grpc.tmpl new file mode 100644 index 000000000..814a8713b --- /dev/null +++ b/docs/grpc.tmpl @@ -0,0 +1,112 @@ +--- +title: "GRPC API" +date: 2018-05-02T00:00:00+00:00 +weight: 40 +geekdocRepo: https://github.com/owncloud/ocis-thumbnails +geekdocEditPath: edit/master/docs +geekdocFilePath: grpc.md +--- + + + +## Table of Contents +{{range .Files}} +{{$file_name := .Name}}- [{{.Name}}](#{{.Name}}) + {{- if .Messages }} + {{range .Messages}} - [{{.LongName}}](#{{.FullName}}) + {{end}} + {{- end -}} + {{- if .Enums }} + {{range .Enums}} - [{{.LongName}}](#{{.FullName}}) + {{end}} + {{- end -}} + {{- if .Extensions }} + {{range .Extensions}} - [File-level Extensions](#{{$file_name}}-extensions) + {{end}} + {{- end -}} + {{- if .Services }} + {{range .Services}} - [{{.Name}}](#{{.FullName}}) + {{end}} + {{- end -}} +{{end}} +- [Scalar Value Types](#scalar-value-types) + +{{range .Files}} +{{$file_name := .Name}} + +

Top

+ +## {{.Name}} +{{.Description}} + +{{range .Messages}} + + +### {{.LongName}} +{{.Description}} + +{{if .HasFields}} +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +{{range .Fields -}} + | {{.Name}} | [{{.LongType}}](#{{.FullType}}) | {{.Label}} | {{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | +{{end}} +{{end}} + +{{if .HasExtensions}} +| Extension | Type | Base | Number | Description | +| --------- | ---- | ---- | ------ | ----------- | +{{range .Extensions -}} + | {{.Name}} | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | +{{end}} +{{end}} + +{{end}} + +{{range .Enums}} + + +### {{.LongName}} +{{.Description}} + +| Name | Number | Description | +| ---- | ------ | ----------- | +{{range .Values -}} + | {{.Name}} | {{.Number}} | {{nobr .Description}} | +{{end}} + +{{end}} + +{{if .HasExtensions}} + + +### File-level Extensions +| Extension | Type | Base | Number | Description | +| --------- | ---- | ---- | ------ | ----------- | +{{range .Extensions -}} + | {{.Name}} | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description}}{{if .DefaultValue}} Default: `{{.DefaultValue}}`{{end}} | +{{end}} +{{end}} + +{{range .Services}} + + +### {{.Name}} +{{.Description}} + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +{{range .Methods -}} + | {{.Name}} | [{{.RequestLongType}}](#{{.RequestFullType}}){{if .RequestStreaming}} stream{{end}} | [{{.ResponseLongType}}](#{{.ResponseFullType}}){{if .ResponseStreaming}} stream{{end}} | {{nobr .Description}} | +{{end}} +{{end}} + +{{end}} + +## Scalar Value Types + +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | +{{range .Scalars -}} + | {{.ProtoType}} | {{.Notes}} | {{.CppType}} | {{.JavaType}} | {{.PythonType}} | {{.GoType}} | {{.CSharp}} | {{.PhpType}} | {{.RubyType}} | +{{end}} diff --git a/docs/license.md b/docs/license.md index 17bd89d71..273e92c3b 100644 --- a/docs/license.md +++ b/docs/license.md @@ -1,7 +1,7 @@ --- title: "License" date: 2018-05-02T00:00:00+00:00 -weight: 40 +weight: 50 geekdocRepo: https://github.com/owncloud/ocis-thumbnails geekdocEditPath: edit/master/docs geekdocFilePath: license.md diff --git a/pkg/proto/v0/thumbnails.proto b/pkg/proto/v0/thumbnails.proto index 261a65b13..9b1f6476a 100644 --- a/pkg/proto/v0/thumbnails.proto +++ b/pkg/proto/v0/thumbnails.proto @@ -3,24 +3,37 @@ syntax = "proto3"; package com.owncloud.ocis.thumbnails.v0; option go_package = "proto"; +// A Service for handling thumbnail generation service ThumbnailService { + // Generates the thumbnail and returns it. rpc GetThumbnail(GetRequest) returns (GetResponse); } +// A request to retrieve a thumbnail message GetRequest { + // The path to the source image string filepath = 1; + // The file types to which the thumbnail cna get encoded to. enum FileType { - PNG = 0; - JPG = 1; + PNG = 0; // Represents PNG type + JPG = 1; // Represents JPG type } + // The type to which the thumbnail should get encoded to. FileType filetype = 2; + // The etag of the source image string etag = 3; + // The width of the thumbnail int32 width = 4; + // The height of the thumbnail int32 height = 5; + // The authorization token string authorization = 6; } +// The service response message GetResponse { + // The thumbnail as a binary bytes thumbnail = 1; + // The mimetype of the thumbnail string mimetype = 2; } \ No newline at end of file From 5841cd7b6f33c2b857b12085f385d9bab5a8d0bd Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Thu, 26 Mar 2020 17:28:40 +0100 Subject: [PATCH 40/80] Better formatting of protodocs --- docs/building.md | 2 +- docs/grpc.md | 96 ++++++++++--------------------------- docs/grpc.tmpl | 121 ++++++++++++++++------------------------------- 3 files changed, 67 insertions(+), 152 deletions(-) diff --git a/docs/building.md b/docs/building.md index 784946f4c..4c0c3dac0 100644 --- a/docs/building.md +++ b/docs/building.md @@ -12,7 +12,7 @@ geekdocFilePath: building.md As this project is built with Go, so you need to install that first. The installation of Go is out of the scope of this document, please follow the official documentation for [Go](https://golang.org/doc/install), to build this project you have to install Go >= v1.12. After the installation of the required tools you need to get the sources: {{< highlight txt >}} -git clone https://github.com/owncloud/{{ Name }}.git +git clone https://github.com/owncloud/ocis-thubnails.git cd {{ Name }} {{< / highlight >}} diff --git a/docs/grpc.md b/docs/grpc.md index ccb334b79..a4b8e2f9d 100644 --- a/docs/grpc.md +++ b/docs/grpc.md @@ -7,70 +7,34 @@ geekdocEditPath: edit/master/docs geekdocFilePath: grpc.md --- - - -## Table of Contents - -- [pkg/proto/v0/thumbnails.proto](#pkg/proto/v0/thumbnails.proto) - - [GetRequest](#com.owncloud.ocis.thumbnails.v0.GetRequest) - - [GetResponse](#com.owncloud.ocis.thumbnails.v0.GetResponse) - - - [GetRequest.FileType](#com.owncloud.ocis.thumbnails.v0.GetRequest.FileType) - - - [ThumbnailService](#com.owncloud.ocis.thumbnails.v0.ThumbnailService) - -- [Scalar Value Types](#scalar-value-types) - - - - -

Top

+{{< toc >}} ## pkg/proto/v0/thumbnails.proto - - - - ### GetRequest -A request to retrieve a thumbnail +A request to retrieve a thumbnail | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | filepath | [string](#string) | | The path to the source image | -| filetype | [GetRequest.FileType](#com.owncloud.ocis.thumbnails.v0.GetRequest.FileType) | | The type to which the thumbnail should get encoded to. | +| filetype | [GetRequest.FileType](#getrequestfiletype) | | The type to which the thumbnail should get encoded to. | | etag | [string](#string) | | The etag of the source image | | width | [int32](#int32) | | The width of the thumbnail | | height | [int32](#int32) | | The height of the thumbnail | | authorization | [string](#string) | | The authorization token | - - - - - - - ### GetResponse -The service response +The service response | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | thumbnail | [bytes](#bytes) | | The thumbnail as a binary | | mimetype | [string](#string) | | The mimetype of the thumbnail | - - - - - - - - - ### GetRequest.FileType + The file types to which the thumbnail cna get encoded to. | Name | Number | Description | @@ -78,42 +42,30 @@ The file types to which the thumbnail cna get encoded to. | PNG | 0 | Represents PNG type | | JPG | 1 | Represents JPG type | - - - - - - - - ### ThumbnailService + A Service for handling thumbnail generation | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| GetThumbnail | [GetRequest](#com.owncloud.ocis.thumbnails.v0.GetRequest) | [GetResponse](#com.owncloud.ocis.thumbnails.v0.GetResponse) | Generates the thumbnail and returns it. | - - - - +| GetThumbnail | [GetRequest](#getrequest) | [GetResponse](#getresponse) | Generates the thumbnail and returns it. | ## Scalar Value Types -| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | -| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | -| double | | double | double | float | float64 | double | float | Float | -| float | | float | float | float | float32 | float | float | Float | -| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | -| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | -| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | -| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | -| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | -| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | -| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | -| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | -| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | -| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | -| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | -| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | -| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | - +| .proto Type | Notes | C++ | Java | +| ----------- | ----- | --- | ---- | +| {{< div id="double" content="double" >}} | | double | double | +| {{< div id="float" content="float" >}} | | float | float | +| {{< div id="int32" content="int32" >}} | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | +| {{< div id="int64" content="int64" >}} | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | +| {{< div id="uint32" content="uint32" >}} | Uses variable-length encoding. | uint32 | int | +| {{< div id="uint64" content="uint64" >}} | Uses variable-length encoding. | uint64 | long | +| {{< div id="sint32" content="sint32" >}} | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | +| {{< div id="sint64" content="sint64" >}} | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | +| {{< div id="fixed32" content="fixed32" >}} | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | +| {{< div id="fixed64" content="fixed64" >}} | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | +| {{< div id="sfixed32" content="sfixed32" >}} | Always four bytes. | int32 | int | +| {{< div id="sfixed64" content="sfixed64" >}} | Always eight bytes. | int64 | long | +| {{< div id="bool" content="bool" >}} | | bool | boolean | +| {{< div id="string" content="string" >}} | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | +| {{< div id="bytes" content="bytes" >}} | May contain any arbitrary sequence of bytes. | string | ByteString | diff --git a/docs/grpc.tmpl b/docs/grpc.tmpl index 814a8713b..d7c9da3e5 100644 --- a/docs/grpc.tmpl +++ b/docs/grpc.tmpl @@ -7,106 +7,69 @@ geekdocEditPath: edit/master/docs geekdocFilePath: grpc.md --- - +{{`{{< toc >}}`}} -## Table of Contents -{{range .Files}} -{{$file_name := .Name}}- [{{.Name}}](#{{.Name}}) - {{- if .Messages }} - {{range .Messages}} - [{{.LongName}}](#{{.FullName}}) - {{end}} - {{- end -}} - {{- if .Enums }} - {{range .Enums}} - [{{.LongName}}](#{{.FullName}}) - {{end}} - {{- end -}} - {{- if .Extensions }} - {{range .Extensions}} - [File-level Extensions](#{{$file_name}}-extensions) - {{end}} - {{- end -}} - {{- if .Services }} - {{range .Services}} - [{{.Name}}](#{{.FullName}}) - {{end}} - {{- end -}} -{{end}} -- [Scalar Value Types](#scalar-value-types) +{{ range .Files -}} +## {{ .Name }} -{{range .Files}} -{{$file_name := .Name}} - -

Top

+{{ .Description }} +{{- range .Messages -}} +{{- /* remove newline */}}### {{ .LongName }} -## {{.Name}} -{{.Description}} +{{ .Description }} -{{range .Messages}} - - -### {{.LongName}} -{{.Description}} - -{{if .HasFields}} +{{ if .HasFields -}} | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -{{range .Fields -}} - | {{.Name}} | [{{.LongType}}](#{{.FullType}}) | {{.Label}} | {{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | -{{end}} -{{end}} - -{{if .HasExtensions}} +{{ range .Fields -}} +| {{.Name}} | [{{.LongType}}](#{{.LongType | lower | replace "." "" }}) | {{.Label}} | {{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end }} | +{{ end -}} +{{ end }} +{{ if .HasExtensions -}} | Extension | Type | Base | Number | Description | | --------- | ---- | ---- | ------ | ----------- | -{{range .Extensions -}} - | {{.Name}} | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | -{{end}} -{{end}} +{{ range .Extensions -}} +| {{.Name}} | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | +{{ end -}} +{{ end -}} +{{ end -}} +{{- range .Enums -}} +### {{ .LongName }} -{{end}} - -{{range .Enums}} - - -### {{.LongName}} -{{.Description}} +{{ .Description }} | Name | Number | Description | | ---- | ------ | ----------- | {{range .Values -}} - | {{.Name}} | {{.Number}} | {{nobr .Description}} | -{{end}} +| {{.Name}} | {{.Number}} | {{nobr .Description}} | +{{ end -}} +{{ end -}} -{{end}} - -{{if .HasExtensions}} - +{{ if .HasExtensions }} ### File-level Extensions | Extension | Type | Base | Number | Description | | --------- | ---- | ---- | ------ | ----------- | -{{range .Extensions -}} - | {{.Name}} | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description}}{{if .DefaultValue}} Default: `{{.DefaultValue}}`{{end}} | -{{end}} -{{end}} +{{ range .Extensions -}} +| {{.Name}} | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description}}{{if .DefaultValue}} Default: `{{.DefaultValue}}`{{end}} | +{{ end -}} +{{ end -}} +{{- range .Services }} +### {{ .Name }} -{{range .Services}} - - -### {{.Name}} -{{.Description}} +{{ .Description }} | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -{{range .Methods -}} - | {{.Name}} | [{{.RequestLongType}}](#{{.RequestFullType}}){{if .RequestStreaming}} stream{{end}} | [{{.ResponseLongType}}](#{{.ResponseFullType}}){{if .ResponseStreaming}} stream{{end}} | {{nobr .Description}} | -{{end}} -{{end}} - -{{end}} - +{{ range .Methods -}} +| {{.Name}} | [{{.RequestLongType}}](#{{.RequestLongType | lower | replace "." "" }}){{if .RequestStreaming}} stream{{end}} | [{{.ResponseLongType}}](#{{.ResponseLongType | lower | replace "." "" }}){{if .ResponseStreaming}} stream{{end}} | {{nobr .Description}} | +{{ end -}} +{{ end -}} +{{ end }} ## Scalar Value Types -| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | -| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | -{{range .Scalars -}} - | {{.ProtoType}} | {{.Notes}} | {{.CppType}} | {{.JavaType}} | {{.PythonType}} | {{.GoType}} | {{.CSharp}} | {{.PhpType}} | {{.RubyType}} | -{{end}} +| .proto Type | Notes | C++ | Java | +| ----------- | ----- | --- | ---- | +{{ range .Scalars -}} +| {{`{{< div id="`}}{{.ProtoType}}{{`" content="`}}{{.ProtoType}}{{`" >}}`}} | {{.Notes}} | {{.CppType}} | {{.JavaType}} | +{{ end }} \ No newline at end of file From f8afe85c3e0410089c89296742a88af0f7343e69 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Fri, 27 Mar 2020 12:24:28 +0100 Subject: [PATCH 41/80] Move templates to a separate folder --- Makefile | 4 ++-- {docs => templates}/grpc.tmpl | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename {docs => templates}/grpc.tmpl (100%) diff --git a/Makefile b/Makefile index 0fd89d2e7..10babead3 100644 --- a/Makefile +++ b/Makefile @@ -136,7 +136,7 @@ release-finish: release-copy release-check .PHONY: proto-docs-build proto-docs-build: go get -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc; \ - protoc --doc_out=./docs --doc_opt=./docs/grpc.tmpl,grpc.md $(PROTO_SRC) + protoc --doc_out=./docs --doc_opt=./templates/grpc.tmpl,grpc.md $(PROTO_SRC) .PHONY: docs-copy docs-copy: @@ -148,7 +148,7 @@ docs-copy: git remote add origin https://github.com/owncloud/owncloud.github.io; \ git fetch; \ git checkout origin/source -f; \ - rsync --delete -ax ../docs/*.md content/extensions/$(NAME) + rsync --delete -ax ../docs/ content/extensions/$(NAME) .PHONY: docs-build docs-build: diff --git a/docs/grpc.tmpl b/templates/grpc.tmpl similarity index 100% rename from docs/grpc.tmpl rename to templates/grpc.tmpl From af26eeffd8bf491b40581341a04d5e88984b4865 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 31 Mar 2020 11:13:52 +0200 Subject: [PATCH 42/80] prepare 0.1.0 release --- changelog/{unreleased => 0.1.0_2020-03-31}/initial-version.md | 0 .../{unreleased => 0.1.0_2020-03-31}/predefined-resolutions.md | 0 changelog/{unreleased => 0.1.0_2020-03-31}/thumbnail-service.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename changelog/{unreleased => 0.1.0_2020-03-31}/initial-version.md (100%) rename changelog/{unreleased => 0.1.0_2020-03-31}/predefined-resolutions.md (100%) rename changelog/{unreleased => 0.1.0_2020-03-31}/thumbnail-service.md (100%) diff --git a/changelog/unreleased/initial-version.md b/changelog/0.1.0_2020-03-31/initial-version.md similarity index 100% rename from changelog/unreleased/initial-version.md rename to changelog/0.1.0_2020-03-31/initial-version.md diff --git a/changelog/unreleased/predefined-resolutions.md b/changelog/0.1.0_2020-03-31/predefined-resolutions.md similarity index 100% rename from changelog/unreleased/predefined-resolutions.md rename to changelog/0.1.0_2020-03-31/predefined-resolutions.md diff --git a/changelog/unreleased/thumbnail-service.md b/changelog/0.1.0_2020-03-31/thumbnail-service.md similarity index 100% rename from changelog/unreleased/thumbnail-service.md rename to changelog/0.1.0_2020-03-31/thumbnail-service.md From 3d2697459370336a9e4bb482967ad99a6f7f680d Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Tue, 31 Mar 2020 09:22:15 +0000 Subject: [PATCH 43/80] Automated changelog update [skip ci] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 943e57e02..27d896e40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# Changes in unreleased +# Changes in 0.1.0 ## Summary From 0b757c8713b90004bbffe12cd856a5d3e5c1d687 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 21 Apr 2020 10:58:27 +0200 Subject: [PATCH 44/80] fix program flags --- changelog/unreleased/fix-flags.md | 6 +++ go.mod | 13 ++++- go.sum | 54 +++++++++++++++++--- pkg/command/root.go | 85 ++++++++++++++++--------------- pkg/command/server.go | 4 +- pkg/flagset/flagset.go | 2 + pkg/server/grpc/option.go | 9 ++++ pkg/server/grpc/server.go | 1 + pkg/service/v0/service.go | 10 ++-- 9 files changed, 133 insertions(+), 51 deletions(-) create mode 100644 changelog/unreleased/fix-flags.md diff --git a/changelog/unreleased/fix-flags.md b/changelog/unreleased/fix-flags.md new file mode 100644 index 000000000..23d2b2050 --- /dev/null +++ b/changelog/unreleased/fix-flags.md @@ -0,0 +1,6 @@ +Bugfix: Fix execution when passing program flags + +The program flags where not correctly recognized because we didn't pass them to +the micro framework when initializing a grpc service. + +https://github.com/owncloud/ocis-thumbnails/issues/15 diff --git a/go.mod b/go.mod index 3c986aa07..308d52f3d 100644 --- a/go.mod +++ b/go.mod @@ -6,19 +6,30 @@ require ( contrib.go.opencensus.io/exporter/jaeger v0.2.0 contrib.go.opencensus.io/exporter/ocagent v0.6.0 contrib.go.opencensus.io/exporter/zipkin v0.1.1 + github.com/Masterminds/sprig/v3 v3.1.0 // indirect github.com/UnnoTed/fileb0x v1.1.4 github.com/cespare/reflex v0.2.0 + github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31 // indirect + github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d // indirect github.com/golang/protobuf v1.3.2 + github.com/imdario/mergo v0.3.9 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 + github.com/mattn/go-colorable v0.1.2 // indirect github.com/micro/cli/v2 v2.1.1 github.com/micro/go-micro/v2 v2.0.0 + github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/ogier/pflag v0.0.1 github.com/oklog/run v1.0.0 github.com/openzipkin/zipkin-go v0.2.2 - github.com/owncloud/ocis-pkg/v2 v2.0.1 + github.com/owncloud/ocis-pkg/v2 v2.2.1 github.com/prometheus/client_golang v1.2.1 github.com/restic/calens v0.2.0 + github.com/spf13/afero v1.2.2 // indirect github.com/spf13/viper v1.5.0 go.opencensus.io v0.22.2 + golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a // indirect + golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect + golang.org/x/tools v0.0.0-20200421042724-cfa8b22178d2 // indirect + honnef.co/go/tools v0.0.1-2020.1.3 // indirect ) diff --git a/go.sum b/go.sum index bec1dd4f8..774f1c0ca 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,12 @@ github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RP github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.0.2 h1:tRi7ENs+AaOUCH+j6qwNQgPYfV26dX3JNonq+V4mhqc= github.com/Masterminds/semver/v3 v3.0.2/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk= +github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig/v3 v3.0.1 h1:RuaOafp+8qOLUPX1lInLfUrLc1MEVbnz7a40RLoixKY= github.com/Masterminds/sprig/v3 v3.0.1/go.mod h1:Cp7HwZjmqKrC+Y7XqSJOU2yRvAJRGLiohfgz5ZJj8+4= +github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y= +github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= @@ -185,6 +189,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg= github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31 h1:28FVBuwkwowZMjbA7M0wXsI6t3PYulRTMio3SO+eKCM= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= 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= @@ -192,6 +198,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -255,16 +263,18 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5 h1:FdBGmSkD2QpQzRWup//SGObvWf2nq89zj9+ta9OvI3A= -github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5/go.mod h1:0YZ2wQSuwviXXXGUiK6zXzskyBLAbLXhamxzcFHSLoM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= +github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -289,6 +299,7 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= @@ -331,9 +342,13 @@ github.com/marten-seemann/qtls v0.4.1 h1:YlT8QP3WCCvvok7MGEZkMldXbyqgr8oFg5/n8Gt github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= @@ -377,6 +392,8 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -436,8 +453,8 @@ github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= -github.com/owncloud/ocis-pkg/v2 v2.0.1 h1:3ISEtfjAz4pDFczTggIJwKuft3bVsAp1C7dFY9BBPEs= -github.com/owncloud/ocis-pkg/v2 v2.0.1/go.mod h1:7bVnn3VUaqdmvpMkXF0QVEF1fRugs35hSkuVTAq9yjk= +github.com/owncloud/ocis-pkg/v2 v2.2.1 h1:LK7WxHYugEFQ9NHTOz0EP8DRjbt51wXhyqruV03z6zI= +github.com/owncloud/ocis-pkg/v2 v2.2.1/go.mod h1:MXv7QzsYsu4YWuyJxhq1kLLmJa/r5gbqHe1FXulMHaw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= @@ -484,8 +501,6 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/restic/calens v0.1.0 h1:RHGokdZ72dICyIz1EjEsfZwUhvNZz/zy2SawxJktdWA= -github.com/restic/calens v0.1.0/go.mod h1:u67f5msOjCTDYNzOf/NoAUSdmXP03YXPCwIQLYADy5M= github.com/restic/calens v0.2.0 h1:LVNAtmFc+Pb4ODX66qdX1T3Di1P0OTLyUsVyvM/xD7E= github.com/restic/calens v0.2.0/go.mod h1:UXwyAKS4wsgUZGEc7NrzzygJbLsQZIo3wl+62Q1wvmU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -512,8 +527,12 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -530,6 +549,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -560,6 +581,7 @@ github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4m github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= @@ -609,6 +631,9 @@ golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a h1:R/qVym5WAxsZWQqZCwDY/8 golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad h1:Jh8cai0fqIK+f6nG0UgPW5wFk8wmiMhM3AyciDBdtQg= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a h1:y6sBfNd1b9Wy08a6K1Z1DZc4aXABUN5TKjkYhz7UKmo= +golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -626,11 +651,15 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -660,6 +689,8 @@ golang.org/x/net v0.0.0-20191109021931-daa7c04131f5 h1:bHNaocaoJxYBo5cw41UyTMLjY golang.org/x/net v0.0.0-20191109021931-daa7c04131f5/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= @@ -684,6 +715,7 @@ golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -716,6 +748,7 @@ golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -737,12 +770,19 @@ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361 h1:RIIXAeV6GvDBuADKumTODatUqANFZ+5BPMnzsy4hulY= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200421042724-cfa8b22178d2 h1:fVXd7Kf16vo/CTZCrBFb6BeEhaSbBtsPz9oWmWAoyl8= +golang.org/x/tools v0.0.0-20200421042724-cfa8b22178d2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -819,6 +859,8 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= diff --git a/pkg/command/root.go b/pkg/command/root.go index adb4ee9dc..4143bd885 100644 --- a/pkg/command/root.go +++ b/pkg/command/root.go @@ -5,10 +5,10 @@ import ( "strings" "github.com/micro/cli/v2" + "github.com/owncloud/ocis-pkg/v2/log" "github.com/owncloud/ocis-thumbnails/pkg/config" "github.com/owncloud/ocis-thumbnails/pkg/flagset" "github.com/owncloud/ocis-thumbnails/pkg/version" - "github.com/owncloud/ocis-pkg/v2/log" "github.com/spf13/viper" ) @@ -32,45 +32,7 @@ func Execute() error { Flags: flagset.RootWithConfig(cfg), Before: func(c *cli.Context) error { - logger := NewLogger(cfg) - - viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - viper.SetEnvPrefix("THUMBNAILS") - viper.AutomaticEnv() - - if c.IsSet("config-file") { - viper.SetConfigFile(c.String("config-file")) - } else { - viper.SetConfigName("thumbnails") - - viper.AddConfigPath("/etc/ocis") - viper.AddConfigPath("$HOME/.ocis") - viper.AddConfigPath("./config") - } - - if err := viper.ReadInConfig(); err != nil { - switch err.(type) { - case viper.ConfigFileNotFoundError: - logger.Info(). - Msg("Continue without config") - case viper.UnsupportedConfigError: - logger.Fatal(). - Err(err). - Msg("Unsupported config type") - default: - logger.Fatal(). - Err(err). - Msg("Failed to read config") - } - } - - if err := viper.Unmarshal(&cfg); err != nil { - logger.Fatal(). - Err(err). - Msg("Failed to parse config") - } - - return nil + return ParseConfig(c, cfg) }, Commands: []*cli.Command{ @@ -101,3 +63,46 @@ func NewLogger(cfg *config.Config) log.Logger { log.Color(cfg.Log.Color), ) } + +// ParseConfig loads configuration from Viper known paths. +func ParseConfig(c *cli.Context, cfg *config.Config) error { + logger := NewLogger(cfg) + + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.SetEnvPrefix("THUMBNAILS") + viper.AutomaticEnv() + + if c.IsSet("config-file") { + viper.SetConfigFile(c.String("config-file")) + } else { + viper.SetConfigName("thumbnails") + + viper.AddConfigPath("/etc/ocis") + viper.AddConfigPath("$HOME/.ocis") + viper.AddConfigPath("./config") + } + + if err := viper.ReadInConfig(); err != nil { + switch err.(type) { + case viper.ConfigFileNotFoundError: + logger.Info(). + Msg("Continue without config") + case viper.UnsupportedConfigError: + logger.Fatal(). + Err(err). + Msg("Unsupported config type") + default: + logger.Fatal(). + Err(err). + Msg("Failed to read config") + } + } + + if err := viper.Unmarshal(&cfg); err != nil { + logger.Fatal(). + Err(err). + Msg("Failed to parse config") + } + + return nil +} diff --git a/pkg/command/server.go b/pkg/command/server.go index 9967701ef..833aaf5fe 100644 --- a/pkg/command/server.go +++ b/pkg/command/server.go @@ -30,7 +30,8 @@ func Server(cfg *config.Config) *cli.Command { Flags: flagset.ServerWithConfig(cfg), Before: func(c *cli.Context) error { cfg.Thumbnail.Resolutions = c.StringSlice("thumbnail-resolution") - return nil + + return ParseConfig(c, cfg) }, Action: func(c *cli.Context) error { logger := NewLogger(cfg) @@ -134,6 +135,7 @@ func Server(cfg *config.Config) *cli.Command { grpc.Namespace(cfg.Server.Namespace), grpc.Address(cfg.Server.Address), grpc.Metrics(metrics), + grpc.Flags(flagset.RootWithConfig(config.New())), ) gr.Add(func() error { diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 60126809c..79a83e329 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -27,12 +27,14 @@ func RootWithConfig(cfg *config.Config) []cli.Flag { }, &cli.BoolFlag{ Name: "log-pretty", + Value: true, Usage: "Enable pretty logging", EnvVars: []string{"THUMBNAILS_LOG_PRETTY"}, Destination: &cfg.Log.Pretty, }, &cli.BoolFlag{ Name: "log-color", + Value: true, Usage: "Enable colored logging", EnvVars: []string{"THUMBNAILS_LOG_COLOR"}, Destination: &cfg.Log.Color, diff --git a/pkg/server/grpc/option.go b/pkg/server/grpc/option.go index 569480ff6..0d015101f 100644 --- a/pkg/server/grpc/option.go +++ b/pkg/server/grpc/option.go @@ -3,6 +3,7 @@ package grpc import ( "context" + "github.com/micro/cli/v2" "github.com/owncloud/ocis-pkg/v2/log" "github.com/owncloud/ocis-thumbnails/pkg/config" "github.com/owncloud/ocis-thumbnails/pkg/metrics" @@ -20,6 +21,7 @@ type Options struct { Config *config.Config Metrics *metrics.Metrics Namespace string + Flags []cli.Flag } // newOptions initializes the available default options. @@ -81,3 +83,10 @@ func Namespace(val string) Option { o.Namespace = val } } + +// Flags provides a function to set the flags option. +func Flags(flags []cli.Flag) Option { + return func(o *Options) { + o.Flags = append(o.Flags, flags...) + } +} diff --git a/pkg/server/grpc/server.go b/pkg/server/grpc/server.go index 37a79b755..3a7de2c93 100644 --- a/pkg/server/grpc/server.go +++ b/pkg/server/grpc/server.go @@ -18,6 +18,7 @@ func NewService(opts ...Option) grpc.Service { grpc.Version(version.String), grpc.Address(options.Address), grpc.Context(options.Context), + grpc.Flags(options.Flags...), ) var thumbnail proto.ThumbnailServiceHandler diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 914968c8d..23208d19f 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -12,6 +12,12 @@ import ( "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/storage" ) +type contextKey string + +const ( + authorization contextKey = imgsource.WebDavAuth +) + // NewService returns a service implementation for Service. func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { options := newOptions(opts...) @@ -48,7 +54,6 @@ type Thumbnail struct { func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error { encoder := thumbnail.EncoderForType(req.Filetype.String()) if encoder == nil { - // TODO: better error responses return fmt.Errorf("can't be encoded. filetype %s not supported", req.Filetype.String()) } r := g.resolutions.ClosestMatch(int(req.Width), int(req.Height)) @@ -67,8 +72,7 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rs } auth := req.Authorization - sCtx := context.WithValue(ctx, imgsource.WebDavAuth, auth) - // TODO: clean up error handling + sCtx := context.WithValue(ctx, authorization, auth) img, err := g.source.Get(sCtx, tr.ImagePath) if err != nil { return err From dc641901622b137cd4888f5774c6b89966f434a4 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 21 Apr 2020 09:15:37 +0000 Subject: [PATCH 45/80] Automated changelog update [skip ci] --- CHANGELOG.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27d896e40..019942df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,27 @@ -# Changes in 0.1.0 +# Changelog for [unreleased] (UNRELEASED) + +The following sections list the changes in ocis-thumbnails unreleased. + +[unreleased]: https://github.com/owncloud/ocis-thumbnails/compare/v0.1.0...master + +## Summary + +* Bugfix - Fix execution when passing program flags: [#15](https://github.com/owncloud/ocis-thumbnails/issues/15) + +## Details + +* Bugfix - Fix execution when passing program flags: [#15](https://github.com/owncloud/ocis-thumbnails/issues/15) + + The program flags where not correctly recognized because we didn't pass them to the micro + framework when initializing a grpc service. + + https://github.com/owncloud/ocis-thumbnails/issues/15 + +# Changelog for [0.1.0] (2020-03-31) + +The following sections list the changes in ocis-thumbnails 0.1.0. + +[0.1.0]: https://github.com/owncloud/ocis-thumbnails/compare/c43f3a33cb0b57d7e25ebc88c138d22e95f88cfe...v0.1.0 ## Summary From 16164473c6248b5ddd897449c45e46ebfecd3f7d Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 21 Apr 2020 11:55:34 +0200 Subject: [PATCH 46/80] prepare release 0.1.1 --- changelog/{unreleased => 0.1.1_2020-04-21}/fix-flags.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{unreleased => 0.1.1_2020-04-21}/fix-flags.md (100%) diff --git a/changelog/unreleased/fix-flags.md b/changelog/0.1.1_2020-04-21/fix-flags.md similarity index 100% rename from changelog/unreleased/fix-flags.md rename to changelog/0.1.1_2020-04-21/fix-flags.md From 5177069026fe0c410360d62828e68fec0fba7bbc Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 21 Apr 2020 10:05:54 +0000 Subject: [PATCH 47/80] Automated changelog update [skip ci] --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 019942df7..ff49878c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ -# Changelog for [unreleased] (UNRELEASED) +# Changelog for [0.1.1] (2020-04-21) -The following sections list the changes in ocis-thumbnails unreleased. +The following sections list the changes in ocis-thumbnails 0.1.1. -[unreleased]: https://github.com/owncloud/ocis-thumbnails/compare/v0.1.0...master +[0.1.1]: https://github.com/owncloud/ocis-thumbnails/compare/v0.1.0...v0.1.1 ## Summary From 68f798d0f96c358117dcebe54e7ae2a91ee59481 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 8 Apr 2020 13:34:36 +0200 Subject: [PATCH 48/80] implement integration tests --- go.mod | 1 + go.sum | 2 +- pkg/config/config.go | 5 ++ pkg/proto/v0/thumbnails.pb.micro_test.go | 87 ++++++++++++++++++++ pkg/proto/v0/thumbnails.pb_test.go | 98 +++++++++++++++++++++++ pkg/server/grpc/server.go | 9 +++ pkg/service/v0/option.go | 26 +++++- pkg/service/v0/service.go | 8 +- pkg/thumbnail/imgsource/filesystem.go | 39 +++++++++ testdata/oc.png | Bin 0 -> 69655 bytes 10 files changed, 264 insertions(+), 11 deletions(-) create mode 100644 pkg/proto/v0/thumbnails.pb.micro_test.go create mode 100644 pkg/proto/v0/thumbnails.pb_test.go create mode 100644 pkg/thumbnail/imgsource/filesystem.go create mode 100644 testdata/oc.png diff --git a/go.mod b/go.mod index 308d52f3d..fa5c2ce3a 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/restic/calens v0.2.0 github.com/spf13/afero v1.2.2 // indirect github.com/spf13/viper v1.5.0 + github.com/stretchr/testify v1.4.0 go.opencensus.io v0.22.2 golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a // indirect golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect diff --git a/go.sum b/go.sum index 774f1c0ca..c54ad9fda 100644 --- a/go.sum +++ b/go.sum @@ -455,7 +455,7 @@ github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukw github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= github.com/owncloud/ocis-pkg/v2 v2.2.1 h1:LK7WxHYugEFQ9NHTOz0EP8DRjbt51wXhyqruV03z6zI= github.com/owncloud/ocis-pkg/v2 v2.2.1/go.mod h1:MXv7QzsYsu4YWuyJxhq1kLLmJa/r5gbqHe1FXulMHaw= -github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= +ithub.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= diff --git a/pkg/config/config.go b/pkg/config/config.go index 37286518a..7d1b1645b 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -51,6 +51,11 @@ type WebDavSource struct { BaseURL string } +// FileSystemSource defines the available filesystem source configuration. +type FileSystemSource struct { + BasePath string +} + // Thumbnail defines the available thumbnail related configuration. type Thumbnail struct { Resolutions []string diff --git a/pkg/proto/v0/thumbnails.pb.micro_test.go b/pkg/proto/v0/thumbnails.pb.micro_test.go new file mode 100644 index 000000000..e4b3baf76 --- /dev/null +++ b/pkg/proto/v0/thumbnails.pb.micro_test.go @@ -0,0 +1,87 @@ +package proto_test + +import ( + "bytes" + "context" + "image" + "log" + "os" + "path/filepath" + "testing" + + "github.com/owncloud/ocis-pkg/v2/service/grpc" + "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/imgsource" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/storage" + "github.com/stretchr/testify/assert" + + svc "github.com/owncloud/ocis-thumbnails/pkg/service/v0" +) + +var service = grpc.Service{} + +func init() { + service = grpc.NewService( + grpc.Namespace("com.owncloud.api"), + grpc.Name("thumbnails"), + grpc.Address("localhost:9992"), + ) + + cfg := config.New() + cfg.Thumbnail.Resolutions = []string{"16x16", "32x32", "64x64", "128x128"} + + wd, _ := os.Getwd() + fsCfg := config.FileSystemSource{ + BasePath: filepath.Join(wd, "../../../testdata/"), + } + err := proto.RegisterThumbnailServiceHandler( + service.Server(), + svc.NewService( + svc.Config(cfg), + svc.ThumbnailStorage(storage.NewInMemoryStorage()), + svc.ThumbnailSource(imgsource.NewFileSystemSource(fsCfg)), + ), + ) + if err != nil { + log.Fatalf("could not register ThumbnailHandler: %v", err) + } + service.Server().Start() +} + +func TestGetThumbnail(t *testing.T) { + req := proto.GetRequest{ + Filepath: "oc.png", + Filetype: proto.GetRequest_PNG, + Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4", + Height: 32, + Width: 32, + } + client := service.Client() + cl := proto.NewThumbnailService("com.owncloud.api.thumbnails", client) + rsp, err := cl.GetThumbnail(context.Background(), &req) + if err != nil { + log.Fatalf("error %s", err.Error()) + } + assert.NotEmpty(t, rsp.GetThumbnail()) + + img, _, _ := image.Decode(bytes.NewReader(rsp.GetThumbnail())) + assert.Equal(t, 32, img.Bounds().Size().X) + + assert.Equal(t, "image/png", rsp.GetMimetype()) +} + +func TestGetThumbnailInvalidImage(t *testing.T) { + req := proto.GetRequest{ + Filepath: "invalid.png", + Filetype: proto.GetRequest_PNG, + Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4", + Height: 32, + Width: 32, + } + client := service.Client() + cl := proto.NewThumbnailService("com.owncloud.api.thumbnails", client) + _, err := cl.GetThumbnail(context.Background(), &req) + + assert.NotNil(t, err) +} diff --git a/pkg/proto/v0/thumbnails.pb_test.go b/pkg/proto/v0/thumbnails.pb_test.go new file mode 100644 index 000000000..56af7f298 --- /dev/null +++ b/pkg/proto/v0/thumbnails.pb_test.go @@ -0,0 +1,98 @@ +package proto_test + +import ( + "testing" + + "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" + "github.com/stretchr/testify/assert" +) + +type TestRequest struct { + testDataName string + filepath string + filetype proto.GetRequest_FileType + etag string + width int32 + height int32 + authorization string + expected string +} + +type TestResponse struct { + testDataName string + img []byte + mimetype string + expected string +} + +func TestRequestString(t *testing.T) { + + var tests = []TestRequest{ + { + "ASCII", + "Foo.jpg", + proto.GetRequest_JPG, + "33a64df551425fcc55e4d42a148795d9f25f89d4", + 24, + 24, + "Basic SGVXaG9SZWFkc1RoaXM6SXNTdHVwaWQK", + `filepath:"Foo.jpg" filetype:JPG etag:"33a64df551425fcc55e4d42a148795d9f25f89d4" width:24 height:24 authorization:"Basic SGVXaG9SZWFkc1RoaXM6SXNTdHVwaWQK" `, + }, + { + "UTF", + "मिलन.jpg", + proto.GetRequest_JPG, + "33a64df551425fcc55e4d42a148795d9f25f89d4", + 24, + 24, + "Basic SGVXaG9SZWFkc1RoaXM6SXNTdHVwaWQK", + `filepath:"\340\244\256\340\244\277\340\244\262\340\244\250.jpg" filetype:JPG etag:"33a64df551425fcc55e4d42a148795d9f25f89d4" width:24 height:24 authorization:"Basic SGVXaG9SZWFkc1RoaXM6SXNTdHVwaWQK" `, + }, + { + "PNG", + "Foo.png", + proto.GetRequest_PNG, + "33a64df551425fcc55e4d42a148795d9f25f89d4", + 24, + 24, + "Basic SGVXaG9SZWFkc1RoaXM6SXNTdHVwaWQK", + `filepath:"Foo.png" etag:"33a64df551425fcc55e4d42a148795d9f25f89d4" width:24 height:24 authorization:"Basic SGVXaG9SZWFkc1RoaXM6SXNTdHVwaWQK" `, + }, + } + + for _, testCase := range tests { + t.Run(testCase.testDataName, func(t *testing.T) { + req := proto.GetRequest{ + Filepath: testCase.filepath, + Filetype: testCase.filetype, + Etag: testCase.etag, + Height: testCase.height, + Width: testCase.width, + Authorization: testCase.authorization, + } + assert.Equal(t, testCase.expected, req.String()) + }) + } +} + +func TestResponseString(t *testing.T) { + var tests = []TestResponse{ + { + "ASCII", + []byte("image data"), + "image/png", + `thumbnail:"image data" mimetype:"image/png" `, + }, + } + + for _, testCase := range tests { + t.Run(testCase.testDataName, func(t *testing.T) { + response := proto.GetResponse{ + Thumbnail: testCase.img, + Mimetype: testCase.mimetype, + } + + assert.Equal(t, testCase.expected, response.String()) + }) + } +} diff --git a/pkg/server/grpc/server.go b/pkg/server/grpc/server.go index 3a7de2c93..ed67be00b 100644 --- a/pkg/server/grpc/server.go +++ b/pkg/server/grpc/server.go @@ -4,6 +4,8 @@ import ( "github.com/owncloud/ocis-pkg/v2/service/grpc" "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" svc "github.com/owncloud/ocis-thumbnails/pkg/service/v0" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/imgsource" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/storage" "github.com/owncloud/ocis-thumbnails/pkg/version" ) @@ -26,6 +28,13 @@ func NewService(opts ...Option) grpc.Service { thumbnail = svc.NewService( svc.Config(options.Config), svc.Logger(options.Logger), + svc.ThumbnailSource(imgsource.NewWebDavSource(options.Config.Thumbnail.WebDavSource)), + svc.ThumbnailStorage( + storage.NewFileSystemStorage( + options.Config.Thumbnail.FileSystemStorage, + options.Logger, + ), + ), ) thumbnail = svc.NewInstrument(thumbnail, options.Metrics) thumbnail = svc.NewLogging(thumbnail, options.Logger) diff --git a/pkg/service/v0/option.go b/pkg/service/v0/option.go index b8d33f7b7..c870c7096 100644 --- a/pkg/service/v0/option.go +++ b/pkg/service/v0/option.go @@ -3,8 +3,10 @@ package svc import ( "net/http" - "github.com/owncloud/ocis-thumbnails/pkg/config" "github.com/owncloud/ocis-pkg/v2/log" + "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/imgsource" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/storage" ) // Option defines a single option function. @@ -12,9 +14,11 @@ type Option func(o *Options) // Options defines the available options for this package. type Options struct { - Logger log.Logger - Config *config.Config - Middleware []func(http.Handler) http.Handler + Logger log.Logger + Config *config.Config + Middleware []func(http.Handler) http.Handler + ThumbnailStorage storage.Storage + ImageSource imgsource.Source } // newOptions initializes the available default options. @@ -48,3 +52,17 @@ func Middleware(val ...func(http.Handler) http.Handler) Option { o.Middleware = val } } + +// ThumbnailStorage provides a function to set the thumbnail storage option. +func ThumbnailStorage(val storage.Storage) Option { + return func(o *Options) { + o.ThumbnailStorage = val + } +} + +// ThumbnailSource provides a function to set the image source option. +func ThumbnailSource(val imgsource.Source) Option { + return func(o *Options) { + o.ImageSource = val + } +} diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 23208d19f..658a44a37 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -9,7 +9,6 @@ import ( "github.com/owncloud/ocis-thumbnails/pkg/thumbnail" "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/imgsource" "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/resolution" - "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/storage" ) type contextKey string @@ -28,14 +27,11 @@ func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { } svc := Thumbnail{ manager: thumbnail.NewSimpleManager( - storage.NewFileSystemStorage( - options.Config.Thumbnail.FileSystemStorage, - logger, - ), + options.ThumbnailStorage, logger, ), resolutions: resolutions, - source: imgsource.NewWebDavSource(options.Config.Thumbnail.WebDavSource), + source: options.ImageSource, logger: logger, } diff --git a/pkg/thumbnail/imgsource/filesystem.go b/pkg/thumbnail/imgsource/filesystem.go new file mode 100644 index 000000000..d84f59555 --- /dev/null +++ b/pkg/thumbnail/imgsource/filesystem.go @@ -0,0 +1,39 @@ +package imgsource + +import ( + "context" + "fmt" + "image" + "os" + "path/filepath" + + "github.com/owncloud/ocis-thumbnails/pkg/config" +) + +// NewFileSystemSource return a new FileSystem instance +func NewFileSystemSource(cfg config.FileSystemSource) FileSystem { + return FileSystem{ + basePath: cfg.BasePath, + } +} + +// FileSystem is an image source using the local file system +type FileSystem struct { + basePath string +} + +// Get retrieves an image from the filesystem. +func (s FileSystem) Get(ctx context.Context, file string) (image.Image, error) { + imgPath := filepath.Join(s.basePath, file) + f, err := os.Open(imgPath) + if err != nil { + return nil, fmt.Errorf("failed to load the file %s from %s error %s", file, imgPath, err.Error()) + } + + img, _, err := image.Decode(f) + if err != nil { + return nil, err + } + + return img, nil +} diff --git a/testdata/oc.png b/testdata/oc.png new file mode 100644 index 0000000000000000000000000000000000000000..034ad0e301197c8a04f6b7ee62d0f61e96bf4ebe GIT binary patch literal 69655 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

RmU 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 Date: Tue, 14 Apr 2020 12:24:24 +0200 Subject: [PATCH 49/80] fix tests --- pkg/proto/v0/thumbnails.pb.micro_test.go | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pkg/proto/v0/thumbnails.pb.micro_test.go b/pkg/proto/v0/thumbnails.pb.micro_test.go index e4b3baf76..22dd00571 100644 --- a/pkg/proto/v0/thumbnails.pb.micro_test.go +++ b/pkg/proto/v0/thumbnails.pb.micro_test.go @@ -49,6 +49,21 @@ func init() { service.Server().Start() } +func TestGetThumbnailInvalidImage(t *testing.T) { + req := proto.GetRequest{ + Filepath: "invalid.png", + Filetype: proto.GetRequest_PNG, + Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4", + Height: 32, + Width: 32, + } + client := service.Client() + cl := proto.NewThumbnailService("com.owncloud.api.thumbnails", client) + _, err := cl.GetThumbnail(context.Background(), &req) + + assert.NotNil(t, err) +} + func TestGetThumbnail(t *testing.T) { req := proto.GetRequest{ Filepath: "oc.png", @@ -70,18 +85,3 @@ func TestGetThumbnail(t *testing.T) { assert.Equal(t, "image/png", rsp.GetMimetype()) } - -func TestGetThumbnailInvalidImage(t *testing.T) { - req := proto.GetRequest{ - Filepath: "invalid.png", - Filetype: proto.GetRequest_PNG, - Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4", - Height: 32, - Width: 32, - } - client := service.Client() - cl := proto.NewThumbnailService("com.owncloud.api.thumbnails", client) - _, err := cl.GetThumbnail(context.Background(), &req) - - assert.NotNil(t, err) -} From f92a40879feb439e9bc96fc421b33fce569dd214 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 22 Apr 2020 12:48:28 +0000 Subject: [PATCH 50/80] Automated changelog update [skip ci] --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index fa5c2ce3a..41606b23e 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/restic/calens v0.2.0 github.com/spf13/afero v1.2.2 // indirect github.com/spf13/viper v1.5.0 - github.com/stretchr/testify v1.4.0 + github.com/stretchr/testify v1.5.1 go.opencensus.io v0.22.2 golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a // indirect golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect From 89a822c26f71e1eadf2a72d0fbd38ca6cf17294e Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 6 May 2020 13:56:29 +0200 Subject: [PATCH 51/80] implement functions to augment the context --- changelog/unreleased/fix-context-handling.md | 6 ++++++ go.sum | 2 +- pkg/service/v0/service.go | 8 +------- pkg/thumbnail/imgsource/imgsource.go | 19 +++++++++++++++++++ pkg/thumbnail/imgsource/webdav.go | 14 ++------------ 5 files changed, 29 insertions(+), 20 deletions(-) create mode 100644 changelog/unreleased/fix-context-handling.md diff --git a/changelog/unreleased/fix-context-handling.md b/changelog/unreleased/fix-context-handling.md new file mode 100644 index 000000000..61e1dba33 --- /dev/null +++ b/changelog/unreleased/fix-context-handling.md @@ -0,0 +1,6 @@ +Bugfix: Fix usage of context.Context + +The context was filled with a key defined in the package service but read with a key from the package imgsource. +Since `service.key` and `imgsource.key` are different types imgsource could not read the value provided by service. + +https://github.com/owncloud/ocis-thumbnails/issues/18 diff --git a/go.sum b/go.sum index c54ad9fda..774f1c0ca 100644 --- a/go.sum +++ b/go.sum @@ -455,7 +455,7 @@ github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukw github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= github.com/owncloud/ocis-pkg/v2 v2.2.1 h1:LK7WxHYugEFQ9NHTOz0EP8DRjbt51wXhyqruV03z6zI= github.com/owncloud/ocis-pkg/v2 v2.2.1/go.mod h1:MXv7QzsYsu4YWuyJxhq1kLLmJa/r5gbqHe1FXulMHaw= -ithub.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 658a44a37..59f144a0e 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -11,12 +11,6 @@ import ( "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/resolution" ) -type contextKey string - -const ( - authorization contextKey = imgsource.WebDavAuth -) - // NewService returns a service implementation for Service. func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { options := newOptions(opts...) @@ -68,7 +62,7 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rs } auth := req.Authorization - sCtx := context.WithValue(ctx, authorization, auth) + sCtx := imgsource.WithAuthorization(ctx, auth) img, err := g.source.Get(sCtx, tr.ImagePath) if err != nil { return err diff --git a/pkg/thumbnail/imgsource/imgsource.go b/pkg/thumbnail/imgsource/imgsource.go index 030a966d0..3b4adfd13 100644 --- a/pkg/thumbnail/imgsource/imgsource.go +++ b/pkg/thumbnail/imgsource/imgsource.go @@ -5,7 +5,26 @@ import ( "image" ) +type key int + +const ( + auth key = iota +) + // Source defines the interface for image sources type Source interface { Get(ctx context.Context, path string) (image.Image, error) } + +// WithAuthorization puts the authorization in the context. +func WithAuthorization(parent context.Context, authorization string) context.Context { + return context.WithValue(parent, auth, authorization) +} + +func authorization(ctx context.Context) string { + val := ctx.Value(auth) + if val == nil { + return "" + } + return val.(string) +} diff --git a/pkg/thumbnail/imgsource/webdav.go b/pkg/thumbnail/imgsource/webdav.go index ecb3832e1..65ccca89a 100644 --- a/pkg/thumbnail/imgsource/webdav.go +++ b/pkg/thumbnail/imgsource/webdav.go @@ -23,11 +23,6 @@ type WebDav struct { baseURL string } -const ( - // WebDavAuth is the parameter name for the autorization token - WebDavAuth = "Authorization" -) - // Get downloads the file from a webdav service func (s WebDav) Get(ctx context.Context, file string) (image.Image, error) { u, _ := url.Parse(s.baseURL) @@ -37,8 +32,8 @@ func (s WebDav) Get(ctx context.Context, file string) (image.Image, error) { return nil, fmt.Errorf("could not get the image \"%s\" error: %s", file, err.Error()) } - auth, ok := authorization(ctx) - if !ok { + auth := authorization(ctx) + if auth == "" { return nil, fmt.Errorf("could not get image \"%s\" error: authorization is missing", file) } req.Header.Add("Authorization", auth) @@ -59,8 +54,3 @@ func (s WebDav) Get(ctx context.Context, file string) (image.Image, error) { } return img, nil } - -func authorization(ctx context.Context) (string, bool) { - auth, ok := ctx.Value(WebDavAuth).(string) - return auth, ok -} From 464e76446ab3bb931ac2e695e28ae7002c0de333 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 6 May 2020 12:06:00 +0000 Subject: [PATCH 52/80] Automated changelog update [skip ci] --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff49878c6..93f24d805 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +# Changelog for [unreleased] (UNRELEASED) + +The following sections list the changes in ocis-thumbnails unreleased. + +[unreleased]: https://github.com/owncloud/ocis-thumbnails/compare/v0.1.1...master + +## Summary + +* Bugfix - Fix usage of context.Context: [#18](https://github.com/owncloud/ocis-thumbnails/issues/18) + +## Details + +* Bugfix - Fix usage of context.Context: [#18](https://github.com/owncloud/ocis-thumbnails/issues/18) + + The context was filled with a key defined in the package service but read with a key from the + package imgsource. Since `service.key` and `imgsource.key` are different types imgsource + could not read the value provided by service. + + https://github.com/owncloud/ocis-thumbnails/issues/18 + # Changelog for [0.1.1] (2020-04-21) The following sections list the changes in ocis-thumbnails 0.1.1. From 95691967e6f0abee143c8ba0f586ae019ae64758 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 11 May 2020 09:58:13 +0200 Subject: [PATCH 53/80] prepare release 0.1.2 --- .../{unreleased => 0.1.2_2020-05-11}/fix-context-handling.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{unreleased => 0.1.2_2020-05-11}/fix-context-handling.md (100%) diff --git a/changelog/unreleased/fix-context-handling.md b/changelog/0.1.2_2020-05-11/fix-context-handling.md similarity index 100% rename from changelog/unreleased/fix-context-handling.md rename to changelog/0.1.2_2020-05-11/fix-context-handling.md From 22b2f8307ccf029be7dbef5adb740f38cec75dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Mon, 11 May 2020 08:32:32 +0000 Subject: [PATCH 54/80] Automated changelog update [skip ci] --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93f24d805..573398709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ -# Changelog for [unreleased] (UNRELEASED) +# Changelog for [0.1.2] (2020-05-11) -The following sections list the changes in ocis-thumbnails unreleased. +The following sections list the changes in ocis-thumbnails 0.1.2. -[unreleased]: https://github.com/owncloud/ocis-thumbnails/compare/v0.1.1...master +[0.1.2]: https://github.com/owncloud/ocis-thumbnails/compare/v0.1.1...v0.1.2 ## Summary From 2f24a43c56d0ac5534b394b5e4e9a9e2e0f0a764 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 18 May 2020 14:55:34 +0200 Subject: [PATCH 55/80] fix code smells --- changelog/unreleased/fix-code-smells.md | 6 ++++++ pkg/metrics/metrics.go | 6 +++--- pkg/server/grpc/server.go | 2 +- pkg/thumbnail/imgsource/filesystem.go | 2 +- pkg/thumbnail/storage/filesystem.go | 9 +++++++-- 5 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 changelog/unreleased/fix-code-smells.md diff --git a/changelog/unreleased/fix-code-smells.md b/changelog/unreleased/fix-code-smells.md new file mode 100644 index 000000000..8d15094a8 --- /dev/null +++ b/changelog/unreleased/fix-code-smells.md @@ -0,0 +1,6 @@ +Change: refactor code to remove code smells + +Scanning the code using a static code analyzer showed some code smells. +This change fixes them. + +https://github.com/owncloud/ocis-thumbnails/issues/21 diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 0ecf2f55c..8139f7ba1 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -40,15 +40,15 @@ func New() *Metrics { }, []string{}), } - prometheus.Register( + _ = prometheus.Register( m.Counter, ) - prometheus.Register( + _ = prometheus.Register( m.Latency, ) - prometheus.Register( + _ = prometheus.Register( m.Duration, ) diff --git a/pkg/server/grpc/server.go b/pkg/server/grpc/server.go index ed67be00b..588abbbb2 100644 --- a/pkg/server/grpc/server.go +++ b/pkg/server/grpc/server.go @@ -40,7 +40,7 @@ func NewService(opts ...Option) grpc.Service { thumbnail = svc.NewLogging(thumbnail, options.Logger) } - proto.RegisterThumbnailServiceHandler( + _ = proto.RegisterThumbnailServiceHandler( service.Server(), thumbnail, ) diff --git a/pkg/thumbnail/imgsource/filesystem.go b/pkg/thumbnail/imgsource/filesystem.go index d84f59555..78439db70 100644 --- a/pkg/thumbnail/imgsource/filesystem.go +++ b/pkg/thumbnail/imgsource/filesystem.go @@ -25,7 +25,7 @@ type FileSystem struct { // Get retrieves an image from the filesystem. func (s FileSystem) Get(ctx context.Context, file string) (image.Image, error) { imgPath := filepath.Join(s.basePath, file) - f, err := os.Open(imgPath) + f, err := os.Open(filepath.Clean(imgPath)) if err != nil { return nil, fmt.Errorf("failed to load the file %s from %s error %s", file, imgPath, err.Error()) } diff --git a/pkg/thumbnail/storage/filesystem.go b/pkg/thumbnail/storage/filesystem.go index dc45d9de0..83fbef88e 100644 --- a/pkg/thumbnail/storage/filesystem.go +++ b/pkg/thumbnail/storage/filesystem.go @@ -27,7 +27,8 @@ type FileSystem struct { // Get loads the image from the file system. func (s FileSystem) Get(key string) []byte { - content, err := ioutil.ReadFile(filepath.Join(s.dir, key)) + path := filepath.Join(s.dir, key) + content, err := ioutil.ReadFile(filepath.Clean(path)) if err != nil { s.logger.Warn().Err(err).Msgf("could not read file %s", key) return nil @@ -48,7 +49,11 @@ func (s FileSystem) Set(key string, img []byte) error { if err != nil { return fmt.Errorf("could not create file \"%s\" error: %s", key, err.Error()) } - defer f.Close() + defer func() { + if err := f.Close(); err != nil { + s.logger.Warn().Err(err).Msg("closing file resulted in an error") + } + }() _, err = f.Write(img) if err != nil { return fmt.Errorf("could not write to file \"%s\" error: %s", key, err.Error()) From 2196b101e411c260b47de43b4178a0fdf08ae4ac Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 19 May 2020 07:36:04 +0000 Subject: [PATCH 56/80] Automated changelog update [skip ci] --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 573398709..8a546efa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# Changelog for [unreleased] (UNRELEASED) + +The following sections list the changes in ocis-thumbnails unreleased. + +[unreleased]: https://github.com/owncloud/ocis-thumbnails/compare/v0.1.2...master + +## Summary + +* Change - Refactor code to remove code smells: [#21](https://github.com/owncloud/ocis-thumbnails/issues/21) + +## Details + +* Change - Refactor code to remove code smells: [#21](https://github.com/owncloud/ocis-thumbnails/issues/21) + + Scanning the code using a static code analyzer showed some code smells. This change fixes them. + + https://github.com/owncloud/ocis-thumbnails/issues/21 + # Changelog for [0.1.2] (2020-05-11) The following sections list the changes in ocis-thumbnails 0.1.2. From 89abe9cca9f09d446537540e265d37e31d48efbc Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 19 May 2020 11:02:14 +0200 Subject: [PATCH 57/80] add more default resolutions --- changelog/unreleased/add-resolutions.md | 6 ++++++ pkg/flagset/flagset.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 changelog/unreleased/add-resolutions.md diff --git a/changelog/unreleased/add-resolutions.md b/changelog/unreleased/add-resolutions.md new file mode 100644 index 000000000..b2449e9f3 --- /dev/null +++ b/changelog/unreleased/add-resolutions.md @@ -0,0 +1,6 @@ +Change: add more default resolutions + +The `ocis-thumbnails` service was also used by the mediaviewer but the returned +images were too small since the highest resolution was 128x128 pixels. + +https://github.com/owncloud/ocis-thumbnails/issues/23 diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 79a83e329..d5566b45c 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -155,7 +155,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { }, &cli.StringSliceFlag{ Name: "thumbnail-resolution", - Value: cli.NewStringSlice("16x16", "32x32", "64x64", "128x128"), + Value: cli.NewStringSlice("16x16", "32x32", "64x64", "128x128", "1920x1080", "3840x2160", "7680x4320"), Usage: "--thumbnail-resolution 16x16 [--thumbnail-resolution 32x32]", EnvVars: []string{"THUMBNAILS_RESOLUTIONS"}, }, From 7867c5389055a9235129d6998b1e51633e579dd0 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 19 May 2020 09:32:16 +0000 Subject: [PATCH 58/80] Automated changelog update [skip ci] --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a546efa4..52b298e8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,19 @@ The following sections list the changes in ocis-thumbnails unreleased. ## Summary +* Change - Add more default resolutions: [#23](https://github.com/owncloud/ocis-thumbnails/issues/23) * Change - Refactor code to remove code smells: [#21](https://github.com/owncloud/ocis-thumbnails/issues/21) ## Details +* Change - Add more default resolutions: [#23](https://github.com/owncloud/ocis-thumbnails/issues/23) + + The `ocis-thumbnails` service was also used by the mediaviewer but the returned images were + too small since the highest resolution was 128x128 pixels. + + https://github.com/owncloud/ocis-thumbnails/issues/23 + + * Change - Refactor code to remove code smells: [#21](https://github.com/owncloud/ocis-thumbnails/issues/21) Scanning the code using a static code analyzer showed some code smells. This change fixes them. From a7bd4d27eaa09b42b5a337f9019e96a3ab536e29 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 20 May 2020 10:44:33 +0200 Subject: [PATCH 59/80] add documentation about the releasing process --- docs/grpc.md | 2 +- docs/license.md | 2 +- docs/releasing.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ templates/grpc.tmpl | 2 +- 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 docs/releasing.md diff --git a/docs/grpc.md b/docs/grpc.md index a4b8e2f9d..27d9c2376 100644 --- a/docs/grpc.md +++ b/docs/grpc.md @@ -1,7 +1,7 @@ --- title: "GRPC API" date: 2018-05-02T00:00:00+00:00 -weight: 40 +weight: 50 geekdocRepo: https://github.com/owncloud/ocis-thumbnails geekdocEditPath: edit/master/docs geekdocFilePath: grpc.md diff --git a/docs/license.md b/docs/license.md index 273e92c3b..8a930291c 100644 --- a/docs/license.md +++ b/docs/license.md @@ -1,7 +1,7 @@ --- title: "License" date: 2018-05-02T00:00:00+00:00 -weight: 50 +weight: 60 geekdocRepo: https://github.com/owncloud/ocis-thumbnails geekdocEditPath: edit/master/docs geekdocFilePath: license.md diff --git a/docs/releasing.md b/docs/releasing.md new file mode 100644 index 000000000..3ee9155b9 --- /dev/null +++ b/docs/releasing.md @@ -0,0 +1,44 @@ +--- +title: "Releasing" +date: 2018-05-02T00:00:00+00:00 +weight: 40 +geekdocRepo: https://github.com/owncloud/ocis-thumbnails +geekdocEditPath: edit/master/docs +geekdocFilePath: releasing.md +--- + +{{< toc >}} + +To release a new version of ocis-thumbnails, you have to follow a few simple steps. + +## Preperation + +1. Create a new branch e.g. `prepare-release-x.x.x` where `x.x.x` is the version you want to release. +2. Checkout the preparation branch. +3. Create a new changelog folder and move the unreleased snippets there. +{{< highlight txt >}} +mkdir changelog/x.x.x_yyyy-MM-dd/ # yyyy-MM-dd is the current date +mv changelog/unreleased/* changelog/x.x.x_yyyy-MM-dd/ +{{< / highlight >}} +4. Commit and push the changes +{{< highlight txt >}} +git add --all +git commit -m "prepare release x.x.x" +git push +{{< / highlight >}} +5. Create a pull request to the master branch. + +## Release +1. After the preparation branch has been merged update your local master. +{{< highlight txt >}} +git checkout master +git pull +{{< / highlight >}} +2. Create a new tag (preferably signed). +{{< highlight txt >}} +git tag -s vx.x.x -m "release vx.x.x" +git push --tags +{{< / highlight >}} + + +Congratulations, you just released ocis-thumbnails! diff --git a/templates/grpc.tmpl b/templates/grpc.tmpl index d7c9da3e5..545ab202d 100644 --- a/templates/grpc.tmpl +++ b/templates/grpc.tmpl @@ -1,7 +1,7 @@ --- title: "GRPC API" date: 2018-05-02T00:00:00+00:00 -weight: 40 +weight: 50 geekdocRepo: https://github.com/owncloud/ocis-thumbnails geekdocEditPath: edit/master/docs geekdocFilePath: grpc.md From 486358cd2fed81fc5f1cb53dd40e243b115fbe37 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 20 May 2020 10:59:59 +0200 Subject: [PATCH 60/80] change branch name and add missing step --- docs/releasing.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/releasing.md b/docs/releasing.md index 3ee9155b9..b3bc0c19a 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -13,7 +13,7 @@ To release a new version of ocis-thumbnails, you have to follow a few simple ste ## Preperation -1. Create a new branch e.g. `prepare-release-x.x.x` where `x.x.x` is the version you want to release. +1. Create a new branch e.g. `release-x.x.x` where `x.x.x` is the version you want to release. 2. Checkout the preparation branch. 3. Create a new changelog folder and move the unreleased snippets there. {{< highlight txt >}} @@ -39,6 +39,7 @@ git pull git tag -s vx.x.x -m "release vx.x.x" git push --tags {{< / highlight >}} +3. Wait for CI and check that the GitHub release was published. Congratulations, you just released ocis-thumbnails! From 4ae1a64376741987ac206e8b4c59aaa20b6cf9ae Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 27 May 2020 13:17:10 +0200 Subject: [PATCH 61/80] limit thumbnails a user can access to his own. --- .../unreleased/group-thumbnails-by-users.md | 6 + go.mod | 3 + go.sum | 2 + pkg/proto/v0/thumbnails.pb.micro_test.go | 11 +- pkg/service/v0/service.go | 34 ++++- pkg/thumbnail/storage/filesystem.go | 133 +++++++++++++----- pkg/thumbnail/storage/inmemory.go | 19 ++- pkg/thumbnail/storage/storage.go | 4 +- pkg/thumbnail/thumbnails.go | 5 +- 9 files changed, 163 insertions(+), 54 deletions(-) create mode 100644 changelog/unreleased/group-thumbnails-by-users.md diff --git a/changelog/unreleased/group-thumbnails-by-users.md b/changelog/unreleased/group-thumbnails-by-users.md new file mode 100644 index 000000000..2ccb43a22 --- /dev/null +++ b/changelog/unreleased/group-thumbnails-by-users.md @@ -0,0 +1,6 @@ +Enhancement: Limit users to access own thumbnails + +Users of the service can no longer request thumbnails of another users by guessing the etag. +The thumbnails are now only accessible by the users who created the thumbnail. + +https://github.com/owncloud/ocis-thumbnails/issues/5 diff --git a/go.mod b/go.mod index 41606b23e..3096f178d 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31 // indirect github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d // indirect github.com/golang/protobuf v1.3.2 + github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5 // indirect github.com/imdario/mergo v0.3.9 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mattn/go-colorable v0.1.2 // indirect @@ -23,6 +24,7 @@ require ( github.com/oklog/run v1.0.0 github.com/openzipkin/zipkin-go v0.2.2 github.com/owncloud/ocis-pkg/v2 v2.2.1 + github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.2.1 github.com/restic/calens v0.2.0 github.com/spf13/afero v1.2.2 // indirect @@ -32,5 +34,6 @@ require ( golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a // indirect golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/tools v0.0.0-20200421042724-cfa8b22178d2 // indirect + gopkg.in/square/go-jose.v2 v2.3.1 honnef.co/go/tools v0.0.1-2020.1.3 // indirect ) diff --git a/go.sum b/go.sum index 774f1c0ca..07112a389 100644 --- a/go.sum +++ b/go.sum @@ -263,6 +263,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5 h1:FdBGmSkD2QpQzRWup//SGObvWf2nq89zj9+ta9OvI3A= +github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5/go.mod h1:0YZ2wQSuwviXXXGUiK6zXzskyBLAbLXhamxzcFHSLoM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= diff --git a/pkg/proto/v0/thumbnails.pb.micro_test.go b/pkg/proto/v0/thumbnails.pb.micro_test.go index 22dd00571..4f7ccf556 100644 --- a/pkg/proto/v0/thumbnails.pb.micro_test.go +++ b/pkg/proto/v0/thumbnails.pb.micro_test.go @@ -66,11 +66,12 @@ func TestGetThumbnailInvalidImage(t *testing.T) { func TestGetThumbnail(t *testing.T) { req := proto.GetRequest{ - Filepath: "oc.png", - Filetype: proto.GetRequest_PNG, - Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4", - Height: 32, - Width: 32, + Filepath: "oc.png", + Filetype: proto.GetRequest_PNG, + Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4", + Height: 32, + Width: 32, + Authorization: "Bearer eyJhbGciOiJQUzI1NiIsImtpZCI6IiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJwaG9lbml4IiwiZXhwIjoxNTkwNTc1Mzk4LCJqdGkiOiJqUEw5c1A3UUEzY0diYi1yRnhkSjJCWnFPc1BDTDg1ZyIsImlhdCI6MTU5MDU3NDc5OCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6OTIwMCIsInN1YiI6Ilh0U2lfbWl5V1NCLXBrdkdueFBvQzVBNGZsaWgwVUNMZ3ZVN2NMd2ptakNLWDdGWW4ySFdrNnJSQ0V1eTJHNXFBeV95TVFjX0ZLOWFORmhVTXJYMnBRQGtvbm5lY3QiLCJrYy5pc0FjY2Vzc1Rva2VuIjp0cnVlLCJrYy5hdXRob3JpemVkU2NvcGVzIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJlbWFpbCJdLCJrYy5pZGVudGl0eSI6eyJrYy5pLmRuIjoiRWluc3RlaW4iLCJrYy5pLmlkIjoiY249ZWluc3RlaW4sb3U9dXNlcnMsZGM9ZXhhbXBsZSxkYz1vcmciLCJrYy5pLnVuIjoiZWluc3RlaW4ifSwia2MucHJvdmlkZXIiOiJpZGVudGlmaWVyLWxkYXAifQ.FSDe4vzwYpHbNfckBON5EI-01MS_dYFxenddqfJPzjlAEMEH2FFn2xQHCsxhC7wSxivhjV7Z5eRoNUR606keA64Tjs8pJBNECSptBMmE_xfAlc6X5IFILgDnR5bBu6Z2hhu-dVj72Hcyvo_X__OeWekYu7oyoXW41Mw3ayiUAwjCAzV3WPOAJ_r0zbW68_m29BgH3BoSxaF6lmjStIIAIyw7IBZ2QXb_FvGouknmfeWlGL9lkFPGL_dYKwjWieG947nY4Kg8IvHByEbw-xlY3L2EdA7Q8ZMbqdX7GzjtEIVYvCT4-TxWRcmB3SmO-Z8CVq27NHlKm3aZ0k2PS8Ga1w", } client := service.Client() cl := proto.NewThumbnailService("com.owncloud.api.thumbnails", client) diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 59f144a0e..8f8d10d22 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -4,11 +4,14 @@ import ( "context" "fmt" + "gopkg.in/square/go-jose.v2/jwt" + "github.com/owncloud/ocis-pkg/v2/log" v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" "github.com/owncloud/ocis-thumbnails/pkg/thumbnail" "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/imgsource" "github.com/owncloud/ocis-thumbnails/pkg/thumbnail/resolution" + "github.com/pkg/errors" ) // NewService returns a service implementation for Service. @@ -47,11 +50,22 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rs return fmt.Errorf("can't be encoded. filetype %s not supported", req.Filetype.String()) } r := g.resolutions.ClosestMatch(int(req.Width), int(req.Height)) + + auth := req.Authorization + if auth == "" { + return fmt.Errorf("authorization is missing") + } + username, err := usernameFromAuthorization(auth) + if err != nil { + return err + } + tr := thumbnail.Request{ Resolution: r, ImagePath: req.Filepath, Encoder: encoder, ETag: req.Etag, + Username: username, } thumbnail := g.manager.GetStored(tr) @@ -61,7 +75,6 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rs return nil } - auth := req.Authorization sCtx := imgsource.WithAuthorization(ctx, auth) img, err := g.source.Get(sCtx, tr.ImagePath) if err != nil { @@ -79,3 +92,22 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rs rsp.Mimetype = tr.Encoder.MimeType() return nil } + +func usernameFromAuthorization(auth string) (string, error) { + tokenString := auth[len("Bearer "):] // strip the bearer prefix + + var claims map[string]interface{} + token, err := jwt.ParseSigned(tokenString) + if err != nil { + return "", errors.Wrap(err, "could not parse auth token") + } + err = token.UnsafeClaimsWithoutVerification(&claims) + if err != nil { + return "", errors.Wrap(err, "could not get claims from auth token") + } + + identityMap := claims["kc.identity"].(map[string]interface{}) + username := identityMap["kc.i.un"].(string) + + return username, nil +} diff --git a/pkg/thumbnail/storage/filesystem.go b/pkg/thumbnail/storage/filesystem.go index 83fbef88e..7173d813f 100644 --- a/pkg/thumbnail/storage/filesystem.go +++ b/pkg/thumbnail/storage/filesystem.go @@ -1,64 +1,62 @@ package storage import ( - "bytes" - "fmt" + "crypto/sha256" + "encoding/hex" "io/ioutil" "os" "path/filepath" + "strings" + "sync" "github.com/owncloud/ocis-pkg/v2/log" "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/pkg/errors" +) + +const ( + usersDir = "users" + filesDir = "files" ) // NewFileSystemStorage creates a new instanz of FileSystem -func NewFileSystemStorage(cfg config.FileSystemStorage, logger log.Logger) FileSystem { - return FileSystem{ - dir: cfg.RootDirectory, +func NewFileSystemStorage(cfg config.FileSystemStorage, logger log.Logger) *FileSystem { + return &FileSystem{ + root: cfg.RootDirectory, logger: logger, } } // FileSystem represents a storage for the thumbnails using the local file system. type FileSystem struct { - dir string + root string logger log.Logger + mux sync.Mutex } // Get loads the image from the file system. -func (s FileSystem) Get(key string) []byte { - path := filepath.Join(s.dir, key) - content, err := ioutil.ReadFile(filepath.Clean(path)) +func (s *FileSystem) Get(username string, key string) []byte { + userDir := s.userDir(username) + img := filepath.Join(userDir, key) + content, err := ioutil.ReadFile(img) if err != nil { s.logger.Warn().Err(err).Msgf("could not read file %s", key) return nil } - return content } // Set writes the image to the file system. -func (s FileSystem) Set(key string, img []byte) error { - path := filepath.Join(s.dir, key) - dir := filepath.Dir(path) - if err := os.MkdirAll(dir, 0700); err != nil { - return fmt.Errorf("error while creating directory %s", dir) - } - - f, err := os.Create(path) +func (s *FileSystem) Set(username string, key string, img []byte) error { + _, err := s.storeImage(key, img) if err != nil { - return fmt.Errorf("could not create file \"%s\" error: %s", key, err.Error()) + return errors.Wrap(err, "could not store image") } - defer func() { - if err := f.Close(); err != nil { - s.logger.Warn().Err(err).Msg("closing file resulted in an error") - } - }() - _, err = f.Write(img) + userDir, err := s.createUserDir(username) if err != nil { - return fmt.Errorf("could not write to file \"%s\" error: %s", key, err.Error()) + return err } - return nil + return s.linkImageToUserDir(key, userDir) } // BuildKey generate the unique key for a thumbnail. @@ -69,19 +67,78 @@ func (s FileSystem) Set(key string, img []byte) error { // e.g. 97/9f/4c8db98f7b82e768ef478d3c8612/500x300.png // // The key also represents the path to the thumbnail in the filesystem under the configured root directory. -func (s FileSystem) BuildKey(r Request) string { +func (s *FileSystem) BuildKey(r Request) string { etag := r.ETag filetype := r.Types[0] filename := r.Resolution.String() + "." + filetype - key := new(bytes.Buffer) - key.WriteString(etag[:2]) - key.WriteRune('/') - key.WriteString(etag[2:4]) - key.WriteRune('/') - key.WriteString(etag[4:]) - key.WriteRune('/') - key.WriteString(filename) - - return key.String() + return filepath.Join(etag[:2], etag[2:4], etag[4:], filename) +} + +func (s *FileSystem) rootDir(key string) string { + p := strings.Split(key, string(os.PathSeparator)) + return p[0] +} + +func (s *FileSystem) storeImage(key string, img []byte) (string, error) { + s.mux.Lock() + defer s.mux.Unlock() + imgPath := filepath.Join(s.root, filesDir, key) + dir := filepath.Dir(imgPath) + if err := os.MkdirAll(dir, 0700); err != nil { + return "", errors.Wrapf(err, "error while creating directory %s", dir) + } + + if _, err := os.Stat(imgPath); os.IsNotExist(err) { + f, err := os.Create(imgPath) + if err != nil { + return "", errors.Wrapf(err, "could not create file \"%s\"", key) + } + defer f.Close() + + _, err = f.Write(img) + if err != nil { + return "", errors.Wrapf(err, "could not write to file \"%s\"", key) + } + } + + return imgPath, nil +} + +// userDir returns the path to the user directory. +// The username is hashed before appending it on the path to prevent bugs caused by invalid folder names. +// Also the hash is then splitted up in three parts that results in a path which looks as follows: +// /users/<3 characters>/<3 characters>/<48 characters>/ +// This will balance the folders in setups with many users. +func (s *FileSystem) userDir(username string) string { + hash := sha256.New224() + hash.Write([]byte(username)) + unHash := hex.EncodeToString(hash.Sum(nil)) // 224 Bits or 224 / 4 = 56 characters. + + return filepath.Join(s.root, usersDir, unHash[:3], unHash[3:6], unHash[6:]) +} + +func (s *FileSystem) createUserDir(username string) (string, error) { + userDir := s.userDir(username) + if err := os.MkdirAll(userDir, 0700); err != nil { + return "", errors.Wrapf(err, "could not create userDir: %s", userDir) + } + + return userDir, nil +} + +// linkImageToUserDir links the stored images to the user directory. +// The goal is to minimize disk usage by linking to the images if they already exist and avoid file duplicaiton. +func (s *FileSystem) linkImageToUserDir(key string, userDir string) error { + imgRootDir := s.rootDir(key) + + s.mux.Lock() + defer s.mux.Unlock() + err := os.Symlink(filepath.Join(s.root, filesDir, imgRootDir), filepath.Join(userDir, imgRootDir)) + if err != nil { + if !os.IsExist(err) { + return errors.Wrap(err, "could not link image to userdir") + } + } + return nil } diff --git a/pkg/thumbnail/storage/inmemory.go b/pkg/thumbnail/storage/inmemory.go index 12017eed0..90afe95ab 100644 --- a/pkg/thumbnail/storage/inmemory.go +++ b/pkg/thumbnail/storage/inmemory.go @@ -7,24 +7,31 @@ import ( // NewInMemoryStorage creates a new InMemory instance. func NewInMemoryStorage() InMemory { return InMemory{ - store: make(map[string][]byte), + store: make(map[string]map[string][]byte), } } // InMemory represents an in memory storage for thumbnails // Can be used during development type InMemory struct { - store map[string][]byte + store map[string]map[string][]byte } // Get loads the thumbnail from memory. -func (s InMemory) Get(key string) []byte { - return s.store[key] +func (s InMemory) Get(username string, key string) []byte { + userImages := s.store[username] + if userImages == nil { + return nil + } + return s.store[username][key] } // Set stores the thumbnail in memory. -func (s InMemory) Set(key string, thumbnail []byte) error { - s.store[key] = thumbnail +func (s InMemory) Set(username string, key string, thumbnail []byte) error { + if _, ok := s.store[username]; !ok { + s.store[username] = make(map[string][]byte) + } + s.store[username][key] = thumbnail return nil } diff --git a/pkg/thumbnail/storage/storage.go b/pkg/thumbnail/storage/storage.go index a23a757d3..55d968269 100644 --- a/pkg/thumbnail/storage/storage.go +++ b/pkg/thumbnail/storage/storage.go @@ -11,7 +11,7 @@ type Request struct { // Storage defines the interface for a thumbnail store. type Storage interface { - Get(string) []byte - Set(string, []byte) error + Get(string, string) []byte + Set(string, string, []byte) error BuildKey(Request) string } diff --git a/pkg/thumbnail/thumbnails.go b/pkg/thumbnail/thumbnails.go index 846cd6013..d600b53ad 100644 --- a/pkg/thumbnail/thumbnails.go +++ b/pkg/thumbnail/thumbnails.go @@ -16,6 +16,7 @@ type Request struct { ImagePath string Encoder Encoder ETag string + Username string } // Manager is responsible for generating thumbnails @@ -53,7 +54,7 @@ func (s SimpleManager) Get(r Request, img image.Image) ([]byte, error) { return nil, err } bytes := buf.Bytes() - err = s.storage.Set(key, bytes) + err = s.storage.Set(r.Username, key, bytes) if err != nil { s.logger.Warn().Err(err).Msg("could not store thumbnail") } @@ -64,7 +65,7 @@ func (s SimpleManager) Get(r Request, img image.Image) ([]byte, error) { // If there is no cached thumbnail it will return nil func (s SimpleManager) GetStored(r Request) []byte { key := s.storage.BuildKey(mapToStorageRequest(r)) - stored := s.storage.Get(key) + stored := s.storage.Get(r.Username, key) return stored } From dcb2a13c6198c4dda19d894b812ad244e052347b Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 27 May 2020 11:27:16 +0000 Subject: [PATCH 62/80] Automated changelog update [skip ci] --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52b298e8f..4cd245b94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The following sections list the changes in ocis-thumbnails unreleased. * Change - Add more default resolutions: [#23](https://github.com/owncloud/ocis-thumbnails/issues/23) * Change - Refactor code to remove code smells: [#21](https://github.com/owncloud/ocis-thumbnails/issues/21) +* Enhancement - Limit users to access own thumbnails: [#5](https://github.com/owncloud/ocis-thumbnails/issues/5) ## Details @@ -25,6 +26,14 @@ The following sections list the changes in ocis-thumbnails unreleased. https://github.com/owncloud/ocis-thumbnails/issues/21 + +* Enhancement - Limit users to access own thumbnails: [#5](https://github.com/owncloud/ocis-thumbnails/issues/5) + + Users of the service can no longer request thumbnails of another users by guessing the etag. The + thumbnails are now only accessible by the users who created the thumbnail. + + https://github.com/owncloud/ocis-thumbnails/issues/5 + # Changelog for [0.1.2] (2020-05-11) The following sections list the changes in ocis-thumbnails 0.1.2. From 73476e49236d88b4f9de748f29f7ee2e00da399d Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 29 May 2020 23:18:41 +0200 Subject: [PATCH 63/80] add sonarcloud quality badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7d6963a34..9e130c70b 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![Go Doc](https://godoc.org/github.com/owncloud/ocis-thumbnails?status.svg)](http://godoc.org/github.com/owncloud/ocis-thumbnails) [![Go Report](http://goreportcard.com/badge/github.com/owncloud/ocis-thumbnails)](http://goreportcard.com/report/github.com/owncloud/ocis-thumbnails) [![](https://images.microbadger.com/badges/image/owncloud/ocis-thumbnails.svg)](http://microbadger.com/images/owncloud/ocis-thumbnails "Get your own image badge on microbadger.com") +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=owncloud_ocis-thumbnails&metric=alert_status)](https://sonarcloud.io/dashboard?id=owncloud_ocis-thumbnails) **This project is under heavy development, it's not in a working state yet!** From 9b3279eb277d30f4dbd2e9986fe8cecac2c3e970 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Fri, 10 Jul 2020 12:06:22 +0200 Subject: [PATCH 64/80] Add sonarCloud Code Analyzer Signed-off-by: Michael Barz --- .drone.star | 10 ++++++++++ sonar-project.properties | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 sonar-project.properties diff --git a/.drone.star b/.drone.star index df632bf2a..8eb9e76a8 100644 --- a/.drone.star +++ b/.drone.star @@ -126,6 +126,16 @@ def testing(ctx): }, }, }, + { + 'name': 'sonarcloud', + 'image': 'sonarsource/sonar-scanner-cli', + 'pull': 'always', + 'environment': { + 'SONAR_TOKEN': { + 'from_secret': 'sonar_token', + }, + }, + }, ], 'volumes': [ { diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 000000000..aede03677 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,34 @@ + +sonar.projectKey=owncloud_ocis-thumbnails +sonar.organization=owncloud-1 +sonar.projectName=ownCloud Infinite Scale: THUMBNAILS +sonar.projectVersion=1.0 +sonar.host.url=https://sonarcloud.io + +# ===================================================== +# Meta-data for the project +# ===================================================== + +sonar.links.homepage=https://github.com/owncloud/ocis-thumbnails +sonar.links.ci=https://cloud.drone.io/owncloud/ocis-thumbnails/ +sonar.links.scm=https://github.com/owncloud/ocis-thumbnails +sonar.links.issue=https://github.com/owncloud/ocis-thumbnails/issues + + +# ===================================================== +# Properties that will be shared amongst all modules +# ===================================================== + +# SQ standard properties +sonar.sources=. + +# Pull Requests +sonar.pullrequest.provider=GitHub +sonar.pullrequest.github.repository=owncloud/ocis-thumbnails +sonar.pullrequest.base=${env.DRONE_BRANCH} +sonar.pullrequest.branch=${env.DRONE_SOURCE_BRANCH} +sonar.pullrequest.key=${env.DRONE_PULL_REQUEST} + + +# Properties specific to language plugins: +sonar.go.coverage.reportPaths=coverage.out \ No newline at end of file From c9daeb52fa88b239fcfa8f161150e61d36ce35f7 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 10 Jul 2020 16:37:46 +0200 Subject: [PATCH 65/80] tweak sonar config only set pullrequest config if the build was triggered by a pull request Signed-off-by: David Christofas --- .drone.star | 5 +++++ sonar-project.properties | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.drone.star b/.drone.star index 8eb9e76a8..24da7a04d 100644 --- a/.drone.star +++ b/.drone.star @@ -134,6 +134,11 @@ def testing(ctx): 'SONAR_TOKEN': { 'from_secret': 'sonar_token', }, + 'SONAR_PULL_REQUEST_PROVIDER': 'GitHub' if ctx.build.event == 'pull_request' else None, + 'SONAR_PULL_REQUEST_REPO': 'owncloud/ocis-thumbnails' if ctx.build.event == 'pull_request' else None, + 'SONAR_PULL_REQUEST_BASE': 'master' if ctx.build.event == 'pull_request' else None, + 'SONAR_PULL_REQUEST_BRANCH': ctx.build.source if ctx.build.event == 'pull_request' else None, + 'SONAR_PULL_REQUEST_KEY': ctx.build.ref if ctx.build.event == 'pull_request' else None, }, }, ], diff --git a/sonar-project.properties b/sonar-project.properties index aede03677..05a5d21ff 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -23,11 +23,11 @@ sonar.links.issue=https://github.com/owncloud/ocis-thumbnails/issues sonar.sources=. # Pull Requests -sonar.pullrequest.provider=GitHub -sonar.pullrequest.github.repository=owncloud/ocis-thumbnails -sonar.pullrequest.base=${env.DRONE_BRANCH} -sonar.pullrequest.branch=${env.DRONE_SOURCE_BRANCH} -sonar.pullrequest.key=${env.DRONE_PULL_REQUEST} +sonar.pullrequest.provider=${SONAR_PULL_REQUEST_PROVIDER} +sonar.pullrequest.github.repository=${SONAR_PULL_REQUEST_REPO} +sonar.pullrequest.base=${SONAR_PULL_REQUEST_BASE} +sonar.pullrequest.branch=${SONAR_PULL_REQUEST_BRANCH} +sonar.pullrequest.key=${SONAR_PULL_REQUEST_KEY} # Properties specific to language plugins: From 7a0f3846bbd5e8123e66767f71aca8019c900139 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Fri, 10 Jul 2020 16:42:01 +0200 Subject: [PATCH 66/80] read from env, PR id Signed-off-by: Michael Barz --- .drone.star | 3 +-- sonar-project.properties | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.drone.star b/.drone.star index 24da7a04d..e59576e08 100644 --- a/.drone.star +++ b/.drone.star @@ -134,11 +134,10 @@ def testing(ctx): 'SONAR_TOKEN': { 'from_secret': 'sonar_token', }, - 'SONAR_PULL_REQUEST_PROVIDER': 'GitHub' if ctx.build.event == 'pull_request' else None, 'SONAR_PULL_REQUEST_REPO': 'owncloud/ocis-thumbnails' if ctx.build.event == 'pull_request' else None, 'SONAR_PULL_REQUEST_BASE': 'master' if ctx.build.event == 'pull_request' else None, 'SONAR_PULL_REQUEST_BRANCH': ctx.build.source if ctx.build.event == 'pull_request' else None, - 'SONAR_PULL_REQUEST_KEY': ctx.build.ref if ctx.build.event == 'pull_request' else None, + 'SONAR_PULL_REQUEST_KEY': ctx.build.ref.replace("refs/pull/", "").split("/")[0] if ctx.build.event == 'pull_request' else None, }, }, ], diff --git a/sonar-project.properties b/sonar-project.properties index 05a5d21ff..1f5706c35 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -23,11 +23,11 @@ sonar.links.issue=https://github.com/owncloud/ocis-thumbnails/issues sonar.sources=. # Pull Requests -sonar.pullrequest.provider=${SONAR_PULL_REQUEST_PROVIDER} -sonar.pullrequest.github.repository=${SONAR_PULL_REQUEST_REPO} -sonar.pullrequest.base=${SONAR_PULL_REQUEST_BASE} -sonar.pullrequest.branch=${SONAR_PULL_REQUEST_BRANCH} -sonar.pullrequest.key=${SONAR_PULL_REQUEST_KEY} +sonar.pullrequest.provider=GitHub +sonar.pullrequest.github.repository=owncloud/ocis-thumbnails +sonar.pullrequest.base=${env.SONAR_PULL_REQUEST_BASE} +sonar.pullrequest.branch=${env.SONAR_PULL_REQUEST_BRANCH} +sonar.pullrequest.key=${env.SONAR_PULL_REQUEST_KEY} # Properties specific to language plugins: From 6c397d9c17de35c7a66103c46018495ddcba01c9 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Fri, 10 Jul 2020 17:05:07 +0200 Subject: [PATCH 67/80] add exclude patterns for sonarcloud Signed-off-by: Michael Barz --- sonar-project.properties | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index 1f5706c35..d878209b1 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,7 +1,7 @@ sonar.projectKey=owncloud_ocis-thumbnails sonar.organization=owncloud-1 -sonar.projectName=ownCloud Infinite Scale: THUMBNAILS +sonar.projectName=ocis-thumbnails sonar.projectVersion=1.0 sonar.host.url=https://sonarcloud.io @@ -14,7 +14,6 @@ sonar.links.ci=https://cloud.drone.io/owncloud/ocis-thumbnails/ sonar.links.scm=https://github.com/owncloud/ocis-thumbnails sonar.links.issue=https://github.com/owncloud/ocis-thumbnails/issues - # ===================================================== # Properties that will be shared amongst all modules # ===================================================== @@ -25,10 +24,12 @@ sonar.sources=. # Pull Requests sonar.pullrequest.provider=GitHub sonar.pullrequest.github.repository=owncloud/ocis-thumbnails -sonar.pullrequest.base=${env.SONAR_PULL_REQUEST_BASE} -sonar.pullrequest.branch=${env.SONAR_PULL_REQUEST_BRANCH} -sonar.pullrequest.key=${env.SONAR_PULL_REQUEST_KEY} - +sonar.pullrequest.base=${env.DRONE_BRANCH} +sonar.pullrequest.branch=${env.DRONE_SOURCE_BRANCH} +sonar.pullrequest.key=${env.DRONE_PULL_REQUEST} # Properties specific to language plugins: -sonar.go.coverage.reportPaths=coverage.out \ No newline at end of file +sonar.go.coverage.reportPaths=coverage.out + +# Exclude protobuf files +sonar.exclusions=pkg/proto/**, changelog/**, docs/**, package.json, rollup.config.js, changelog.md \ No newline at end of file From 3a05d580ae07f7d71c5ce9031d144e20b2440191 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Fri, 10 Jul 2020 17:19:02 +0200 Subject: [PATCH 68/80] fix sonar Signed-off-by: Michael Barz --- .drone.star | 1 - sonar-project.properties | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.drone.star b/.drone.star index e59576e08..f39ff5fef 100644 --- a/.drone.star +++ b/.drone.star @@ -134,7 +134,6 @@ def testing(ctx): 'SONAR_TOKEN': { 'from_secret': 'sonar_token', }, - 'SONAR_PULL_REQUEST_REPO': 'owncloud/ocis-thumbnails' if ctx.build.event == 'pull_request' else None, 'SONAR_PULL_REQUEST_BASE': 'master' if ctx.build.event == 'pull_request' else None, 'SONAR_PULL_REQUEST_BRANCH': ctx.build.source if ctx.build.event == 'pull_request' else None, 'SONAR_PULL_REQUEST_KEY': ctx.build.ref.replace("refs/pull/", "").split("/")[0] if ctx.build.event == 'pull_request' else None, diff --git a/sonar-project.properties b/sonar-project.properties index d878209b1..e9cc5c0de 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -24,9 +24,9 @@ sonar.sources=. # Pull Requests sonar.pullrequest.provider=GitHub sonar.pullrequest.github.repository=owncloud/ocis-thumbnails -sonar.pullrequest.base=${env.DRONE_BRANCH} -sonar.pullrequest.branch=${env.DRONE_SOURCE_BRANCH} -sonar.pullrequest.key=${env.DRONE_PULL_REQUEST} +sonar.pullrequest.base=${env.SONAR_PULL_REQUEST_BASE} +sonar.pullrequest.branch=${env.SONAR_PULL_REQUEST_BRANCH} +sonar.pullrequest.key=${env.SONAR_PULL_REQUEST_KEY} # Properties specific to language plugins: sonar.go.coverage.reportPaths=coverage.out From 6685db3fd83bf1170e000b414b121bbf75411532 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 10 Jul 2020 17:34:25 +0200 Subject: [PATCH 69/80] use micros service error api Signed-off-by: David Christofas --- changelog/unreleased/use-micro-error-api.md | 5 +++++ pkg/service/v0/service.go | 15 +++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 changelog/unreleased/use-micro-error-api.md diff --git a/changelog/unreleased/use-micro-error-api.md b/changelog/unreleased/use-micro-error-api.md new file mode 100644 index 000000000..ecc65833a --- /dev/null +++ b/changelog/unreleased/use-micro-error-api.md @@ -0,0 +1,5 @@ +Change: use micro service error api + +The service now also returns a status code when an error occurs. + +https://github.com/owncloud/ocis-thumbnails/issues/31 diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 8f8d10d22..e432d4d3f 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -2,10 +2,10 @@ package svc import ( "context" - "fmt" "gopkg.in/square/go-jose.v2/jwt" + merrors "github.com/micro/go-micro/v2/errors" "github.com/owncloud/ocis-pkg/v2/log" v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0" "github.com/owncloud/ocis-thumbnails/pkg/thumbnail" @@ -23,6 +23,7 @@ func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { logger.Fatal().Err(err).Msg("resolutions not configured correctly") } svc := Thumbnail{ + serviceID: options.Config.Server.Namespace + "." + options.Config.Server.Name, manager: thumbnail.NewSimpleManager( options.ThumbnailStorage, logger, @@ -37,6 +38,7 @@ func NewService(opts ...Option) v0proto.ThumbnailServiceHandler { // Thumbnail implements the GRPC handler. type Thumbnail struct { + serviceID string manager thumbnail.Manager resolutions resolution.Resolutions source imgsource.Source @@ -47,17 +49,18 @@ type Thumbnail struct { func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error { encoder := thumbnail.EncoderForType(req.Filetype.String()) if encoder == nil { - return fmt.Errorf("can't be encoded. filetype %s not supported", req.Filetype.String()) + g.logger.Debug().Str("filetype", req.Filetype.String()).Msg("unsupported filetype") + return nil } r := g.resolutions.ClosestMatch(int(req.Width), int(req.Height)) auth := req.Authorization if auth == "" { - return fmt.Errorf("authorization is missing") + return merrors.BadRequest(g.serviceID, "authorization is missing") } username, err := usernameFromAuthorization(auth) if err != nil { - return err + return merrors.InternalServerError(g.serviceID, "could not get username: %v", err.Error()) } tr := thumbnail.Request{ @@ -78,10 +81,10 @@ func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rs sCtx := imgsource.WithAuthorization(ctx, auth) img, err := g.source.Get(sCtx, tr.ImagePath) if err != nil { - return err + return merrors.InternalServerError(g.serviceID, "could not get image from source: %v", err.Error()) } if img == nil { - return fmt.Errorf("could not retrieve image") + return merrors.InternalServerError(g.serviceID, "could not get image from source") } thumbnail, err = g.manager.Get(tr, img) if err != nil { From 1730e83876add31f8c83460b0500fbdb7e9b9ea8 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 10 Jul 2020 16:03:15 +0000 Subject: [PATCH 70/80] Automated changelog update [skip ci] --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cd245b94..fb0f43ea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The following sections list the changes in ocis-thumbnails unreleased. * Change - Add more default resolutions: [#23](https://github.com/owncloud/ocis-thumbnails/issues/23) * Change - Refactor code to remove code smells: [#21](https://github.com/owncloud/ocis-thumbnails/issues/21) +* Change - Use micro service error api: [#31](https://github.com/owncloud/ocis-thumbnails/issues/31) * Enhancement - Limit users to access own thumbnails: [#5](https://github.com/owncloud/ocis-thumbnails/issues/5) ## Details @@ -27,6 +28,13 @@ The following sections list the changes in ocis-thumbnails unreleased. https://github.com/owncloud/ocis-thumbnails/issues/21 +* Change - Use micro service error api: [#31](https://github.com/owncloud/ocis-thumbnails/issues/31) + + The service now also returns a status code when an error occurs. + + https://github.com/owncloud/ocis-thumbnails/issues/31 + + * Enhancement - Limit users to access own thumbnails: [#5](https://github.com/owncloud/ocis-thumbnails/issues/5) Users of the service can no longer request thumbnails of another users by guessing the etag. The From ef2d06c0b9b4329e28a74e572f42698a3f895ae4 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 10 Jul 2020 18:09:19 +0200 Subject: [PATCH 71/80] prepare relase 0.2.0 Signed-off-by: David Christofas --- changelog/{unreleased => 0.2.0_2020-07-10}/add-resolutions.md | 0 changelog/{unreleased => 0.2.0_2020-07-10}/fix-code-smells.md | 0 .../{unreleased => 0.2.0_2020-07-10}/group-thumbnails-by-users.md | 0 changelog/{unreleased => 0.2.0_2020-07-10}/use-micro-error-api.md | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename changelog/{unreleased => 0.2.0_2020-07-10}/add-resolutions.md (100%) rename changelog/{unreleased => 0.2.0_2020-07-10}/fix-code-smells.md (100%) rename changelog/{unreleased => 0.2.0_2020-07-10}/group-thumbnails-by-users.md (100%) rename changelog/{unreleased => 0.2.0_2020-07-10}/use-micro-error-api.md (100%) diff --git a/changelog/unreleased/add-resolutions.md b/changelog/0.2.0_2020-07-10/add-resolutions.md similarity index 100% rename from changelog/unreleased/add-resolutions.md rename to changelog/0.2.0_2020-07-10/add-resolutions.md diff --git a/changelog/unreleased/fix-code-smells.md b/changelog/0.2.0_2020-07-10/fix-code-smells.md similarity index 100% rename from changelog/unreleased/fix-code-smells.md rename to changelog/0.2.0_2020-07-10/fix-code-smells.md diff --git a/changelog/unreleased/group-thumbnails-by-users.md b/changelog/0.2.0_2020-07-10/group-thumbnails-by-users.md similarity index 100% rename from changelog/unreleased/group-thumbnails-by-users.md rename to changelog/0.2.0_2020-07-10/group-thumbnails-by-users.md diff --git a/changelog/unreleased/use-micro-error-api.md b/changelog/0.2.0_2020-07-10/use-micro-error-api.md similarity index 100% rename from changelog/unreleased/use-micro-error-api.md rename to changelog/0.2.0_2020-07-10/use-micro-error-api.md From 98b583f663ce6afcf49612a45e1e46d425f7dad8 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 10 Jul 2020 16:13:54 +0000 Subject: [PATCH 72/80] Automated changelog update [skip ci] --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb0f43ea0..c6bb65278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ -# Changelog for [unreleased] (UNRELEASED) +# Changelog for [0.2.0] (2020-07-10) -The following sections list the changes in ocis-thumbnails unreleased. +The following sections list the changes in ocis-thumbnails 0.2.0. -[unreleased]: https://github.com/owncloud/ocis-thumbnails/compare/v0.1.2...master +[0.2.0]: https://github.com/owncloud/ocis-thumbnails/compare/v0.1.2...v0.2.0 ## Summary From 8a0a5c3c7caaece4d5d00299a932aa34fe7afcbe Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Thu, 23 Jul 2020 11:25:01 +0200 Subject: [PATCH 73/80] Use alpine:latest instead of alpine:edge in docker builds --- changelog/unreleased/use-alpine-latest.md | 5 +++++ docker/Dockerfile.linux.amd64 | 2 +- docker/Dockerfile.linux.arm | 2 +- docker/Dockerfile.linux.arm64 | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 changelog/unreleased/use-alpine-latest.md diff --git a/changelog/unreleased/use-alpine-latest.md b/changelog/unreleased/use-alpine-latest.md new file mode 100644 index 000000000..c84155183 --- /dev/null +++ b/changelog/unreleased/use-alpine-latest.md @@ -0,0 +1,5 @@ +Bugfix: build docker images with alpine:latest instead of alpine:edge + +ARM builds were failing when built on alpine:edge, so we switched to alpine:latest instead. + +https://github.com/owncloud/ocis-thumbnails/pull/35 diff --git a/docker/Dockerfile.linux.amd64 b/docker/Dockerfile.linux.amd64 index ce868ead2..cdf70eb0d 100644 --- a/docker/Dockerfile.linux.amd64 +++ b/docker/Dockerfile.linux.amd64 @@ -1,4 +1,4 @@ -FROM amd64/alpine:edge +FROM amd64/alpine:latest RUN apk update && \ apk upgrade && \ diff --git a/docker/Dockerfile.linux.arm b/docker/Dockerfile.linux.arm index 4ef28cfa1..33a3aebe9 100644 --- a/docker/Dockerfile.linux.arm +++ b/docker/Dockerfile.linux.arm @@ -1,4 +1,4 @@ -FROM arm32v6/alpine:edge +FROM arm32v6/alpine:latest RUN apk update && \ apk upgrade && \ diff --git a/docker/Dockerfile.linux.arm64 b/docker/Dockerfile.linux.arm64 index 34dc691aa..5fb0c5b24 100644 --- a/docker/Dockerfile.linux.arm64 +++ b/docker/Dockerfile.linux.arm64 @@ -1,4 +1,4 @@ -FROM arm64v8/alpine:edge +FROM arm64v8/alpine:latest RUN apk update && \ apk upgrade && \ From 02610f3b878e5ec85ae80d6a0e1bc3fe937bd46f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Thu, 23 Jul 2020 09:38:18 +0000 Subject: [PATCH 74/80] Automated changelog update [skip ci] --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6bb65278..87a1d357d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# Changelog for [unreleased] (UNRELEASED) + +The following sections list the changes in ocis-thumbnails unreleased. + +[unreleased]: https://github.com/owncloud/ocis-thumbnails/compare/v0.2.0...master + +## Summary + +* Bugfix - Build docker images with alpine:latest instead of alpine:edge: [#35](https://github.com/owncloud/ocis-thumbnails/pull/35) + +## Details + +* Bugfix - Build docker images with alpine:latest instead of alpine:edge: [#35](https://github.com/owncloud/ocis-thumbnails/pull/35) + + ARM builds were failing when built on alpine:edge, so we switched to alpine:latest instead. + + https://github.com/owncloud/ocis-thumbnails/pull/35 + # Changelog for [0.2.0] (2020-07-10) The following sections list the changes in ocis-thumbnails 0.2.0. From 71f31b43545a4911cd07eb50ceea42b155bf6bca Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 29 Jul 2020 11:02:03 +0200 Subject: [PATCH 75/80] add metrics endpoint Signed-off-by: David Christofas --- changelog/unreleased/serve-metrics.md | 5 ++++ pkg/command/server.go | 35 ++++++++++++++++++++++ pkg/server/debug/option.go | 42 ++++++++++++++++++++++++++ pkg/server/debug/server.go | 43 +++++++++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 changelog/unreleased/serve-metrics.md create mode 100644 pkg/server/debug/option.go create mode 100644 pkg/server/debug/server.go diff --git a/changelog/unreleased/serve-metrics.md b/changelog/unreleased/serve-metrics.md new file mode 100644 index 000000000..72c003beb --- /dev/null +++ b/changelog/unreleased/serve-metrics.md @@ -0,0 +1,5 @@ +Enhancement: serve the metrics endpoint + +Added the metrics endpoint to be able to monitor this service like the other services + +https://github.com/owncloud/ocis-thumbnails/issues/37 diff --git a/pkg/command/server.go b/pkg/command/server.go index 833aaf5fe..44a89a79f 100644 --- a/pkg/command/server.go +++ b/pkg/command/server.go @@ -17,6 +17,7 @@ import ( "github.com/owncloud/ocis-thumbnails/pkg/config" "github.com/owncloud/ocis-thumbnails/pkg/flagset" "github.com/owncloud/ocis-thumbnails/pkg/metrics" + "github.com/owncloud/ocis-thumbnails/pkg/server/debug" "github.com/owncloud/ocis-thumbnails/pkg/server/grpc" "go.opencensus.io/stats/view" "go.opencensus.io/trace" @@ -145,6 +146,40 @@ func Server(cfg *config.Config) *cli.Command { cancel() }) + server, err := debug.Server( + debug.Logger(logger), + debug.Config(cfg), + ) + + if err != nil { + logger.Info(). + Err(err). + Str("transport", "debug"). + Msg("Failed to initialize server") + + return err + } + + gr.Add(func() error { + return server.ListenAndServe() + }, func(_ error) { + ctx, timeout := context.WithTimeout(ctx, 5*time.Second) + + defer timeout() + defer cancel() + + if err := server.Shutdown(ctx); err != nil { + logger.Info(). + Err(err). + Str("transport", "debug"). + Msg("Failed to shutdown server") + } else { + logger.Info(). + Str("transport", "debug"). + Msg("Shutting down server") + } + }) + { stop := make(chan os.Signal, 1) diff --git a/pkg/server/debug/option.go b/pkg/server/debug/option.go new file mode 100644 index 000000000..3349a7875 --- /dev/null +++ b/pkg/server/debug/option.go @@ -0,0 +1,42 @@ +package debug + +import ( + "github.com/owncloud/ocis-pkg/v2/log" + "github.com/owncloud/ocis-thumbnails/pkg/config" +) + +// Option defines a single option function. +type Option func(o *Options) + +// Options defines the available options for this package. +type Options struct { + Name string + Address string + Logger log.Logger + Config *config.Config +} + +// newOptions initializes the available default options. +func newOptions(opts ...Option) Options { + opt := Options{} + + for _, o := range opts { + o(&opt) + } + + return opt +} + +// Logger provides a function to set the logger option. +func Logger(val log.Logger) Option { + return func(o *Options) { + o.Logger = val + } +} + +// Config provides a function to set the config option. +func Config(val *config.Config) Option { + return func(o *Options) { + o.Config = val + } +} diff --git a/pkg/server/debug/server.go b/pkg/server/debug/server.go new file mode 100644 index 000000000..cb3603536 --- /dev/null +++ b/pkg/server/debug/server.go @@ -0,0 +1,43 @@ +package debug + +import ( + "io" + "net/http" + + "github.com/owncloud/ocis-pkg/v2/service/debug" + "github.com/owncloud/ocis-thumbnails/pkg/config" + "github.com/owncloud/ocis-thumbnails/pkg/version" +) + +// Server initializes the debug service and server. +func Server(opts ...Option) (*http.Server, error) { + options := newOptions(opts...) + + return debug.NewService( + debug.Logger(options.Logger), + debug.Name(options.Config.Server.Name), + debug.Version(version.String), + debug.Address(options.Config.Debug.Addr), + debug.Token(options.Config.Debug.Token), + debug.Pprof(options.Config.Debug.Pprof), + debug.Zpages(options.Config.Debug.Zpages), + debug.Health(health(options.Config)), + debug.Ready(ready(options.Config)), + ), nil +} + +func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + io.WriteString(w, http.StatusText(http.StatusOK)) + } +} + +func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + io.WriteString(w, http.StatusText(http.StatusOK)) + } +} From 523fad23ed85ef5f3f7dde100e089ff174eae162 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 29 Jul 2020 09:20:30 +0000 Subject: [PATCH 76/80] Automated changelog update [skip ci] --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87a1d357d..361f28ea5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The following sections list the changes in ocis-thumbnails unreleased. ## Summary * Bugfix - Build docker images with alpine:latest instead of alpine:edge: [#35](https://github.com/owncloud/ocis-thumbnails/pull/35) +* Enhancement - Serve the metrics endpoint: [#37](https://github.com/owncloud/ocis-thumbnails/issues/37) ## Details @@ -16,6 +17,13 @@ The following sections list the changes in ocis-thumbnails unreleased. https://github.com/owncloud/ocis-thumbnails/pull/35 + +* Enhancement - Serve the metrics endpoint: [#37](https://github.com/owncloud/ocis-thumbnails/issues/37) + + Added the metrics endpoint to be able to monitor this service like the other services + + https://github.com/owncloud/ocis-thumbnails/issues/37 + # Changelog for [0.2.0] (2020-07-10) The following sections list the changes in ocis-thumbnails 0.2.0. From 840d84d22c23cbdf4d882e6d6e917183fb84bbea Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 6 Aug 2020 12:05:46 +0200 Subject: [PATCH 77/80] Use the ocis-proxy URL to make sure auth works correctly Since a recent change in ocis-accounts that makes a distinction between user id and user name, the thumbnails cannot properly authenticate against the Reva API endpoint (port 9140) using the provided Bearer token. Since the ocis-proxy is handling authentication correctly, the default setting has been changed here to connect to ocis-proxy. --- docs/getting-started.md | 4 ++-- pkg/flagset/flagset.go | 2 +- pkg/thumbnail/imgsource/webdav.go | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index fc8988afc..0efc461b4 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -85,7 +85,7 @@ THUMBNAILS_FILESYSTEMSTORAGE_ROOT : Root path of the filesystem storage directory, defaults to `/ocis-thumbnails/` THUMBNAILS_WEBDAVSOURCE_BASEURL -: Base url for a webdav api, defaults to `htp://localhost:9140/remote.php/webdav/` +: Base url for a webdav api, defaults to `https://localhost:9200/remote.php/webdav/` THUMBNAILS_RESOLUTIONS : List of resolutions supported by the service, defaults to `["16x16", "32x32", "64x64", "128x128"] @@ -155,7 +155,7 @@ If you prefer to configure the service with commandline flags you can see the av : Root path of the filesystem storage directory, defaults to `/ocis-thumbnails/` --webdavsource-baseurl -: Base url for a webdav api, defaults to `htp://localhost:9140/remote.php/webdav/` +: Base url for a webdav api, defaults to `https://localhost:9200/remote.php/webdav/` --thumbnail-resolution : List of resolutions supported by the service, defaults to `["16x16", "32x32", "64x64", "128x128"] diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index d5566b45c..706b30d22 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -148,7 +148,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { }, &cli.StringFlag{ Name: "webdavsource-baseurl", - Value: "http://localhost:9140/remote.php/webdav/", + Value: "https://localhost:9200/remote.php/webdav/", Usage: "Base url for a webdav api", EnvVars: []string{"THUMBNAILS_WEBDAVSOURCE_BASEURL"}, Destination: &cfg.Thumbnail.WebDavSource.BaseURL, diff --git a/pkg/thumbnail/imgsource/webdav.go b/pkg/thumbnail/imgsource/webdav.go index 65ccca89a..e5c9da618 100644 --- a/pkg/thumbnail/imgsource/webdav.go +++ b/pkg/thumbnail/imgsource/webdav.go @@ -2,6 +2,7 @@ package imgsource import ( "context" + "crypto/tls" "fmt" "image" "net/http" @@ -27,12 +28,17 @@ type WebDav struct { func (s WebDav) Get(ctx context.Context, file string) (image.Image, error) { u, _ := url.Parse(s.baseURL) u.Path = path.Join(u.Path, file) + fmt.Printf("url: %s", u.String()) req, err := http.NewRequest(http.MethodGet, u.String(), nil) if err != nil { return nil, fmt.Errorf("could not get the image \"%s\" error: %s", file, err.Error()) } + // FIXME: make this configurable!! + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + auth := authorization(ctx) + fmt.Printf("auth: %s", auth) if auth == "" { return nil, fmt.Errorf("could not get image \"%s\" error: authorization is missing", file) } From 52163c9e67c4abafa25768c4888af8c00817fe8f Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 6 Aug 2020 14:05:35 +0200 Subject: [PATCH 78/80] Add Insecure flag for trusting https certs --- pkg/config/config.go | 3 ++- pkg/flagset/flagset.go | 7 +++++++ pkg/thumbnail/imgsource/webdav.go | 11 +++++------ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 7d1b1645b..75dd1dbf5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -48,7 +48,8 @@ type FileSystemStorage struct { // WebDavSource defines the available webdav source configuration. type WebDavSource struct { - BaseURL string + BaseURL string + Insecure bool } // FileSystemSource defines the available filesystem source configuration. diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 706b30d22..b8027badc 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -153,6 +153,13 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { EnvVars: []string{"THUMBNAILS_WEBDAVSOURCE_BASEURL"}, Destination: &cfg.Thumbnail.WebDavSource.BaseURL, }, + &cli.BoolFlag{ + Name: "webdavsource-insecure", + Value: true, + Usage: "Whether to skip certificate checks", + EnvVars: []string{"THUMBNAILS_WEBDAVSOURCE_INSECURE"}, + Destination: &cfg.Thumbnail.WebDavSource.Insecure, + }, &cli.StringSliceFlag{ Name: "thumbnail-resolution", Value: cli.NewStringSlice("16x16", "32x32", "64x64", "128x128", "1920x1080", "3840x2160", "7680x4320"), diff --git a/pkg/thumbnail/imgsource/webdav.go b/pkg/thumbnail/imgsource/webdav.go index e5c9da618..3d173793b 100644 --- a/pkg/thumbnail/imgsource/webdav.go +++ b/pkg/thumbnail/imgsource/webdav.go @@ -15,30 +15,29 @@ import ( // NewWebDavSource creates a new webdav instance. func NewWebDavSource(cfg config.WebDavSource) WebDav { return WebDav{ - baseURL: cfg.BaseURL, + baseURL: cfg.BaseURL, + insecure: cfg.Insecure, } } // WebDav implements the Source interface for webdav services type WebDav struct { - baseURL string + baseURL string + insecure bool } // Get downloads the file from a webdav service func (s WebDav) Get(ctx context.Context, file string) (image.Image, error) { u, _ := url.Parse(s.baseURL) u.Path = path.Join(u.Path, file) - fmt.Printf("url: %s", u.String()) req, err := http.NewRequest(http.MethodGet, u.String(), nil) if err != nil { return nil, fmt.Errorf("could not get the image \"%s\" error: %s", file, err.Error()) } - // FIXME: make this configurable!! - http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: s.insecure} auth := authorization(ctx) - fmt.Printf("auth: %s", auth) if auth == "" { return nil, fmt.Errorf("could not get image \"%s\" error: authorization is missing", file) } From 96b0f3eb1e963b19dd691e7694bf8f98744e451f Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 17 Aug 2020 15:26:44 +0200 Subject: [PATCH 79/80] Prepare release 0.3.0 --- changelog/{unreleased => 0.3.0_2020-08-17}/serve-metrics.md | 0 changelog/{unreleased => 0.3.0_2020-08-17}/use-alpine-latest.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename changelog/{unreleased => 0.3.0_2020-08-17}/serve-metrics.md (100%) rename changelog/{unreleased => 0.3.0_2020-08-17}/use-alpine-latest.md (100%) diff --git a/changelog/unreleased/serve-metrics.md b/changelog/0.3.0_2020-08-17/serve-metrics.md similarity index 100% rename from changelog/unreleased/serve-metrics.md rename to changelog/0.3.0_2020-08-17/serve-metrics.md diff --git a/changelog/unreleased/use-alpine-latest.md b/changelog/0.3.0_2020-08-17/use-alpine-latest.md similarity index 100% rename from changelog/unreleased/use-alpine-latest.md rename to changelog/0.3.0_2020-08-17/use-alpine-latest.md From 2ebf7b2f23af96b3b499c401fe5498a9349403d8 Mon Sep 17 00:00:00 2001 From: Alex Unger Date: Mon, 17 Aug 2020 13:44:45 +0000 Subject: [PATCH 80/80] Automated changelog update [skip ci] --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 361f28ea5..08769b343 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ -# Changelog for [unreleased] (UNRELEASED) +# Changelog for [0.3.0] (2020-08-17) -The following sections list the changes in ocis-thumbnails unreleased. +The following sections list the changes in ocis-thumbnails 0.3.0. -[unreleased]: https://github.com/owncloud/ocis-thumbnails/compare/v0.2.0...master +[0.3.0]: https://github.com/owncloud/ocis-thumbnails/compare/v0.2.0...v0.3.0 ## Summary