From e19ba5648cb8b4b6a261e7f3905f83d7aa418ad0 Mon Sep 17 00:00:00 2001 From: Tan Jiang Date: Fri, 19 Feb 2016 13:01:58 +0800 Subject: [PATCH] switch to /vendor, remove docker/distribution docker/libtrust go-sql-driver/mysql, and update Dockerfile to use go get to download these packages. --- Dockerfile | 7 +- Godeps/Godeps.json | 71 - Godeps/Readme | 5 - Godeps/_workspace/.gitignore | 2 - .../docker/distribution/context/context.go | 85 - .../docker/distribution/context/doc.go | 89 - .../docker/distribution/context/http.go | 364 ---- .../docker/distribution/context/http_test.go | 285 --- .../docker/distribution/context/logger.go | 116 -- .../docker/distribution/context/trace.go | 104 - .../docker/distribution/context/trace_test.go | 85 - .../docker/distribution/context/util.go | 32 - .../docker/distribution/context/version.go | 16 - .../distribution/context/version_test.go | 19 - .../docker/distribution/registry/auth/auth.go | 144 -- .../registry/auth/htpasswd/access.go | 102 - .../registry/auth/htpasswd/access_test.go | 122 -- .../registry/auth/htpasswd/htpasswd.go | 80 - .../registry/auth/htpasswd/htpasswd_test.go | 85 - .../registry/auth/silly/access.go | 97 - .../registry/auth/silly/access_test.go | 71 - .../registry/auth/token/accesscontroller.go | 268 --- .../registry/auth/token/stringset.go | 35 - .../distribution/registry/auth/token/token.go | 343 ---- .../registry/auth/token/token_test.go | 386 ---- .../distribution/registry/auth/token/util.go | 58 - .../docker/distribution/uuid/uuid.go | 126 -- .../docker/distribution/uuid/uuid_test.go | 48 - .../docker/libtrust/CONTRIBUTING.md | 13 - .../src/github.com/docker/libtrust/LICENSE | 191 -- .../github.com/docker/libtrust/MAINTAINERS | 3 - .../src/github.com/docker/libtrust/README.md | 18 - .../docker/libtrust/certificates.go | 175 -- .../docker/libtrust/certificates_test.go | 111 -- .../src/github.com/docker/libtrust/doc.go | 9 - .../src/github.com/docker/libtrust/ec_key.go | 428 ----- .../github.com/docker/libtrust/ec_key_test.go | 157 -- .../src/github.com/docker/libtrust/filter.go | 50 - .../github.com/docker/libtrust/filter_test.go | 81 - .../src/github.com/docker/libtrust/hash.go | 56 - .../github.com/docker/libtrust/jsonsign.go | 657 ------- .../docker/libtrust/jsonsign_test.go | 380 ---- .../src/github.com/docker/libtrust/key.go | 253 --- .../github.com/docker/libtrust/key_files.go | 255 --- .../docker/libtrust/key_files_test.go | 220 --- .../github.com/docker/libtrust/key_manager.go | 175 -- .../github.com/docker/libtrust/key_test.go | 80 - .../src/github.com/docker/libtrust/rsa_key.go | 427 ----- .../docker/libtrust/rsa_key_test.go | 157 -- .../docker/libtrust/testutil/certificates.go | 94 - .../docker/libtrust/tlsdemo/README.md | 50 - .../docker/libtrust/tlsdemo/client.go | 89 - .../docker/libtrust/tlsdemo/gencert.go | 62 - .../docker/libtrust/tlsdemo/genkeys.go | 61 - .../docker/libtrust/tlsdemo/server.go | 80 - .../docker/libtrust/trustgraph/graph.go | 50 - .../libtrust/trustgraph/memory_graph.go | 133 -- .../libtrust/trustgraph/memory_graph_test.go | 174 -- .../docker/libtrust/trustgraph/statement.go | 227 --- .../libtrust/trustgraph/statement_test.go | 417 ---- .../src/github.com/docker/libtrust/util.go | 363 ---- .../github.com/docker/libtrust/util_test.go | 45 - .../github.com/go-sql-driver/mysql/.gitignore | 8 - .../go-sql-driver/mysql/.travis.yml | 10 - .../github.com/go-sql-driver/mysql/AUTHORS | 44 - .../go-sql-driver/mysql/CHANGELOG.md | 92 - .../go-sql-driver/mysql/CONTRIBUTING.md | 40 - .../github.com/go-sql-driver/mysql/LICENSE | 373 ---- .../github.com/go-sql-driver/mysql/README.md | 386 ---- .../go-sql-driver/mysql/appengine.go | 19 - .../go-sql-driver/mysql/benchmark_test.go | 246 --- .../github.com/go-sql-driver/mysql/buffer.go | 136 -- .../go-sql-driver/mysql/collations.go | 250 --- .../go-sql-driver/mysql/connection.go | 403 ---- .../github.com/go-sql-driver/mysql/const.go | 162 -- .../github.com/go-sql-driver/mysql/driver.go | 149 -- .../go-sql-driver/mysql/driver_test.go | 1681 ----------------- .../github.com/go-sql-driver/mysql/errors.go | 131 -- .../go-sql-driver/mysql/errors_test.go | 42 - .../github.com/go-sql-driver/mysql/infile.go | 164 -- .../github.com/go-sql-driver/mysql/packets.go | 1179 ------------ .../github.com/go-sql-driver/mysql/result.go | 22 - .../github.com/go-sql-driver/mysql/rows.go | 106 -- .../go-sql-driver/mysql/statement.go | 150 -- .../go-sql-driver/mysql/transaction.go | 31 - .../github.com/go-sql-driver/mysql/utils.go | 973 ---------- .../go-sql-driver/mysql/utils_test.go | 346 ---- .../github.com/Sirupsen/logrus/.gitignore | 0 .../github.com/Sirupsen/logrus/.travis.yml | 0 .../github.com/Sirupsen/logrus/CHANGELOG.md | 0 .../github.com/Sirupsen/logrus/LICENSE | 0 .../github.com/Sirupsen/logrus/README.md | 0 .../github.com/Sirupsen/logrus/doc.go | 0 .../github.com/Sirupsen/logrus/entry.go | 0 .../github.com/Sirupsen/logrus/entry_test.go | 0 .../Sirupsen/logrus/examples/basic/basic.go | 0 .../Sirupsen/logrus/examples/hook/hook.go | 0 .../github.com/Sirupsen/logrus/exported.go | 0 .../github.com/Sirupsen/logrus/formatter.go | 0 .../Sirupsen/logrus/formatter_bench_test.go | 0 .../logrus/formatters/logstash/logstash.go | 0 .../formatters/logstash/logstash_test.go | 0 .../github.com/Sirupsen/logrus/hook_test.go | 0 .../github.com/Sirupsen/logrus/hooks.go | 0 .../Sirupsen/logrus/hooks/bugsnag/bugsnag.go | 0 .../logrus/hooks/bugsnag/bugsnag_test.go | 0 .../Sirupsen/logrus/hooks/syslog/README.md | 0 .../Sirupsen/logrus/hooks/syslog/syslog.go | 0 .../logrus/hooks/syslog/syslog_test.go | 0 .../Sirupsen/logrus/json_formatter.go | 0 .../Sirupsen/logrus/json_formatter_test.go | 0 .../github.com/Sirupsen/logrus/logger.go | 0 .../github.com/Sirupsen/logrus/logrus.go | 0 .../github.com/Sirupsen/logrus/logrus_test.go | 0 .../Sirupsen/logrus/terminal_bsd.go | 0 .../Sirupsen/logrus/terminal_linux.go | 0 .../Sirupsen/logrus/terminal_notwindows.go | 0 .../Sirupsen/logrus/terminal_windows.go | 0 .../Sirupsen/logrus/text_formatter.go | 0 .../Sirupsen/logrus/text_formatter_test.go | 0 .../github.com/Sirupsen/logrus/writer.go | 0 .../github.com/Unknwon/goconfig/.gitignore | 0 .../github.com/Unknwon/goconfig/LICENSE | 0 .../github.com/Unknwon/goconfig/README.md | 0 .../github.com/Unknwon/goconfig/README_ZH.md | 0 .../github.com/Unknwon/goconfig/conf.go | 0 .../Unknwon/goconfig/goconfig_test.go | 0 .../github.com/Unknwon/goconfig/read.go | 0 .../Unknwon/goconfig/testdata/conf.ini | 0 .../Unknwon/goconfig/testdata/conf2.ini | 0 .../Unknwon/goconfig/testdata/conf_test.ini | 0 .../github.com/Unknwon/goconfig/write.go | 0 .../github.com/astaxie/beego/.gitignore | 0 .../github.com/astaxie/beego/.travis.yml | 0 .../github.com/astaxie/beego/CONTRIBUTING.md | 0 .../github.com/astaxie/beego/LICENSE | 0 .../github.com/astaxie/beego/README.md | 0 .../github.com/astaxie/beego/admin.go | 0 .../github.com/astaxie/beego/adminui.go | 0 .../github.com/astaxie/beego/app.go | 0 .../github.com/astaxie/beego/beego.go | 0 .../github.com/astaxie/beego/cache/README.md | 0 .../github.com/astaxie/beego/cache/cache.go | 0 .../github.com/astaxie/beego/cache/conv.go | 0 .../github.com/astaxie/beego/cache/file.go | 0 .../astaxie/beego/cache/memcache/memcache.go | 0 .../github.com/astaxie/beego/cache/memory.go | 0 .../astaxie/beego/cache/redis/redis.go | 0 .../github.com/astaxie/beego/config.go | 0 .../github.com/astaxie/beego/config/config.go | 0 .../github.com/astaxie/beego/config/fake.go | 0 .../github.com/astaxie/beego/config/ini.go | 0 .../github.com/astaxie/beego/config/json.go | 0 .../astaxie/beego/config/xml/xml.go | 0 .../astaxie/beego/config/yaml/yaml.go | 0 .../astaxie/beego/context/acceptencoder.go | 0 .../astaxie/beego/context/context.go | 0 .../github.com/astaxie/beego/context/input.go | 0 .../astaxie/beego/context/output.go | 0 .../github.com/astaxie/beego/controller.go | 0 .../github.com/astaxie/beego/doc.go | 0 .../github.com/astaxie/beego/docs.go | 0 .../github.com/astaxie/beego/error.go | 0 .../github.com/astaxie/beego/filter.go | 0 .../github.com/astaxie/beego/flash.go | 0 .../github.com/astaxie/beego/grace/conn.go | 0 .../github.com/astaxie/beego/grace/grace.go | 0 .../astaxie/beego/grace/listener.go | 0 .../github.com/astaxie/beego/grace/server.go | 0 .../github.com/astaxie/beego/hooks.go | 0 .../astaxie/beego/httplib/README.md | 0 .../astaxie/beego/httplib/httplib.go | 0 .../github.com/astaxie/beego/log.go | 0 .../github.com/astaxie/beego/logs/README.md | 0 .../github.com/astaxie/beego/logs/conn.go | 0 .../github.com/astaxie/beego/logs/console.go | 0 .../github.com/astaxie/beego/logs/es/es.go | 0 .../github.com/astaxie/beego/logs/file.go | 0 .../github.com/astaxie/beego/logs/log.go | 0 .../github.com/astaxie/beego/logs/smtp.go | 0 .../github.com/astaxie/beego/migration/ddl.go | 0 .../astaxie/beego/migration/migration.go | 0 .../github.com/astaxie/beego/mime.go | 0 .../github.com/astaxie/beego/namespace.go | 0 .../github.com/astaxie/beego/orm/README.md | 0 .../github.com/astaxie/beego/orm/cmd.go | 0 .../github.com/astaxie/beego/orm/cmd_utils.go | 0 .../github.com/astaxie/beego/orm/db.go | 0 .../github.com/astaxie/beego/orm/db_alias.go | 0 .../github.com/astaxie/beego/orm/db_mysql.go | 0 .../github.com/astaxie/beego/orm/db_oracle.go | 0 .../astaxie/beego/orm/db_postgres.go | 0 .../github.com/astaxie/beego/orm/db_sqlite.go | 0 .../github.com/astaxie/beego/orm/db_tables.go | 0 .../github.com/astaxie/beego/orm/db_tidb.go | 0 .../github.com/astaxie/beego/orm/db_utils.go | 0 .../github.com/astaxie/beego/orm/models.go | 0 .../astaxie/beego/orm/models_boot.go | 0 .../astaxie/beego/orm/models_fields.go | 0 .../astaxie/beego/orm/models_info_f.go | 0 .../astaxie/beego/orm/models_info_m.go | 0 .../astaxie/beego/orm/models_utils.go | 0 .../github.com/astaxie/beego/orm/orm.go | 0 .../github.com/astaxie/beego/orm/orm_conds.go | 0 .../github.com/astaxie/beego/orm/orm_log.go | 0 .../astaxie/beego/orm/orm_object.go | 0 .../astaxie/beego/orm/orm_querym2m.go | 0 .../astaxie/beego/orm/orm_queryset.go | 0 .../github.com/astaxie/beego/orm/orm_raw.go | 0 .../github.com/astaxie/beego/orm/qb.go | 0 .../github.com/astaxie/beego/orm/qb_mysql.go | 0 .../github.com/astaxie/beego/orm/qb_tidb.go | 0 .../github.com/astaxie/beego/orm/types.go | 0 .../github.com/astaxie/beego/orm/utils.go | 0 .../github.com/astaxie/beego/parser.go | 0 .../astaxie/beego/plugins/apiauth/apiauth.go | 0 .../astaxie/beego/plugins/auth/basic.go | 0 .../astaxie/beego/plugins/cors/cors.go | 0 .../github.com/astaxie/beego/router.go | 0 .../astaxie/beego/session/README.md | 0 .../beego/session/couchbase/sess_couchbase.go | 0 .../beego/session/ledis/ledis_session.go | 0 .../beego/session/memcache/sess_memcache.go | 0 .../astaxie/beego/session/mysql/sess_mysql.go | 0 .../beego/session/postgres/sess_postgresql.go | 0 .../astaxie/beego/session/redis/sess_redis.go | 0 .../astaxie/beego/session/sess_cookie.go | 0 .../astaxie/beego/session/sess_file.go | 0 .../astaxie/beego/session/sess_mem.go | 0 .../astaxie/beego/session/sess_utils.go | 0 .../astaxie/beego/session/session.go | 0 .../github.com/astaxie/beego/staticfile.go | 0 .../astaxie/beego/swagger/docs_spec.go | 0 .../github.com/astaxie/beego/template.go | 0 .../github.com/astaxie/beego/templatefunc.go | 0 .../astaxie/beego/testing/assertions.go | 0 .../astaxie/beego/testing/client.go | 0 .../astaxie/beego/toolbox/healthcheck.go | 0 .../astaxie/beego/toolbox/profile.go | 0 .../astaxie/beego/toolbox/statistics.go | 0 .../github.com/astaxie/beego/toolbox/task.go | 0 .../github.com/astaxie/beego/tree.go | 0 .../github.com/astaxie/beego/utils/caller.go | 0 .../astaxie/beego/utils/captcha/LICENSE | 0 .../astaxie/beego/utils/captcha/README.md | 0 .../astaxie/beego/utils/captcha/captcha.go | 0 .../astaxie/beego/utils/captcha/image.go | 0 .../astaxie/beego/utils/captcha/siprng.go | 0 .../github.com/astaxie/beego/utils/debug.go | 0 .../github.com/astaxie/beego/utils/file.go | 0 .../github.com/astaxie/beego/utils/mail.go | 0 .../beego/utils/pagination/controller.go | 0 .../astaxie/beego/utils/pagination/doc.go | 0 .../beego/utils/pagination/paginator.go | 0 .../astaxie/beego/utils/pagination/utils.go | 0 .../github.com/astaxie/beego/utils/rand.go | 0 .../github.com/astaxie/beego/utils/safemap.go | 0 .../github.com/astaxie/beego/utils/slice.go | 0 .../astaxie/beego/validation/README.md | 0 .../astaxie/beego/validation/util.go | 0 .../astaxie/beego/validation/validation.go | 0 .../astaxie/beego/validation/validators.go | 0 .../github.com/beego/i18n/.gitignore | 0 .../github.com/beego/i18n/LICENSE | 0 .../github.com/beego/i18n/README.md | 0 .../github.com/beego/i18n/beei18n/beei18n.go | 0 .../github.com/beego/i18n/beei18n/sync.go | 0 .../github.com/beego/i18n/i18n.go | 0 .../github.com/gorilla/context/.travis.yml | 0 .../github.com/gorilla/context/LICENSE | 0 .../github.com/gorilla/context/README.md | 0 .../github.com/gorilla/context/context.go | 0 .../gorilla/context/context_test.go | 0 .../github.com/gorilla/context/doc.go | 0 .../github.com/gorilla/mux/.travis.yml | 0 .../github.com/gorilla/mux/LICENSE | 0 .../github.com/gorilla/mux/README.md | 0 .../github.com/gorilla/mux/bench_test.go | 0 .../github.com/gorilla/mux/doc.go | 0 .../github.com/gorilla/mux/mux.go | 0 .../github.com/gorilla/mux/mux_test.go | 0 .../github.com/gorilla/mux/old_test.go | 0 .../github.com/gorilla/mux/regexp.go | 0 .../github.com/gorilla/mux/route.go | 0 .../github.com/mqu/openldap/LICENCE.txt | 0 .../github.com/mqu/openldap/README.md | 0 .../mqu/openldap/add-modify-delete.go | 0 .../github.com/mqu/openldap/defines.go | 0 .../github.com/mqu/openldap/openldap.go | 0 .../github.com/mqu/openldap/options-errors.go | 0 .../github.com/mqu/openldap/results.go | 0 .../github.com/mqu/openldap/types.go | 0 .../src => vendor}/golang.org/x/blog/LICENSE | 0 .../src => vendor}/golang.org/x/blog/PATENTS | 0 ...ird-party-libraries-goprotobuf-and.article | 0 .../golang.org/x/crypto/LICENSE | 0 .../golang.org/x/crypto/PATENTS | 0 .../golang.org/x/crypto/pbkdf2/pbkdf2.go | 0 .../src => vendor}/golang.org/x/exp/LICENSE | 0 .../src => vendor}/golang.org/x/exp/PATENTS | 0 .../src => vendor}/golang.org/x/image/LICENSE | 0 .../src => vendor}/golang.org/x/image/PATENTS | 0 .../golang.org/x/mobile/LICENSE | 0 .../golang.org/x/mobile/PATENTS | 0 .../src => vendor}/golang.org/x/net/LICENSE | 0 .../src => vendor}/golang.org/x/net/PATENTS | 0 .../golang.org/x/net/context/context.go | 0 .../src => vendor}/golang.org/x/sys/LICENSE | 0 .../src => vendor}/golang.org/x/sys/PATENTS | 0 .../src => vendor}/golang.org/x/talks/LICENSE | 0 .../src => vendor}/golang.org/x/talks/PATENTS | 0 .../src => vendor}/golang.org/x/text/LICENSE | 0 .../src => vendor}/golang.org/x/text/PATENTS | 0 .../src => vendor}/golang.org/x/tools/LICENSE | 0 .../src => vendor}/golang.org/x/tools/PATENTS | 0 315 files changed, 6 insertions(+), 16123 deletions(-) delete mode 100644 Godeps/Godeps.json delete mode 100644 Godeps/Readme delete mode 100644 Godeps/_workspace/.gitignore delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/context/context.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/context/doc.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/context/http.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/context/http_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/context/logger.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/context/trace.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/context/trace_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/context/util.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/context/version.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/context/version_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/auth.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/access.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/access_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/htpasswd.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/htpasswd_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/silly/access.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/silly/access_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/accesscontroller.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/stringset.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/token.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/token_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/util.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/uuid/uuid.go delete mode 100644 Godeps/_workspace/src/github.com/docker/distribution/uuid/uuid_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/CONTRIBUTING.md delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/MAINTAINERS delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/README.md delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/certificates.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/certificates_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/doc.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/ec_key.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/ec_key_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/filter.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/filter_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/hash.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/jsonsign.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/jsonsign_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/key.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/key_files.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/key_files_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/key_manager.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/key_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/rsa_key.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/rsa_key_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/testutil/certificates.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/README.md delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/client.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/gencert.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/genkeys.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/server.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/graph.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/memory_graph.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/memory_graph_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/statement.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/statement_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/util.go delete mode 100644 Godeps/_workspace/src/github.com/docker/libtrust/util_test.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/AUTHORS delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/CHANGELOG.md delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/CONTRIBUTING.md delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/README.md delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/appengine.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/benchmark_test.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/collations.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors_test.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go delete mode 100644 Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/.gitignore (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/.travis.yml (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/CHANGELOG.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/doc.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/entry.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/entry_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/examples/basic/basic.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/examples/hook/hook.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/exported.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/formatter.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/formatter_bench_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/formatters/logstash/logstash.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/hook_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/hooks.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/hooks/syslog/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/hooks/syslog/syslog.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/json_formatter.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/json_formatter_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/logger.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/logrus.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/logrus_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/terminal_bsd.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/terminal_linux.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/terminal_notwindows.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/terminal_windows.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/text_formatter.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/text_formatter_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Sirupsen/logrus/writer.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Unknwon/goconfig/.gitignore (100%) rename {Godeps/_workspace/src => vendor}/github.com/Unknwon/goconfig/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/github.com/Unknwon/goconfig/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/Unknwon/goconfig/README_ZH.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/Unknwon/goconfig/conf.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Unknwon/goconfig/goconfig_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Unknwon/goconfig/read.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/Unknwon/goconfig/testdata/conf.ini (100%) rename {Godeps/_workspace/src => vendor}/github.com/Unknwon/goconfig/testdata/conf2.ini (100%) rename {Godeps/_workspace/src => vendor}/github.com/Unknwon/goconfig/testdata/conf_test.ini (100%) rename {Godeps/_workspace/src => vendor}/github.com/Unknwon/goconfig/write.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/.gitignore (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/.travis.yml (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/CONTRIBUTING.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/admin.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/adminui.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/app.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/beego.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/cache/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/cache/cache.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/cache/conv.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/cache/file.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/cache/memcache/memcache.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/cache/memory.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/cache/redis/redis.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/config.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/config/config.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/config/fake.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/config/ini.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/config/json.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/config/xml/xml.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/config/yaml/yaml.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/context/acceptencoder.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/context/context.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/context/input.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/context/output.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/controller.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/doc.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/docs.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/error.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/filter.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/flash.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/grace/conn.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/grace/grace.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/grace/listener.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/grace/server.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/hooks.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/httplib/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/httplib/httplib.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/log.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/logs/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/logs/conn.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/logs/console.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/logs/es/es.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/logs/file.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/logs/log.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/logs/smtp.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/migration/ddl.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/migration/migration.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/mime.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/namespace.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/cmd.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/cmd_utils.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/db.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/db_alias.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/db_mysql.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/db_oracle.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/db_postgres.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/db_sqlite.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/db_tables.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/db_tidb.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/db_utils.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/models.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/models_boot.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/models_fields.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/models_info_f.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/models_info_m.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/models_utils.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/orm.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/orm_conds.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/orm_log.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/orm_object.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/orm_querym2m.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/orm_queryset.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/orm_raw.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/qb.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/qb_mysql.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/qb_tidb.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/types.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/orm/utils.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/parser.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/plugins/apiauth/apiauth.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/plugins/auth/basic.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/plugins/cors/cors.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/router.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/couchbase/sess_couchbase.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/ledis/ledis_session.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/memcache/sess_memcache.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/mysql/sess_mysql.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/postgres/sess_postgresql.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/redis/sess_redis.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/sess_cookie.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/sess_file.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/sess_mem.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/sess_utils.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/session/session.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/staticfile.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/swagger/docs_spec.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/template.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/templatefunc.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/testing/assertions.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/testing/client.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/toolbox/healthcheck.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/toolbox/profile.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/toolbox/statistics.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/toolbox/task.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/tree.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/caller.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/captcha/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/captcha/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/captcha/captcha.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/captcha/image.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/captcha/siprng.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/debug.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/file.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/mail.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/pagination/controller.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/pagination/doc.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/pagination/paginator.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/pagination/utils.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/rand.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/safemap.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/utils/slice.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/validation/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/validation/util.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/validation/validation.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/astaxie/beego/validation/validators.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/beego/i18n/.gitignore (100%) rename {Godeps/_workspace/src => vendor}/github.com/beego/i18n/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/github.com/beego/i18n/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/beego/i18n/beei18n/beei18n.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/beego/i18n/beei18n/sync.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/beego/i18n/i18n.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/context/.travis.yml (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/context/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/context/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/context/context.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/context/context_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/context/doc.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/mux/.travis.yml (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/mux/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/mux/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/mux/bench_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/mux/doc.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/mux/mux.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/mux/mux_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/mux/old_test.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/mux/regexp.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/gorilla/mux/route.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/mqu/openldap/LICENCE.txt (100%) rename {Godeps/_workspace/src => vendor}/github.com/mqu/openldap/README.md (100%) rename {Godeps/_workspace/src => vendor}/github.com/mqu/openldap/add-modify-delete.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/mqu/openldap/defines.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/mqu/openldap/openldap.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/mqu/openldap/options-errors.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/mqu/openldap/results.go (100%) rename {Godeps/_workspace/src => vendor}/github.com/mqu/openldap/types.go (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/blog/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/blog/PATENTS (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/blog/content/third-party-libraries-goprotobuf-and.article (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/crypto/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/crypto/PATENTS (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/crypto/pbkdf2/pbkdf2.go (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/exp/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/exp/PATENTS (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/image/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/image/PATENTS (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/mobile/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/mobile/PATENTS (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/net/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/net/PATENTS (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/net/context/context.go (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/sys/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/sys/PATENTS (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/talks/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/talks/PATENTS (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/text/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/text/PATENTS (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/tools/LICENSE (100%) rename {Godeps/_workspace/src => vendor}/golang.org/x/tools/PATENTS (100%) diff --git a/Dockerfile b/Dockerfile index b73d41b33..468612f62 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,9 +7,14 @@ RUN apt-get update \ && rm -r /var/lib/apt/lists/* COPY . /go/src/github.com/vmware/harbor +#golang.org is blocked in China +COPY ./vendor/golang.org /go/src/golang.org WORKDIR /go/src/github.com/vmware/harbor -ENV GOPATH /go/src/github.com/vmware/harbor/Godeps/_workspace:$GOPATH +ENV GO15VENDOREXPERIMENT 1 +RUN go get -d github.com/docker/distribution \ + && go get -d github.com/docker/libtrust \ + && go get -d github.com/go-sql-driver/mysql RUN go install -v -a ENV MYSQL_USR root \ diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json deleted file mode 100644 index 2e22c3372..000000000 --- a/Godeps/Godeps.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "ImportPath": "vmware/harbor", - "GoVersion": "go1.5.1", - "Deps": [ - { - "ImportPath": "github.com/Sirupsen/logrus", - "Comment": "v0.8.7-30-g4643a7e", - "Rev": "4643a7efec9b8fd7548882e7b30095711343100f" - }, - { - "ImportPath": "github.com/Unknwon/goconfig", - "Rev": "18bc852dc01720ee9f0e9e0a01a1bdbc8744095c" - }, - { - "ImportPath": "github.com/astaxie/beego", - "Comment": "v1.6.0-7-g23bb36d", - "Rev": "23bb36d35cf83ca8b95c4432aa846326530d24b9" - }, - { - "ImportPath": "github.com/beego/i18n", - "Rev": "e87155e8f0c05bf323d0b13470e1b97af0cb5652" - }, - { - "ImportPath": "github.com/docker/distribution/context", - "Comment": "v2.2.0-55-g226d8c4", - "Rev": "226d8c4f07955f1246308af3fdba9d89db6e4ca5" - }, - { - "ImportPath": "github.com/docker/distribution/registry/auth", - "Comment": "v2.2.0-55-g226d8c4", - "Rev": "226d8c4f07955f1246308af3fdba9d89db6e4ca5" - }, - { - "ImportPath": "github.com/docker/distribution/uuid", - "Comment": "v2.2.0-55-g226d8c4", - "Rev": "226d8c4f07955f1246308af3fdba9d89db6e4ca5" - }, - { - "ImportPath": "github.com/docker/libtrust", - "Rev": "9cbd2a1374f46905c68a4eb3694a130610adc62a" - }, - { - "ImportPath": "github.com/go-sql-driver/mysql", - "Comment": "v1.2-119-g527bcd5", - "Rev": "527bcd55aab2e53314f1a150922560174b493034" - }, - { - "ImportPath": "github.com/gorilla/context", - "Rev": "50c25fb3b2b3b3cc724e9b6ac75fb44b3bccd0da" - }, - { - "ImportPath": "github.com/gorilla/mux", - "Rev": "e444e69cbd2e2e3e0749a2f3c717cec491552bbf" - }, - { - "ImportPath": "github.com/mqu/openldap", - "Comment": "v0.2-11-g91bfc21", - "Rev": "91bfc2150f6c4991335de476c800caf26a990324" - }, - { - "ImportPath": "golang.org/x/crypto/pbkdf2", - "Comment": "release-20141215-1-g14213be", - "Rev": "14213be8fdd634b2e113c4f65cca53bdece9d8d4" - }, - { - "ImportPath": "golang.org/x/net/context", - "Comment": "release-20141215-1-g14213be", - "Rev": "14213be8fdd634b2e113c4f65cca53bdece9d8d4" - } - ] -} diff --git a/Godeps/Readme b/Godeps/Readme deleted file mode 100644 index 4cdaa53d5..000000000 --- a/Godeps/Readme +++ /dev/null @@ -1,5 +0,0 @@ -This directory tree is generated automatically by godep. - -Please do not edit. - -See https://github.com/tools/godep for more information. diff --git a/Godeps/_workspace/.gitignore b/Godeps/_workspace/.gitignore deleted file mode 100644 index f037d684e..000000000 --- a/Godeps/_workspace/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/pkg -/bin diff --git a/Godeps/_workspace/src/github.com/docker/distribution/context/context.go b/Godeps/_workspace/src/github.com/docker/distribution/context/context.go deleted file mode 100644 index 23cbf5b54..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/context/context.go +++ /dev/null @@ -1,85 +0,0 @@ -package context - -import ( - "sync" - - "github.com/docker/distribution/uuid" - "golang.org/x/net/context" -) - -// Context is a copy of Context from the golang.org/x/net/context package. -type Context interface { - context.Context -} - -// instanceContext is a context that provides only an instance id. It is -// provided as the main background context. -type instanceContext struct { - Context - id string // id of context, logged as "instance.id" - once sync.Once // once protect generation of the id -} - -func (ic *instanceContext) Value(key interface{}) interface{} { - if key == "instance.id" { - ic.once.Do(func() { - // We want to lazy initialize the UUID such that we don't - // call a random generator from the package initialization - // code. For various reasons random could not be available - // https://github.com/docker/distribution/issues/782 - ic.id = uuid.Generate().String() - }) - return ic.id - } - - return ic.Context.Value(key) -} - -var background = &instanceContext{ - Context: context.Background(), -} - -// Background returns a non-nil, empty Context. The background context -// provides a single key, "instance.id" that is globally unique to the -// process. -func Background() Context { - return background -} - -// WithValue returns a copy of parent in which the value associated with key is -// val. Use context Values only for request-scoped data that transits processes -// and APIs, not for passing optional parameters to functions. -func WithValue(parent Context, key, val interface{}) Context { - return context.WithValue(parent, key, val) -} - -// stringMapContext is a simple context implementation that checks a map for a -// key, falling back to a parent if not present. -type stringMapContext struct { - context.Context - m map[string]interface{} -} - -// WithValues returns a context that proxies lookups through a map. Only -// supports string keys. -func WithValues(ctx context.Context, m map[string]interface{}) context.Context { - mo := make(map[string]interface{}, len(m)) // make our own copy. - for k, v := range m { - mo[k] = v - } - - return stringMapContext{ - Context: ctx, - m: mo, - } -} - -func (smc stringMapContext) Value(key interface{}) interface{} { - if ks, ok := key.(string); ok { - if v, ok := smc.m[ks]; ok { - return v - } - } - - return smc.Context.Value(key) -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/context/doc.go b/Godeps/_workspace/src/github.com/docker/distribution/context/doc.go deleted file mode 100644 index 6fe1f817d..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/context/doc.go +++ /dev/null @@ -1,89 +0,0 @@ -// Package context provides several utilities for working with -// golang.org/x/net/context in http requests. Primarily, the focus is on -// logging relevent request information but this package is not limited to -// that purpose. -// -// The easiest way to get started is to get the background context: -// -// ctx := context.Background() -// -// The returned context should be passed around your application and be the -// root of all other context instances. If the application has a version, this -// line should be called before anything else: -// -// ctx := context.WithVersion(context.Background(), version) -// -// The above will store the version in the context and will be available to -// the logger. -// -// Logging -// -// The most useful aspect of this package is GetLogger. This function takes -// any context.Context interface and returns the current logger from the -// context. Canonical usage looks like this: -// -// GetLogger(ctx).Infof("something interesting happened") -// -// GetLogger also takes optional key arguments. The keys will be looked up in -// the context and reported with the logger. The following example would -// return a logger that prints the version with each log message: -// -// ctx := context.Context(context.Background(), "version", version) -// GetLogger(ctx, "version").Infof("this log message has a version field") -// -// The above would print out a log message like this: -// -// INFO[0000] this log message has a version field version=v2.0.0-alpha.2.m -// -// When used with WithLogger, we gain the ability to decorate the context with -// loggers that have information from disparate parts of the call stack. -// Following from the version example, we can build a new context with the -// configured logger such that we always print the version field: -// -// ctx = WithLogger(ctx, GetLogger(ctx, "version")) -// -// Since the logger has been pushed to the context, we can now get the version -// field for free with our log messages. Future calls to GetLogger on the new -// context will have the version field: -// -// GetLogger(ctx).Infof("this log message has a version field") -// -// This becomes more powerful when we start stacking loggers. Let's say we -// have the version logger from above but also want a request id. Using the -// context above, in our request scoped function, we place another logger in -// the context: -// -// ctx = context.WithValue(ctx, "http.request.id", "unique id") // called when building request context -// ctx = WithLogger(ctx, GetLogger(ctx, "http.request.id")) -// -// When GetLogger is called on the new context, "http.request.id" will be -// included as a logger field, along with the original "version" field: -// -// INFO[0000] this log message has a version field http.request.id=unique id version=v2.0.0-alpha.2.m -// -// Note that this only affects the new context, the previous context, with the -// version field, can be used independently. Put another way, the new logger, -// added to the request context, is unique to that context and can have -// request scoped varaibles. -// -// HTTP Requests -// -// This package also contains several methods for working with http requests. -// The concepts are very similar to those described above. We simply place the -// request in the context using WithRequest. This makes the request variables -// available. GetRequestLogger can then be called to get request specific -// variables in a log line: -// -// ctx = WithRequest(ctx, req) -// GetRequestLogger(ctx).Infof("request variables") -// -// Like above, if we want to include the request data in all log messages in -// the context, we push the logger to a new context and use that one: -// -// ctx = WithLogger(ctx, GetRequestLogger(ctx)) -// -// The concept is fairly powerful and ensures that calls throughout the stack -// can be traced in log messages. Using the fields like "http.request.id", one -// can analyze call flow for a particular request with a simple grep of the -// logs. -package context diff --git a/Godeps/_workspace/src/github.com/docker/distribution/context/http.go b/Godeps/_workspace/src/github.com/docker/distribution/context/http.go deleted file mode 100644 index 2cb1d0417..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/context/http.go +++ /dev/null @@ -1,364 +0,0 @@ -package context - -import ( - "errors" - "net" - "net/http" - "strings" - "sync" - "time" - - log "github.com/Sirupsen/logrus" - "github.com/docker/distribution/uuid" - "github.com/gorilla/mux" -) - -// Common errors used with this package. -var ( - ErrNoRequestContext = errors.New("no http request in context") - ErrNoResponseWriterContext = errors.New("no http response in context") -) - -func parseIP(ipStr string) net.IP { - ip := net.ParseIP(ipStr) - if ip == nil { - log.Warnf("invalid remote IP address: %q", ipStr) - } - return ip -} - -// RemoteAddr extracts the remote address of the request, taking into -// account proxy headers. -func RemoteAddr(r *http.Request) string { - if prior := r.Header.Get("X-Forwarded-For"); prior != "" { - proxies := strings.Split(prior, ",") - if len(proxies) > 0 { - remoteAddr := strings.Trim(proxies[0], " ") - if parseIP(remoteAddr) != nil { - return remoteAddr - } - } - } - // X-Real-Ip is less supported, but worth checking in the - // absence of X-Forwarded-For - if realIP := r.Header.Get("X-Real-Ip"); realIP != "" { - if parseIP(realIP) != nil { - return realIP - } - } - - return r.RemoteAddr -} - -// RemoteIP extracts the remote IP of the request, taking into -// account proxy headers. -func RemoteIP(r *http.Request) string { - addr := RemoteAddr(r) - - // Try parsing it as "IP:port" - if ip, _, err := net.SplitHostPort(addr); err == nil { - return ip - } - - return addr -} - -// WithRequest places the request on the context. The context of the request -// is assigned a unique id, available at "http.request.id". The request itself -// is available at "http.request". Other common attributes are available under -// the prefix "http.request.". If a request is already present on the context, -// this method will panic. -func WithRequest(ctx Context, r *http.Request) Context { - if ctx.Value("http.request") != nil { - // NOTE(stevvooe): This needs to be considered a programming error. It - // is unlikely that we'd want to have more than one request in - // context. - panic("only one request per context") - } - - return &httpRequestContext{ - Context: ctx, - startedAt: time.Now(), - id: uuid.Generate().String(), - r: r, - } -} - -// GetRequest returns the http request in the given context. Returns -// ErrNoRequestContext if the context does not have an http request associated -// with it. -func GetRequest(ctx Context) (*http.Request, error) { - if r, ok := ctx.Value("http.request").(*http.Request); r != nil && ok { - return r, nil - } - return nil, ErrNoRequestContext -} - -// GetRequestID attempts to resolve the current request id, if possible. An -// error is return if it is not available on the context. -func GetRequestID(ctx Context) string { - return GetStringValue(ctx, "http.request.id") -} - -// WithResponseWriter returns a new context and response writer that makes -// interesting response statistics available within the context. -func WithResponseWriter(ctx Context, w http.ResponseWriter) (Context, http.ResponseWriter) { - irw := instrumentedResponseWriter{ - ResponseWriter: w, - Context: ctx, - } - - if closeNotifier, ok := w.(http.CloseNotifier); ok { - irwCN := &instrumentedResponseWriterCN{ - instrumentedResponseWriter: irw, - CloseNotifier: closeNotifier, - } - - return irwCN, irwCN - } - - return &irw, &irw -} - -// GetResponseWriter returns the http.ResponseWriter from the provided -// context. If not present, ErrNoResponseWriterContext is returned. The -// returned instance provides instrumentation in the context. -func GetResponseWriter(ctx Context) (http.ResponseWriter, error) { - v := ctx.Value("http.response") - - rw, ok := v.(http.ResponseWriter) - if !ok || rw == nil { - return nil, ErrNoResponseWriterContext - } - - return rw, nil -} - -// getVarsFromRequest let's us change request vars implementation for testing -// and maybe future changes. -var getVarsFromRequest = mux.Vars - -// WithVars extracts gorilla/mux vars and makes them available on the returned -// context. Variables are available at keys with the prefix "vars.". For -// example, if looking for the variable "name", it can be accessed as -// "vars.name". Implementations that are accessing values need not know that -// the underlying context is implemented with gorilla/mux vars. -func WithVars(ctx Context, r *http.Request) Context { - return &muxVarsContext{ - Context: ctx, - vars: getVarsFromRequest(r), - } -} - -// GetRequestLogger returns a logger that contains fields from the request in -// the current context. If the request is not available in the context, no -// fields will display. Request loggers can safely be pushed onto the context. -func GetRequestLogger(ctx Context) Logger { - return GetLogger(ctx, - "http.request.id", - "http.request.method", - "http.request.host", - "http.request.uri", - "http.request.referer", - "http.request.useragent", - "http.request.remoteaddr", - "http.request.contenttype") -} - -// GetResponseLogger reads the current response stats and builds a logger. -// Because the values are read at call time, pushing a logger returned from -// this function on the context will lead to missing or invalid data. Only -// call this at the end of a request, after the response has been written. -func GetResponseLogger(ctx Context) Logger { - l := getLogrusLogger(ctx, - "http.response.written", - "http.response.status", - "http.response.contenttype") - - duration := Since(ctx, "http.request.startedat") - - if duration > 0 { - l = l.WithField("http.response.duration", duration.String()) - } - - return l -} - -// httpRequestContext makes information about a request available to context. -type httpRequestContext struct { - Context - - startedAt time.Time - id string - r *http.Request -} - -// Value returns a keyed element of the request for use in the context. To get -// the request itself, query "request". For other components, access them as -// "request.". For example, r.RequestURI -func (ctx *httpRequestContext) Value(key interface{}) interface{} { - if keyStr, ok := key.(string); ok { - if keyStr == "http.request" { - return ctx.r - } - - if !strings.HasPrefix(keyStr, "http.request.") { - goto fallback - } - - parts := strings.Split(keyStr, ".") - - if len(parts) != 3 { - goto fallback - } - - switch parts[2] { - case "uri": - return ctx.r.RequestURI - case "remoteaddr": - return RemoteAddr(ctx.r) - case "method": - return ctx.r.Method - case "host": - return ctx.r.Host - case "referer": - referer := ctx.r.Referer() - if referer != "" { - return referer - } - case "useragent": - return ctx.r.UserAgent() - case "id": - return ctx.id - case "startedat": - return ctx.startedAt - case "contenttype": - ct := ctx.r.Header.Get("Content-Type") - if ct != "" { - return ct - } - } - } - -fallback: - return ctx.Context.Value(key) -} - -type muxVarsContext struct { - Context - vars map[string]string -} - -func (ctx *muxVarsContext) Value(key interface{}) interface{} { - if keyStr, ok := key.(string); ok { - if keyStr == "vars" { - return ctx.vars - } - - if strings.HasPrefix(keyStr, "vars.") { - keyStr = strings.TrimPrefix(keyStr, "vars.") - } - - if v, ok := ctx.vars[keyStr]; ok { - return v - } - } - - return ctx.Context.Value(key) -} - -// instrumentedResponseWriterCN provides response writer information in a -// context. It implements http.CloseNotifier so that users can detect -// early disconnects. -type instrumentedResponseWriterCN struct { - instrumentedResponseWriter - http.CloseNotifier -} - -// instrumentedResponseWriter provides response writer information in a -// context. This variant is only used in the case where CloseNotifier is not -// implemented by the parent ResponseWriter. -type instrumentedResponseWriter struct { - http.ResponseWriter - Context - - mu sync.Mutex - status int - written int64 -} - -func (irw *instrumentedResponseWriter) Write(p []byte) (n int, err error) { - n, err = irw.ResponseWriter.Write(p) - - irw.mu.Lock() - irw.written += int64(n) - - // Guess the likely status if not set. - if irw.status == 0 { - irw.status = http.StatusOK - } - - irw.mu.Unlock() - - return -} - -func (irw *instrumentedResponseWriter) WriteHeader(status int) { - irw.ResponseWriter.WriteHeader(status) - - irw.mu.Lock() - irw.status = status - irw.mu.Unlock() -} - -func (irw *instrumentedResponseWriter) Flush() { - if flusher, ok := irw.ResponseWriter.(http.Flusher); ok { - flusher.Flush() - } -} - -func (irw *instrumentedResponseWriter) Value(key interface{}) interface{} { - if keyStr, ok := key.(string); ok { - if keyStr == "http.response" { - return irw - } - - if !strings.HasPrefix(keyStr, "http.response.") { - goto fallback - } - - parts := strings.Split(keyStr, ".") - - if len(parts) != 3 { - goto fallback - } - - irw.mu.Lock() - defer irw.mu.Unlock() - - switch parts[2] { - case "written": - return irw.written - case "status": - return irw.status - case "contenttype": - contentType := irw.Header().Get("Content-Type") - if contentType != "" { - return contentType - } - } - } - -fallback: - return irw.Context.Value(key) -} - -func (irw *instrumentedResponseWriterCN) Value(key interface{}) interface{} { - if keyStr, ok := key.(string); ok { - if keyStr == "http.response" { - return irw - } - } - - return irw.instrumentedResponseWriter.Value(key) -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/context/http_test.go b/Godeps/_workspace/src/github.com/docker/distribution/context/http_test.go deleted file mode 100644 index 3d4b3c8eb..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/context/http_test.go +++ /dev/null @@ -1,285 +0,0 @@ -package context - -import ( - "net/http" - "net/http/httptest" - "net/http/httputil" - "net/url" - "reflect" - "testing" - "time" -) - -func TestWithRequest(t *testing.T) { - var req http.Request - - start := time.Now() - req.Method = "GET" - req.Host = "example.com" - req.RequestURI = "/test-test" - req.Header = make(http.Header) - req.Header.Set("Referer", "foo.com/referer") - req.Header.Set("User-Agent", "test/0.1") - - ctx := WithRequest(Background(), &req) - for _, testcase := range []struct { - key string - expected interface{} - }{ - { - key: "http.request", - expected: &req, - }, - { - key: "http.request.id", - }, - { - key: "http.request.method", - expected: req.Method, - }, - { - key: "http.request.host", - expected: req.Host, - }, - { - key: "http.request.uri", - expected: req.RequestURI, - }, - { - key: "http.request.referer", - expected: req.Referer(), - }, - { - key: "http.request.useragent", - expected: req.UserAgent(), - }, - { - key: "http.request.remoteaddr", - expected: req.RemoteAddr, - }, - { - key: "http.request.startedat", - }, - } { - v := ctx.Value(testcase.key) - - if v == nil { - t.Fatalf("value not found for %q", testcase.key) - } - - if testcase.expected != nil && v != testcase.expected { - t.Fatalf("%s: %v != %v", testcase.key, v, testcase.expected) - } - - // Key specific checks! - switch testcase.key { - case "http.request.id": - if _, ok := v.(string); !ok { - t.Fatalf("request id not a string: %v", v) - } - case "http.request.startedat": - vt, ok := v.(time.Time) - if !ok { - t.Fatalf("value not a time: %v", v) - } - - now := time.Now() - if vt.After(now) { - t.Fatalf("time generated too late: %v > %v", vt, now) - } - - if vt.Before(start) { - t.Fatalf("time generated too early: %v < %v", vt, start) - } - } - } -} - -type testResponseWriter struct { - flushed bool - status int - written int64 - header http.Header -} - -func (trw *testResponseWriter) Header() http.Header { - if trw.header == nil { - trw.header = make(http.Header) - } - - return trw.header -} - -func (trw *testResponseWriter) Write(p []byte) (n int, err error) { - if trw.status == 0 { - trw.status = http.StatusOK - } - - n = len(p) - trw.written += int64(n) - return -} - -func (trw *testResponseWriter) WriteHeader(status int) { - trw.status = status -} - -func (trw *testResponseWriter) Flush() { - trw.flushed = true -} - -func TestWithResponseWriter(t *testing.T) { - trw := testResponseWriter{} - ctx, rw := WithResponseWriter(Background(), &trw) - - if ctx.Value("http.response") != rw { - t.Fatalf("response not available in context: %v != %v", ctx.Value("http.response"), rw) - } - - grw, err := GetResponseWriter(ctx) - if err != nil { - t.Fatalf("error getting response writer: %v", err) - } - - if grw != rw { - t.Fatalf("unexpected response writer returned: %#v != %#v", grw, rw) - } - - if ctx.Value("http.response.status") != 0 { - t.Fatalf("response status should always be a number and should be zero here: %v != 0", ctx.Value("http.response.status")) - } - - if n, err := rw.Write(make([]byte, 1024)); err != nil { - t.Fatalf("unexpected error writing: %v", err) - } else if n != 1024 { - t.Fatalf("unexpected number of bytes written: %v != %v", n, 1024) - } - - if ctx.Value("http.response.status") != http.StatusOK { - t.Fatalf("unexpected response status in context: %v != %v", ctx.Value("http.response.status"), http.StatusOK) - } - - if ctx.Value("http.response.written") != int64(1024) { - t.Fatalf("unexpected number reported bytes written: %v != %v", ctx.Value("http.response.written"), 1024) - } - - // Make sure flush propagates - rw.(http.Flusher).Flush() - - if !trw.flushed { - t.Fatalf("response writer not flushed") - } - - // Write another status and make sure context is correct. This normally - // wouldn't work except for in this contrived testcase. - rw.WriteHeader(http.StatusBadRequest) - - if ctx.Value("http.response.status") != http.StatusBadRequest { - t.Fatalf("unexpected response status in context: %v != %v", ctx.Value("http.response.status"), http.StatusBadRequest) - } -} - -func TestWithVars(t *testing.T) { - var req http.Request - vars := map[string]string{ - "foo": "asdf", - "bar": "qwer", - } - - getVarsFromRequest = func(r *http.Request) map[string]string { - if r != &req { - t.Fatalf("unexpected request: %v != %v", r, req) - } - - return vars - } - - ctx := WithVars(Background(), &req) - for _, testcase := range []struct { - key string - expected interface{} - }{ - { - key: "vars", - expected: vars, - }, - { - key: "vars.foo", - expected: "asdf", - }, - { - key: "vars.bar", - expected: "qwer", - }, - } { - v := ctx.Value(testcase.key) - - if !reflect.DeepEqual(v, testcase.expected) { - t.Fatalf("%q: %v != %v", testcase.key, v, testcase.expected) - } - } -} - -// SingleHostReverseProxy will insert an X-Forwarded-For header, and can be used to test -// RemoteAddr(). A fake RemoteAddr cannot be set on the HTTP request - it is overwritten -// at the transport layer to 127.0.0.1: . However, as the X-Forwarded-For header -// just contains the IP address, it is different enough for testing. -func TestRemoteAddr(t *testing.T) { - var expectedRemote string - backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - - if r.RemoteAddr == expectedRemote { - t.Errorf("Unexpected matching remote addresses") - } - - actualRemote := RemoteAddr(r) - if expectedRemote != actualRemote { - t.Errorf("Mismatching remote hosts: %v != %v", expectedRemote, actualRemote) - } - - w.WriteHeader(200) - })) - - defer backend.Close() - backendURL, err := url.Parse(backend.URL) - if err != nil { - t.Fatal(err) - } - - proxy := httputil.NewSingleHostReverseProxy(backendURL) - frontend := httptest.NewServer(proxy) - defer frontend.Close() - - // X-Forwarded-For set by proxy - expectedRemote = "127.0.0.1" - proxyReq, err := http.NewRequest("GET", frontend.URL, nil) - if err != nil { - t.Fatal(err) - } - - _, err = http.DefaultClient.Do(proxyReq) - if err != nil { - t.Fatal(err) - } - - // RemoteAddr in X-Real-Ip - getReq, err := http.NewRequest("GET", backend.URL, nil) - if err != nil { - t.Fatal(err) - } - - expectedRemote = "1.2.3.4" - getReq.Header["X-Real-ip"] = []string{expectedRemote} - _, err = http.DefaultClient.Do(getReq) - if err != nil { - t.Fatal(err) - } - - // Valid X-Real-Ip and invalid X-Forwarded-For - getReq.Header["X-forwarded-for"] = []string{"1.2.3"} - _, err = http.DefaultClient.Do(getReq) - if err != nil { - t.Fatal(err) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/context/logger.go b/Godeps/_workspace/src/github.com/docker/distribution/context/logger.go deleted file mode 100644 index fbb6a0511..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/context/logger.go +++ /dev/null @@ -1,116 +0,0 @@ -package context - -import ( - "fmt" - - "github.com/Sirupsen/logrus" - "runtime" -) - -// Logger provides a leveled-logging interface. -type Logger interface { - // standard logger methods - Print(args ...interface{}) - Printf(format string, args ...interface{}) - Println(args ...interface{}) - - Fatal(args ...interface{}) - Fatalf(format string, args ...interface{}) - Fatalln(args ...interface{}) - - Panic(args ...interface{}) - Panicf(format string, args ...interface{}) - Panicln(args ...interface{}) - - // Leveled methods, from logrus - Debug(args ...interface{}) - Debugf(format string, args ...interface{}) - Debugln(args ...interface{}) - - Error(args ...interface{}) - Errorf(format string, args ...interface{}) - Errorln(args ...interface{}) - - Info(args ...interface{}) - Infof(format string, args ...interface{}) - Infoln(args ...interface{}) - - Warn(args ...interface{}) - Warnf(format string, args ...interface{}) - Warnln(args ...interface{}) -} - -// WithLogger creates a new context with provided logger. -func WithLogger(ctx Context, logger Logger) Context { - return WithValue(ctx, "logger", logger) -} - -// GetLoggerWithField returns a logger instance with the specified field key -// and value without affecting the context. Extra specified keys will be -// resolved from the context. -func GetLoggerWithField(ctx Context, key, value interface{}, keys ...interface{}) Logger { - return getLogrusLogger(ctx, keys...).WithField(fmt.Sprint(key), value) -} - -// GetLoggerWithFields returns a logger instance with the specified fields -// without affecting the context. Extra specified keys will be resolved from -// the context. -func GetLoggerWithFields(ctx Context, fields map[interface{}]interface{}, keys ...interface{}) Logger { - // must convert from interface{} -> interface{} to string -> interface{} for logrus. - lfields := make(logrus.Fields, len(fields)) - for key, value := range fields { - lfields[fmt.Sprint(key)] = value - } - - return getLogrusLogger(ctx, keys...).WithFields(lfields) -} - -// GetLogger returns the logger from the current context, if present. If one -// or more keys are provided, they will be resolved on the context and -// included in the logger. While context.Value takes an interface, any key -// argument passed to GetLogger will be passed to fmt.Sprint when expanded as -// a logging key field. If context keys are integer constants, for example, -// its recommended that a String method is implemented. -func GetLogger(ctx Context, keys ...interface{}) Logger { - return getLogrusLogger(ctx, keys...) -} - -// GetLogrusLogger returns the logrus logger for the context. If one more keys -// are provided, they will be resolved on the context and included in the -// logger. Only use this function if specific logrus functionality is -// required. -func getLogrusLogger(ctx Context, keys ...interface{}) *logrus.Entry { - var logger *logrus.Entry - - // Get a logger, if it is present. - loggerInterface := ctx.Value("logger") - if loggerInterface != nil { - if lgr, ok := loggerInterface.(*logrus.Entry); ok { - logger = lgr - } - } - - if logger == nil { - fields := logrus.Fields{} - - // Fill in the instance id, if we have it. - instanceID := ctx.Value("instance.id") - if instanceID != nil { - fields["instance.id"] = instanceID - } - - fields["go.version"] = runtime.Version() - // If no logger is found, just return the standard logger. - logger = logrus.StandardLogger().WithFields(fields) - } - - fields := logrus.Fields{} - for _, key := range keys { - v := ctx.Value(key) - if v != nil { - fields[fmt.Sprint(key)] = v - } - } - - return logger.WithFields(fields) -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/context/trace.go b/Godeps/_workspace/src/github.com/docker/distribution/context/trace.go deleted file mode 100644 index af4f1351e..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/context/trace.go +++ /dev/null @@ -1,104 +0,0 @@ -package context - -import ( - "runtime" - "time" - - "github.com/docker/distribution/uuid" -) - -// WithTrace allocates a traced timing span in a new context. This allows a -// caller to track the time between calling WithTrace and the returned done -// function. When the done function is called, a log message is emitted with a -// "trace.duration" field, corresponding to the elapased time and a -// "trace.func" field, corresponding to the function that called WithTrace. -// -// The logging keys "trace.id" and "trace.parent.id" are provided to implement -// dapper-like tracing. This function should be complemented with a WithSpan -// method that could be used for tracing distributed RPC calls. -// -// The main benefit of this function is to post-process log messages or -// intercept them in a hook to provide timing data. Trace ids and parent ids -// can also be linked to provide call tracing, if so required. -// -// Here is an example of the usage: -// -// func timedOperation(ctx Context) { -// ctx, done := WithTrace(ctx) -// defer done("this will be the log message") -// // ... function body ... -// } -// -// If the function ran for roughly 1s, such a usage would emit a log message -// as follows: -// -// INFO[0001] this will be the log message trace.duration=1.004575763s trace.func=github.com/docker/distribution/context.traceOperation trace.id= ... -// -// Notice that the function name is automatically resolved, along with the -// package and a trace id is emitted that can be linked with parent ids. -func WithTrace(ctx Context) (Context, func(format string, a ...interface{})) { - if ctx == nil { - ctx = Background() - } - - pc, file, line, _ := runtime.Caller(1) - f := runtime.FuncForPC(pc) - ctx = &traced{ - Context: ctx, - id: uuid.Generate().String(), - start: time.Now(), - parent: GetStringValue(ctx, "trace.id"), - fnname: f.Name(), - file: file, - line: line, - } - - return ctx, func(format string, a ...interface{}) { - GetLogger(ctx, - "trace.duration", - "trace.id", - "trace.parent.id", - "trace.func", - "trace.file", - "trace.line"). - Debugf(format, a...) - } -} - -// traced represents a context that is traced for function call timing. It -// also provides fast lookup for the various attributes that are available on -// the trace. -type traced struct { - Context - id string - parent string - start time.Time - fnname string - file string - line int -} - -func (ts *traced) Value(key interface{}) interface{} { - switch key { - case "trace.start": - return ts.start - case "trace.duration": - return time.Since(ts.start) - case "trace.id": - return ts.id - case "trace.parent.id": - if ts.parent == "" { - return nil // must return nil to signal no parent. - } - - return ts.parent - case "trace.func": - return ts.fnname - case "trace.file": - return ts.file - case "trace.line": - return ts.line - } - - return ts.Context.Value(key) -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/context/trace_test.go b/Godeps/_workspace/src/github.com/docker/distribution/context/trace_test.go deleted file mode 100644 index 4b969fbb0..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/context/trace_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package context - -import ( - "runtime" - "testing" - "time" -) - -// TestWithTrace ensures that tracing has the expected values in the context. -func TestWithTrace(t *testing.T) { - pc, file, _, _ := runtime.Caller(0) // get current caller. - f := runtime.FuncForPC(pc) - - base := []valueTestCase{ - { - key: "trace.id", - notnilorempty: true, - }, - - { - key: "trace.file", - expected: file, - notnilorempty: true, - }, - { - key: "trace.line", - notnilorempty: true, - }, - { - key: "trace.start", - notnilorempty: true, - }, - } - - ctx, done := WithTrace(Background()) - defer done("this will be emitted at end of test") - - checkContextForValues(t, ctx, append(base, valueTestCase{ - key: "trace.func", - expected: f.Name(), - })) - - traced := func() { - parentID := ctx.Value("trace.id") // ensure the parent trace id is correct. - - pc, _, _, _ := runtime.Caller(0) // get current caller. - f := runtime.FuncForPC(pc) - ctx, done := WithTrace(ctx) - defer done("this should be subordinate to the other trace") - time.Sleep(time.Second) - checkContextForValues(t, ctx, append(base, valueTestCase{ - key: "trace.func", - expected: f.Name(), - }, valueTestCase{ - key: "trace.parent.id", - expected: parentID, - })) - } - traced() - - time.Sleep(time.Second) -} - -type valueTestCase struct { - key string - expected interface{} - notnilorempty bool // just check not empty/not nil -} - -func checkContextForValues(t *testing.T, ctx Context, values []valueTestCase) { - - for _, testcase := range values { - v := ctx.Value(testcase.key) - if testcase.notnilorempty { - if v == nil || v == "" { - t.Fatalf("value was nil or empty for %q: %#v", testcase.key, v) - } - continue - } - - if v != testcase.expected { - t.Fatalf("unexpected value for key %q: %v != %v", testcase.key, v, testcase.expected) - } - } -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/context/util.go b/Godeps/_workspace/src/github.com/docker/distribution/context/util.go deleted file mode 100644 index 299edc004..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/context/util.go +++ /dev/null @@ -1,32 +0,0 @@ -package context - -import ( - "time" -) - -// Since looks up key, which should be a time.Time, and returns the duration -// since that time. If the key is not found, the value returned will be zero. -// This is helpful when inferring metrics related to context execution times. -func Since(ctx Context, key interface{}) time.Duration { - startedAtI := ctx.Value(key) - if startedAtI != nil { - if startedAt, ok := startedAtI.(time.Time); ok { - return time.Since(startedAt) - } - } - - return 0 -} - -// GetStringValue returns a string value from the context. The empty string -// will be returned if not found. -func GetStringValue(ctx Context, key interface{}) (value string) { - stringi := ctx.Value(key) - if stringi != nil { - if valuev, ok := stringi.(string); ok { - value = valuev - } - } - - return value -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/context/version.go b/Godeps/_workspace/src/github.com/docker/distribution/context/version.go deleted file mode 100644 index 746cda02e..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/context/version.go +++ /dev/null @@ -1,16 +0,0 @@ -package context - -// WithVersion stores the application version in the context. The new context -// gets a logger to ensure log messages are marked with the application -// version. -func WithVersion(ctx Context, version string) Context { - ctx = WithValue(ctx, "version", version) - // push a new logger onto the stack - return WithLogger(ctx, GetLogger(ctx, "version")) -} - -// GetVersion returns the application version from the context. An empty -// string may returned if the version was not set on the context. -func GetVersion(ctx Context) string { - return GetStringValue(ctx, "version") -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/context/version_test.go b/Godeps/_workspace/src/github.com/docker/distribution/context/version_test.go deleted file mode 100644 index b81652691..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/context/version_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package context - -import "testing" - -func TestVersionContext(t *testing.T) { - ctx := Background() - - if GetVersion(ctx) != "" { - t.Fatalf("context should not yet have a version") - } - - expected := "2.1-whatever" - ctx = WithVersion(ctx, expected) - version := GetVersion(ctx) - - if version != expected { - t.Fatalf("version was not set: %q != %q", version, expected) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/auth.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/auth.go deleted file mode 100644 index b3bb580d2..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/auth.go +++ /dev/null @@ -1,144 +0,0 @@ -// Package auth defines a standard interface for request access controllers. -// -// An access controller has a simple interface with a single `Authorized` -// method which checks that a given request is authorized to perform one or -// more actions on one or more resources. This method should return a non-nil -// error if the request is not authorized. -// -// An implementation registers its access controller by name with a constructor -// which accepts an options map for configuring the access controller. -// -// options := map[string]interface{}{"sillySecret": "whysosilly?"} -// accessController, _ := auth.GetAccessController("silly", options) -// -// This `accessController` can then be used in a request handler like so: -// -// func updateOrder(w http.ResponseWriter, r *http.Request) { -// orderNumber := r.FormValue("orderNumber") -// resource := auth.Resource{Type: "customerOrder", Name: orderNumber} -// access := auth.Access{Resource: resource, Action: "update"} -// -// if ctx, err := accessController.Authorized(ctx, access); err != nil { -// if challenge, ok := err.(auth.Challenge) { -// // Let the challenge write the response. -// challenge.SetHeaders(w) -// w.WriteHeader(http.StatusUnauthorized) -// return -// } else { -// // Some other error. -// } -// } -// } -// -package auth - -import ( - "fmt" - "net/http" - - "github.com/docker/distribution/context" -) - -// UserInfo carries information about -// an autenticated/authorized client. -type UserInfo struct { - Name string -} - -// Resource describes a resource by type and name. -type Resource struct { - Type string - Name string -} - -// Access describes a specific action that is -// requested or allowed for a given resource. -type Access struct { - Resource - Action string -} - -// Challenge is a special error type which is used for HTTP 401 Unauthorized -// responses and is able to write the response with WWW-Authenticate challenge -// header values based on the error. -type Challenge interface { - error - - // SetHeaders prepares the request to conduct a challenge response by - // adding the an HTTP challenge header on the response message. Callers - // are expected to set the appropriate HTTP status code (e.g. 401) - // themselves. - SetHeaders(w http.ResponseWriter) -} - -// AccessController controls access to registry resources based on a request -// and required access levels for a request. Implementations can support both -// complete denial and http authorization challenges. -type AccessController interface { - // Authorized returns a non-nil error if the context is granted access and - // returns a new authorized context. If one or more Access structs are - // provided, the requested access will be compared with what is available - // to the context. The given context will contain a "http.request" key with - // a `*http.Request` value. If the error is non-nil, access should always - // be denied. The error may be of type Challenge, in which case the caller - // may have the Challenge handle the request or choose what action to take - // based on the Challenge header or response status. The returned context - // object should have a "auth.user" value set to a UserInfo struct. - Authorized(ctx context.Context, access ...Access) (context.Context, error) -} - -// WithUser returns a context with the authorized user info. -func WithUser(ctx context.Context, user UserInfo) context.Context { - return userInfoContext{ - Context: ctx, - user: user, - } -} - -type userInfoContext struct { - context.Context - user UserInfo -} - -func (uic userInfoContext) Value(key interface{}) interface{} { - switch key { - case "auth.user": - return uic.user - case "auth.user.name": - return uic.user.Name - } - - return uic.Context.Value(key) -} - -// InitFunc is the type of an AccessController factory function and is used -// to register the constructor for different AccesController backends. -type InitFunc func(options map[string]interface{}) (AccessController, error) - -var accessControllers map[string]InitFunc - -func init() { - accessControllers = make(map[string]InitFunc) -} - -// Register is used to register an InitFunc for -// an AccessController backend with the given name. -func Register(name string, initFunc InitFunc) error { - if _, exists := accessControllers[name]; exists { - return fmt.Errorf("name already registered: %s", name) - } - - accessControllers[name] = initFunc - - return nil -} - -// GetAccessController constructs an AccessController -// with the given options using the named backend. -func GetAccessController(name string, options map[string]interface{}) (AccessController, error) { - if initFunc, exists := accessControllers[name]; exists { - return initFunc(options) - } - - return nil, fmt.Errorf("no access controller registered with name: %s", name) -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/access.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/access.go deleted file mode 100644 index 82d3556dc..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/access.go +++ /dev/null @@ -1,102 +0,0 @@ -// Package htpasswd provides a simple authentication scheme that checks for the -// user credential hash in an htpasswd formatted file in a configuration-determined -// location. -// -// This authentication method MUST be used under TLS, as simple token-replay attack is possible. -package htpasswd - -import ( - "errors" - "fmt" - "net/http" - "os" - - "github.com/docker/distribution/context" - "github.com/docker/distribution/registry/auth" -) - -var ( - // ErrInvalidCredential is returned when the auth token does not authenticate correctly. - ErrInvalidCredential = errors.New("invalid authorization credential") - - // ErrAuthenticationFailure returned when authentication failure to be presented to agent. - ErrAuthenticationFailure = errors.New("authentication failured") -) - -type accessController struct { - realm string - htpasswd *htpasswd -} - -var _ auth.AccessController = &accessController{} - -func newAccessController(options map[string]interface{}) (auth.AccessController, error) { - realm, present := options["realm"] - if _, ok := realm.(string); !present || !ok { - return nil, fmt.Errorf(`"realm" must be set for htpasswd access controller`) - } - - path, present := options["path"] - if _, ok := path.(string); !present || !ok { - return nil, fmt.Errorf(`"path" must be set for htpasswd access controller`) - } - - f, err := os.Open(path.(string)) - if err != nil { - return nil, err - } - defer f.Close() - - h, err := newHTPasswd(f) - if err != nil { - return nil, err - } - - return &accessController{realm: realm.(string), htpasswd: h}, nil -} - -func (ac *accessController) Authorized(ctx context.Context, accessRecords ...auth.Access) (context.Context, error) { - req, err := context.GetRequest(ctx) - if err != nil { - return nil, err - } - - username, password, ok := req.BasicAuth() - if !ok { - return nil, &challenge{ - realm: ac.realm, - err: ErrInvalidCredential, - } - } - - if err := ac.htpasswd.authenticateUser(username, password); err != nil { - context.GetLogger(ctx).Errorf("error authenticating user %q: %v", username, err) - return nil, &challenge{ - realm: ac.realm, - err: ErrAuthenticationFailure, - } - } - - return auth.WithUser(ctx, auth.UserInfo{Name: username}), nil -} - -// challenge implements the auth.Challenge interface. -type challenge struct { - realm string - err error -} - -var _ auth.Challenge = challenge{} - -// SetHeaders sets the basic challenge header on the response. -func (ch challenge) SetHeaders(w http.ResponseWriter) { - w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", ch.realm)) -} - -func (ch challenge) Error() string { - return fmt.Sprintf("basic authentication challenge for realm %q: %s", ch.realm, ch.err) -} - -func init() { - auth.Register("htpasswd", auth.InitFunc(newAccessController)) -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/access_test.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/access_test.go deleted file mode 100644 index db0405475..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/access_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package htpasswd - -import ( - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - - "github.com/docker/distribution/context" - "github.com/docker/distribution/registry/auth" -) - -func TestBasicAccessController(t *testing.T) { - testRealm := "The-Shire" - testUsers := []string{"bilbo", "frodo", "MiShil", "DeokMan"} - testPasswords := []string{"baggins", "baggins", "새주", "공주님"} - testHtpasswdContent := `bilbo:{SHA}5siv5c0SHx681xU6GiSx9ZQryqs= - frodo:$2y$05$926C3y10Quzn/LnqQH86VOEVh/18T6RnLaS.khre96jLNL/7e.K5W - MiShil:$2y$05$0oHgwMehvoe8iAWS8I.7l.KoECXrwVaC16RPfaSCU5eVTFrATuMI2 - DeokMan:공주님` - - tempFile, err := ioutil.TempFile("", "htpasswd-test") - if err != nil { - t.Fatal("could not create temporary htpasswd file") - } - if _, err = tempFile.WriteString(testHtpasswdContent); err != nil { - t.Fatal("could not write temporary htpasswd file") - } - - options := map[string]interface{}{ - "realm": testRealm, - "path": tempFile.Name(), - } - ctx := context.Background() - - accessController, err := newAccessController(options) - if err != nil { - t.Fatal("error creating access controller") - } - - tempFile.Close() - - var userNumber = 0 - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := context.WithRequest(ctx, r) - authCtx, err := accessController.Authorized(ctx) - if err != nil { - switch err := err.(type) { - case auth.Challenge: - err.SetHeaders(w) - w.WriteHeader(http.StatusUnauthorized) - return - default: - t.Fatalf("unexpected error authorizing request: %v", err) - } - } - - userInfo, ok := authCtx.Value("auth.user").(auth.UserInfo) - if !ok { - t.Fatal("basic accessController did not set auth.user context") - } - - if userInfo.Name != testUsers[userNumber] { - t.Fatalf("expected user name %q, got %q", testUsers[userNumber], userInfo.Name) - } - - w.WriteHeader(http.StatusNoContent) - })) - - client := &http.Client{ - CheckRedirect: nil, - } - - req, _ := http.NewRequest("GET", server.URL, nil) - resp, err := client.Do(req) - - if err != nil { - t.Fatalf("unexpected error during GET: %v", err) - } - defer resp.Body.Close() - - // Request should not be authorized - if resp.StatusCode != http.StatusUnauthorized { - t.Fatalf("unexpected non-fail response status: %v != %v", resp.StatusCode, http.StatusUnauthorized) - } - - nonbcrypt := map[string]struct{}{ - "bilbo": {}, - "DeokMan": {}, - } - - for i := 0; i < len(testUsers); i++ { - userNumber = i - req, err := http.NewRequest("GET", server.URL, nil) - if err != nil { - t.Fatalf("error allocating new request: %v", err) - } - - req.SetBasicAuth(testUsers[i], testPasswords[i]) - - resp, err = client.Do(req) - if err != nil { - t.Fatalf("unexpected error during GET: %v", err) - } - defer resp.Body.Close() - - if _, ok := nonbcrypt[testUsers[i]]; ok { - // these are not allowed. - // Request should be authorized - if resp.StatusCode != http.StatusUnauthorized { - t.Fatalf("unexpected non-success response status: %v != %v for %s %s", resp.StatusCode, http.StatusUnauthorized, testUsers[i], testPasswords[i]) - } - } else { - // Request should be authorized - if resp.StatusCode != http.StatusNoContent { - t.Fatalf("unexpected non-success response status: %v != %v for %s %s", resp.StatusCode, http.StatusNoContent, testUsers[i], testPasswords[i]) - } - } - } - -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/htpasswd.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/htpasswd.go deleted file mode 100644 index 494ad0a76..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/htpasswd.go +++ /dev/null @@ -1,80 +0,0 @@ -package htpasswd - -import ( - "bufio" - "fmt" - "io" - "strings" - - "golang.org/x/crypto/bcrypt" -) - -// htpasswd holds a path to a system .htpasswd file and the machinery to parse -// it. Only bcrypt hash entries are supported. -type htpasswd struct { - entries map[string][]byte // maps username to password byte slice. -} - -// newHTPasswd parses the reader and returns an htpasswd or an error. -func newHTPasswd(rd io.Reader) (*htpasswd, error) { - entries, err := parseHTPasswd(rd) - if err != nil { - return nil, err - } - - return &htpasswd{entries: entries}, nil -} - -// AuthenticateUser checks a given user:password credential against the -// receiving HTPasswd's file. If the check passes, nil is returned. -func (htpasswd *htpasswd) authenticateUser(username string, password string) error { - credentials, ok := htpasswd.entries[username] - if !ok { - // timing attack paranoia - bcrypt.CompareHashAndPassword([]byte{}, []byte(password)) - - return ErrAuthenticationFailure - } - - err := bcrypt.CompareHashAndPassword([]byte(credentials), []byte(password)) - if err != nil { - return ErrAuthenticationFailure - } - - return nil -} - -// parseHTPasswd parses the contents of htpasswd. This will read all the -// entries in the file, whether or not they are needed. An error is returned -// if an syntax errors are encountered or if the reader fails. -func parseHTPasswd(rd io.Reader) (map[string][]byte, error) { - entries := map[string][]byte{} - scanner := bufio.NewScanner(rd) - var line int - for scanner.Scan() { - line++ // 1-based line numbering - t := strings.TrimSpace(scanner.Text()) - - if len(t) < 1 { - continue - } - - // lines that *begin* with a '#' are considered comments - if t[0] == '#' { - continue - } - - i := strings.Index(t, ":") - if i < 0 || i >= len(t) { - return nil, fmt.Errorf("htpasswd: invalid entry at line %d: %q", line, scanner.Text()) - } - - entries[t[:i]] = []byte(t[i+1:]) - } - - if err := scanner.Err(); err != nil { - return nil, err - } - - return entries, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/htpasswd_test.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/htpasswd_test.go deleted file mode 100644 index 309c359ad..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/htpasswd/htpasswd_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package htpasswd - -import ( - "fmt" - "reflect" - "strings" - "testing" -) - -func TestParseHTPasswd(t *testing.T) { - - for _, tc := range []struct { - desc string - input string - err error - entries map[string][]byte - }{ - { - desc: "basic example", - input: ` -# This is a comment in a basic example. -bilbo:{SHA}5siv5c0SHx681xU6GiSx9ZQryqs= -frodo:$2y$05$926C3y10Quzn/LnqQH86VOEVh/18T6RnLaS.khre96jLNL/7e.K5W -MiShil:$2y$05$0oHgwMehvoe8iAWS8I.7l.KoECXrwVaC16RPfaSCU5eVTFrATuMI2 -DeokMan:공주님 -`, - entries: map[string][]byte{ - "bilbo": []byte("{SHA}5siv5c0SHx681xU6GiSx9ZQryqs="), - "frodo": []byte("$2y$05$926C3y10Quzn/LnqQH86VOEVh/18T6RnLaS.khre96jLNL/7e.K5W"), - "MiShil": []byte("$2y$05$0oHgwMehvoe8iAWS8I.7l.KoECXrwVaC16RPfaSCU5eVTFrATuMI2"), - "DeokMan": []byte("공주님"), - }, - }, - { - desc: "ensures comments are filtered", - input: ` -# asdf:asdf -`, - }, - { - desc: "ensure midline hash is not comment", - input: ` -asdf:as#df -`, - entries: map[string][]byte{ - "asdf": []byte("as#df"), - }, - }, - { - desc: "ensure midline hash is not comment", - input: ` -# A valid comment -valid:entry -asdf -`, - err: fmt.Errorf(`htpasswd: invalid entry at line 4: "asdf"`), - }, - } { - - entries, err := parseHTPasswd(strings.NewReader(tc.input)) - if err != tc.err { - if tc.err == nil { - t.Fatalf("%s: unexpected error: %v", tc.desc, err) - } else { - if err.Error() != tc.err.Error() { // use string equality here. - t.Fatalf("%s: expected error not returned: %v != %v", tc.desc, err, tc.err) - } - } - } - - if tc.err != nil { - continue // don't test output - } - - // allow empty and nil to be equal - if tc.entries == nil { - tc.entries = map[string][]byte{} - } - - if !reflect.DeepEqual(entries, tc.entries) { - t.Fatalf("%s: entries not parsed correctly: %v != %v", tc.desc, entries, tc.entries) - } - } - -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/silly/access.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/silly/access.go deleted file mode 100644 index 2b801d946..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/silly/access.go +++ /dev/null @@ -1,97 +0,0 @@ -// Package silly provides a simple authentication scheme that checks for the -// existence of an Authorization header and issues access if is present and -// non-empty. -// -// This package is present as an example implementation of a minimal -// auth.AccessController and for testing. This is not suitable for any kind of -// production security. -package silly - -import ( - "fmt" - "net/http" - "strings" - - "github.com/docker/distribution/context" - "github.com/docker/distribution/registry/auth" -) - -// accessController provides a simple implementation of auth.AccessController -// that simply checks for a non-empty Authorization header. It is useful for -// demonstration and testing. -type accessController struct { - realm string - service string -} - -var _ auth.AccessController = &accessController{} - -func newAccessController(options map[string]interface{}) (auth.AccessController, error) { - realm, present := options["realm"] - if _, ok := realm.(string); !present || !ok { - return nil, fmt.Errorf(`"realm" must be set for silly access controller`) - } - - service, present := options["service"] - if _, ok := service.(string); !present || !ok { - return nil, fmt.Errorf(`"service" must be set for silly access controller`) - } - - return &accessController{realm: realm.(string), service: service.(string)}, nil -} - -// Authorized simply checks for the existence of the authorization header, -// responding with a bearer challenge if it doesn't exist. -func (ac *accessController) Authorized(ctx context.Context, accessRecords ...auth.Access) (context.Context, error) { - req, err := context.GetRequest(ctx) - if err != nil { - return nil, err - } - - if req.Header.Get("Authorization") == "" { - challenge := challenge{ - realm: ac.realm, - service: ac.service, - } - - if len(accessRecords) > 0 { - var scopes []string - for _, access := range accessRecords { - scopes = append(scopes, fmt.Sprintf("%s:%s:%s", access.Type, access.Resource.Name, access.Action)) - } - challenge.scope = strings.Join(scopes, " ") - } - - return nil, &challenge - } - - return auth.WithUser(ctx, auth.UserInfo{Name: "silly"}), nil -} - -type challenge struct { - realm string - service string - scope string -} - -var _ auth.Challenge = challenge{} - -// SetHeaders sets a simple bearer challenge on the response. -func (ch challenge) SetHeaders(w http.ResponseWriter) { - header := fmt.Sprintf("Bearer realm=%q,service=%q", ch.realm, ch.service) - - if ch.scope != "" { - header = fmt.Sprintf("%s,scope=%q", header, ch.scope) - } - - w.Header().Set("WWW-Authenticate", header) -} - -func (ch challenge) Error() string { - return fmt.Sprintf("silly authentication challenge: %#v", ch) -} - -// init registers the silly auth backend. -func init() { - auth.Register("silly", auth.InitFunc(newAccessController)) -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/silly/access_test.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/silly/access_test.go deleted file mode 100644 index ff2155b18..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/silly/access_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package silly - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/docker/distribution/context" - "github.com/docker/distribution/registry/auth" -) - -func TestSillyAccessController(t *testing.T) { - ac := &accessController{ - realm: "test-realm", - service: "test-service", - } - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := context.WithValue(nil, "http.request", r) - authCtx, err := ac.Authorized(ctx) - if err != nil { - switch err := err.(type) { - case auth.Challenge: - err.SetHeaders(w) - w.WriteHeader(http.StatusUnauthorized) - return - default: - t.Fatalf("unexpected error authorizing request: %v", err) - } - } - - userInfo, ok := authCtx.Value("auth.user").(auth.UserInfo) - if !ok { - t.Fatal("silly accessController did not set auth.user context") - } - - if userInfo.Name != "silly" { - t.Fatalf("expected user name %q, got %q", "silly", userInfo.Name) - } - - w.WriteHeader(http.StatusNoContent) - })) - - resp, err := http.Get(server.URL) - if err != nil { - t.Fatalf("unexpected error during GET: %v", err) - } - defer resp.Body.Close() - - // Request should not be authorized - if resp.StatusCode != http.StatusUnauthorized { - t.Fatalf("unexpected response status: %v != %v", resp.StatusCode, http.StatusUnauthorized) - } - - req, err := http.NewRequest("GET", server.URL, nil) - if err != nil { - t.Fatalf("unexpected error creating new request: %v", err) - } - req.Header.Set("Authorization", "seriously, anything") - - resp, err = http.DefaultClient.Do(req) - if err != nil { - t.Fatalf("unexpected error during GET: %v", err) - } - defer resp.Body.Close() - - // Request should not be authorized - if resp.StatusCode != http.StatusNoContent { - t.Fatalf("unexpected response status: %v != %v", resp.StatusCode, http.StatusNoContent) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/accesscontroller.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/accesscontroller.go deleted file mode 100644 index 5b1ff7caa..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/accesscontroller.go +++ /dev/null @@ -1,268 +0,0 @@ -package token - -import ( - "crypto" - "crypto/x509" - "encoding/pem" - "errors" - "fmt" - "io/ioutil" - "net/http" - "os" - "strings" - - "github.com/docker/distribution/context" - "github.com/docker/distribution/registry/auth" - "github.com/docker/libtrust" -) - -// accessSet maps a typed, named resource to -// a set of actions requested or authorized. -type accessSet map[auth.Resource]actionSet - -// newAccessSet constructs an accessSet from -// a variable number of auth.Access items. -func newAccessSet(accessItems ...auth.Access) accessSet { - accessSet := make(accessSet, len(accessItems)) - - for _, access := range accessItems { - resource := auth.Resource{ - Type: access.Type, - Name: access.Name, - } - - set, exists := accessSet[resource] - if !exists { - set = newActionSet() - accessSet[resource] = set - } - - set.add(access.Action) - } - - return accessSet -} - -// contains returns whether or not the given access is in this accessSet. -func (s accessSet) contains(access auth.Access) bool { - actionSet, ok := s[access.Resource] - if ok { - return actionSet.contains(access.Action) - } - - return false -} - -// scopeParam returns a collection of scopes which can -// be used for a WWW-Authenticate challenge parameter. -// See https://tools.ietf.org/html/rfc6750#section-3 -func (s accessSet) scopeParam() string { - scopes := make([]string, 0, len(s)) - - for resource, actionSet := range s { - actions := strings.Join(actionSet.keys(), ",") - scopes = append(scopes, fmt.Sprintf("%s:%s:%s", resource.Type, resource.Name, actions)) - } - - return strings.Join(scopes, " ") -} - -// Errors used and exported by this package. -var ( - ErrInsufficientScope = errors.New("insufficient scope") - ErrTokenRequired = errors.New("authorization token required") -) - -// authChallenge implements the auth.Challenge interface. -type authChallenge struct { - err error - realm string - service string - accessSet accessSet -} - -var _ auth.Challenge = authChallenge{} - -// Error returns the internal error string for this authChallenge. -func (ac authChallenge) Error() string { - return ac.err.Error() -} - -// Status returns the HTTP Response Status Code for this authChallenge. -func (ac authChallenge) Status() int { - return http.StatusUnauthorized -} - -// challengeParams constructs the value to be used in -// the WWW-Authenticate response challenge header. -// See https://tools.ietf.org/html/rfc6750#section-3 -func (ac authChallenge) challengeParams() string { - str := fmt.Sprintf("Bearer realm=%q,service=%q", ac.realm, ac.service) - - if scope := ac.accessSet.scopeParam(); scope != "" { - str = fmt.Sprintf("%s,scope=%q", str, scope) - } - - if ac.err == ErrInvalidToken || ac.err == ErrMalformedToken { - str = fmt.Sprintf("%s,error=%q", str, "invalid_token") - } else if ac.err == ErrInsufficientScope { - str = fmt.Sprintf("%s,error=%q", str, "insufficient_scope") - } - - return str -} - -// SetChallenge sets the WWW-Authenticate value for the response. -func (ac authChallenge) SetHeaders(w http.ResponseWriter) { - w.Header().Add("WWW-Authenticate", ac.challengeParams()) -} - -// accessController implements the auth.AccessController interface. -type accessController struct { - realm string - issuer string - service string - rootCerts *x509.CertPool - trustedKeys map[string]libtrust.PublicKey -} - -// tokenAccessOptions is a convenience type for handling -// options to the contstructor of an accessController. -type tokenAccessOptions struct { - realm string - issuer string - service string - rootCertBundle string -} - -// checkOptions gathers the necessary options -// for an accessController from the given map. -func checkOptions(options map[string]interface{}) (tokenAccessOptions, error) { - var opts tokenAccessOptions - - keys := []string{"realm", "issuer", "service", "rootcertbundle"} - vals := make([]string, 0, len(keys)) - for _, key := range keys { - val, ok := options[key].(string) - if !ok { - return opts, fmt.Errorf("token auth requires a valid option string: %q", key) - } - vals = append(vals, val) - } - - opts.realm, opts.issuer, opts.service, opts.rootCertBundle = vals[0], vals[1], vals[2], vals[3] - - return opts, nil -} - -// newAccessController creates an accessController using the given options. -func newAccessController(options map[string]interface{}) (auth.AccessController, error) { - config, err := checkOptions(options) - if err != nil { - return nil, err - } - - fp, err := os.Open(config.rootCertBundle) - if err != nil { - return nil, fmt.Errorf("unable to open token auth root certificate bundle file %q: %s", config.rootCertBundle, err) - } - defer fp.Close() - - rawCertBundle, err := ioutil.ReadAll(fp) - if err != nil { - return nil, fmt.Errorf("unable to read token auth root certificate bundle file %q: %s", config.rootCertBundle, err) - } - - var rootCerts []*x509.Certificate - pemBlock, rawCertBundle := pem.Decode(rawCertBundle) - for pemBlock != nil { - cert, err := x509.ParseCertificate(pemBlock.Bytes) - if err != nil { - return nil, fmt.Errorf("unable to parse token auth root certificate: %s", err) - } - - rootCerts = append(rootCerts, cert) - - pemBlock, rawCertBundle = pem.Decode(rawCertBundle) - } - - if len(rootCerts) == 0 { - return nil, errors.New("token auth requires at least one token signing root certificate") - } - - rootPool := x509.NewCertPool() - trustedKeys := make(map[string]libtrust.PublicKey, len(rootCerts)) - for _, rootCert := range rootCerts { - rootPool.AddCert(rootCert) - pubKey, err := libtrust.FromCryptoPublicKey(crypto.PublicKey(rootCert.PublicKey)) - if err != nil { - return nil, fmt.Errorf("unable to get public key from token auth root certificate: %s", err) - } - trustedKeys[pubKey.KeyID()] = pubKey - } - - return &accessController{ - realm: config.realm, - issuer: config.issuer, - service: config.service, - rootCerts: rootPool, - trustedKeys: trustedKeys, - }, nil -} - -// Authorized handles checking whether the given request is authorized -// for actions on resources described by the given access items. -func (ac *accessController) Authorized(ctx context.Context, accessItems ...auth.Access) (context.Context, error) { - challenge := &authChallenge{ - realm: ac.realm, - service: ac.service, - accessSet: newAccessSet(accessItems...), - } - - req, err := context.GetRequest(ctx) - if err != nil { - return nil, err - } - - parts := strings.Split(req.Header.Get("Authorization"), " ") - - if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" { - challenge.err = ErrTokenRequired - return nil, challenge - } - - rawToken := parts[1] - - token, err := NewToken(rawToken) - if err != nil { - challenge.err = err - return nil, challenge - } - - verifyOpts := VerifyOptions{ - TrustedIssuers: []string{ac.issuer}, - AcceptedAudiences: []string{ac.service}, - Roots: ac.rootCerts, - TrustedKeys: ac.trustedKeys, - } - - if err = token.Verify(verifyOpts); err != nil { - challenge.err = err - return nil, challenge - } - - accessSet := token.accessSet() - for _, access := range accessItems { - if !accessSet.contains(access) { - challenge.err = ErrInsufficientScope - return nil, challenge - } - } - - return auth.WithUser(ctx, auth.UserInfo{Name: token.Claims.Subject}), nil -} - -// init handles registering the token auth backend. -func init() { - auth.Register("token", auth.InitFunc(newAccessController)) -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/stringset.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/stringset.go deleted file mode 100644 index 1d04f104c..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/stringset.go +++ /dev/null @@ -1,35 +0,0 @@ -package token - -// StringSet is a useful type for looking up strings. -type stringSet map[string]struct{} - -// NewStringSet creates a new StringSet with the given strings. -func newStringSet(keys ...string) stringSet { - ss := make(stringSet, len(keys)) - ss.add(keys...) - return ss -} - -// Add inserts the given keys into this StringSet. -func (ss stringSet) add(keys ...string) { - for _, key := range keys { - ss[key] = struct{}{} - } -} - -// Contains returns whether the given key is in this StringSet. -func (ss stringSet) contains(key string) bool { - _, ok := ss[key] - return ok -} - -// Keys returns a slice of all keys in this StringSet. -func (ss stringSet) keys() []string { - keys := make([]string, 0, len(ss)) - - for key := range ss { - keys = append(keys, key) - } - - return keys -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/token.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/token.go deleted file mode 100644 index 166816eea..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/token.go +++ /dev/null @@ -1,343 +0,0 @@ -package token - -import ( - "crypto" - "crypto/x509" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "strings" - "time" - - log "github.com/Sirupsen/logrus" - "github.com/docker/libtrust" - - "github.com/docker/distribution/registry/auth" -) - -const ( - // TokenSeparator is the value which separates the header, claims, and - // signature in the compact serialization of a JSON Web Token. - TokenSeparator = "." -) - -// Errors used by token parsing and verification. -var ( - ErrMalformedToken = errors.New("malformed token") - ErrInvalidToken = errors.New("invalid token") -) - -// ResourceActions stores allowed actions on a named and typed resource. -type ResourceActions struct { - Type string `json:"type"` - Name string `json:"name"` - Actions []string `json:"actions"` -} - -// ClaimSet describes the main section of a JSON Web Token. -type ClaimSet struct { - // Public claims - Issuer string `json:"iss"` - Subject string `json:"sub"` - Audience string `json:"aud"` - Expiration int64 `json:"exp"` - NotBefore int64 `json:"nbf"` - IssuedAt int64 `json:"iat"` - JWTID string `json:"jti"` - - // Private claims - Access []*ResourceActions `json:"access"` -} - -// Header describes the header section of a JSON Web Token. -type Header struct { - Type string `json:"typ"` - SigningAlg string `json:"alg"` - KeyID string `json:"kid,omitempty"` - X5c []string `json:"x5c,omitempty"` - RawJWK json.RawMessage `json:"jwk,omitempty"` -} - -// Token describes a JSON Web Token. -type Token struct { - Raw string - Header *Header - Claims *ClaimSet - Signature []byte -} - -// VerifyOptions is used to specify -// options when verifying a JSON Web Token. -type VerifyOptions struct { - TrustedIssuers []string - AcceptedAudiences []string - Roots *x509.CertPool - TrustedKeys map[string]libtrust.PublicKey -} - -// NewToken parses the given raw token string -// and constructs an unverified JSON Web Token. -func NewToken(rawToken string) (*Token, error) { - parts := strings.Split(rawToken, TokenSeparator) - if len(parts) != 3 { - return nil, ErrMalformedToken - } - - var ( - rawHeader, rawClaims = parts[0], parts[1] - headerJSON, claimsJSON []byte - err error - ) - - defer func() { - if err != nil { - log.Errorf("error while unmarshalling raw token: %s", err) - } - }() - - if headerJSON, err = joseBase64UrlDecode(rawHeader); err != nil { - err = fmt.Errorf("unable to decode header: %s", err) - return nil, ErrMalformedToken - } - - if claimsJSON, err = joseBase64UrlDecode(rawClaims); err != nil { - err = fmt.Errorf("unable to decode claims: %s", err) - return nil, ErrMalformedToken - } - - token := new(Token) - token.Header = new(Header) - token.Claims = new(ClaimSet) - - token.Raw = strings.Join(parts[:2], TokenSeparator) - if token.Signature, err = joseBase64UrlDecode(parts[2]); err != nil { - err = fmt.Errorf("unable to decode signature: %s", err) - return nil, ErrMalformedToken - } - - if err = json.Unmarshal(headerJSON, token.Header); err != nil { - return nil, ErrMalformedToken - } - - if err = json.Unmarshal(claimsJSON, token.Claims); err != nil { - return nil, ErrMalformedToken - } - - return token, nil -} - -// Verify attempts to verify this token using the given options. -// Returns a nil error if the token is valid. -func (t *Token) Verify(verifyOpts VerifyOptions) error { - // Verify that the Issuer claim is a trusted authority. - if !contains(verifyOpts.TrustedIssuers, t.Claims.Issuer) { - log.Errorf("token from untrusted issuer: %q", t.Claims.Issuer) - return ErrInvalidToken - } - - // Verify that the Audience claim is allowed. - if !contains(verifyOpts.AcceptedAudiences, t.Claims.Audience) { - log.Errorf("token intended for another audience: %q", t.Claims.Audience) - return ErrInvalidToken - } - - // Verify that the token is currently usable and not expired. - currentUnixTime := time.Now().Unix() - if !(t.Claims.NotBefore <= currentUnixTime && currentUnixTime <= t.Claims.Expiration) { - log.Errorf("token not to be used before %d or after %d - currently %d", t.Claims.NotBefore, t.Claims.Expiration, currentUnixTime) - return ErrInvalidToken - } - - // Verify the token signature. - if len(t.Signature) == 0 { - log.Error("token has no signature") - return ErrInvalidToken - } - - // Verify that the signing key is trusted. - signingKey, err := t.VerifySigningKey(verifyOpts) - if err != nil { - log.Error(err) - return ErrInvalidToken - } - - // Finally, verify the signature of the token using the key which signed it. - if err := signingKey.Verify(strings.NewReader(t.Raw), t.Header.SigningAlg, t.Signature); err != nil { - log.Errorf("unable to verify token signature: %s", err) - return ErrInvalidToken - } - - return nil -} - -// VerifySigningKey attempts to get the key which was used to sign this token. -// The token header should contain either of these 3 fields: -// `x5c` - The x509 certificate chain for the signing key. Needs to be -// verified. -// `jwk` - The JSON Web Key representation of the signing key. -// May contain its own `x5c` field which needs to be verified. -// `kid` - The unique identifier for the key. This library interprets it -// as a libtrust fingerprint. The key itself can be looked up in -// the trustedKeys field of the given verify options. -// Each of these methods are tried in that order of preference until the -// signing key is found or an error is returned. -func (t *Token) VerifySigningKey(verifyOpts VerifyOptions) (signingKey libtrust.PublicKey, err error) { - // First attempt to get an x509 certificate chain from the header. - var ( - x5c = t.Header.X5c - rawJWK = t.Header.RawJWK - keyID = t.Header.KeyID - ) - - switch { - case len(x5c) > 0: - signingKey, err = parseAndVerifyCertChain(x5c, verifyOpts.Roots) - case len(rawJWK) > 0: - signingKey, err = parseAndVerifyRawJWK(rawJWK, verifyOpts) - case len(keyID) > 0: - signingKey = verifyOpts.TrustedKeys[keyID] - if signingKey == nil { - err = fmt.Errorf("token signed by untrusted key with ID: %q", keyID) - } - default: - err = errors.New("unable to get token signing key") - } - - return -} - -func parseAndVerifyCertChain(x5c []string, roots *x509.CertPool) (leafKey libtrust.PublicKey, err error) { - if len(x5c) == 0 { - return nil, errors.New("empty x509 certificate chain") - } - - // Ensure the first element is encoded correctly. - leafCertDer, err := base64.StdEncoding.DecodeString(x5c[0]) - if err != nil { - return nil, fmt.Errorf("unable to decode leaf certificate: %s", err) - } - - // And that it is a valid x509 certificate. - leafCert, err := x509.ParseCertificate(leafCertDer) - if err != nil { - return nil, fmt.Errorf("unable to parse leaf certificate: %s", err) - } - - // The rest of the certificate chain are intermediate certificates. - intermediates := x509.NewCertPool() - for i := 1; i < len(x5c); i++ { - intermediateCertDer, err := base64.StdEncoding.DecodeString(x5c[i]) - if err != nil { - return nil, fmt.Errorf("unable to decode intermediate certificate: %s", err) - } - - intermediateCert, err := x509.ParseCertificate(intermediateCertDer) - if err != nil { - return nil, fmt.Errorf("unable to parse intermediate certificate: %s", err) - } - - intermediates.AddCert(intermediateCert) - } - - verifyOpts := x509.VerifyOptions{ - Intermediates: intermediates, - Roots: roots, - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, - } - - // TODO: this call returns certificate chains which we ignore for now, but - // we should check them for revocations if we have the ability later. - if _, err = leafCert.Verify(verifyOpts); err != nil { - return nil, fmt.Errorf("unable to verify certificate chain: %s", err) - } - - // Get the public key from the leaf certificate. - leafCryptoKey, ok := leafCert.PublicKey.(crypto.PublicKey) - if !ok { - return nil, errors.New("unable to get leaf cert public key value") - } - - leafKey, err = libtrust.FromCryptoPublicKey(leafCryptoKey) - if err != nil { - return nil, fmt.Errorf("unable to make libtrust public key from leaf certificate: %s", err) - } - - return -} - -func parseAndVerifyRawJWK(rawJWK json.RawMessage, verifyOpts VerifyOptions) (pubKey libtrust.PublicKey, err error) { - pubKey, err = libtrust.UnmarshalPublicKeyJWK([]byte(rawJWK)) - if err != nil { - return nil, fmt.Errorf("unable to decode raw JWK value: %s", err) - } - - // Check to see if the key includes a certificate chain. - x5cVal, ok := pubKey.GetExtendedField("x5c").([]interface{}) - if !ok { - // The JWK should be one of the trusted root keys. - if _, trusted := verifyOpts.TrustedKeys[pubKey.KeyID()]; !trusted { - return nil, errors.New("untrusted JWK with no certificate chain") - } - - // The JWK is one of the trusted keys. - return - } - - // Ensure each item in the chain is of the correct type. - x5c := make([]string, len(x5cVal)) - for i, val := range x5cVal { - certString, ok := val.(string) - if !ok || len(certString) == 0 { - return nil, errors.New("malformed certificate chain") - } - x5c[i] = certString - } - - // Ensure that the x509 certificate chain can - // be verified up to one of our trusted roots. - leafKey, err := parseAndVerifyCertChain(x5c, verifyOpts.Roots) - if err != nil { - return nil, fmt.Errorf("could not verify JWK certificate chain: %s", err) - } - - // Verify that the public key in the leaf cert *is* the signing key. - if pubKey.KeyID() != leafKey.KeyID() { - return nil, errors.New("leaf certificate public key ID does not match JWK key ID") - } - - return -} - -// accessSet returns a set of actions available for the resource -// actions listed in the `access` section of this token. -func (t *Token) accessSet() accessSet { - if t.Claims == nil { - return nil - } - - accessSet := make(accessSet, len(t.Claims.Access)) - - for _, resourceActions := range t.Claims.Access { - resource := auth.Resource{ - Type: resourceActions.Type, - Name: resourceActions.Name, - } - - set, exists := accessSet[resource] - if !exists { - set = newActionSet() - accessSet[resource] = set - } - - for _, action := range resourceActions.Actions { - set.add(action) - } - } - - return accessSet -} - -func (t *Token) compactRaw() string { - return fmt.Sprintf("%s.%s", t.Raw, joseBase64UrlEncode(t.Signature)) -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/token_test.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/token_test.go deleted file mode 100644 index 119aa738a..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/token_test.go +++ /dev/null @@ -1,386 +0,0 @@ -package token - -import ( - "crypto" - "crypto/rand" - "crypto/x509" - "encoding/base64" - "encoding/json" - "encoding/pem" - "fmt" - "io/ioutil" - "net/http" - "os" - "strings" - "testing" - "time" - - "github.com/docker/distribution/context" - "github.com/docker/distribution/registry/auth" - "github.com/docker/libtrust" -) - -func makeRootKeys(numKeys int) ([]libtrust.PrivateKey, error) { - keys := make([]libtrust.PrivateKey, 0, numKeys) - - for i := 0; i < numKeys; i++ { - key, err := libtrust.GenerateECP256PrivateKey() - if err != nil { - return nil, err - } - keys = append(keys, key) - } - - return keys, nil -} - -func makeSigningKeyWithChain(rootKey libtrust.PrivateKey, depth int) (libtrust.PrivateKey, error) { - if depth == 0 { - // Don't need to build a chain. - return rootKey, nil - } - - var ( - x5c = make([]string, depth) - parentKey = rootKey - key libtrust.PrivateKey - cert *x509.Certificate - err error - ) - - for depth > 0 { - if key, err = libtrust.GenerateECP256PrivateKey(); err != nil { - return nil, err - } - - if cert, err = libtrust.GenerateCACert(parentKey, key); err != nil { - return nil, err - } - - depth-- - x5c[depth] = base64.StdEncoding.EncodeToString(cert.Raw) - parentKey = key - } - - key.AddExtendedField("x5c", x5c) - - return key, nil -} - -func makeRootCerts(rootKeys []libtrust.PrivateKey) ([]*x509.Certificate, error) { - certs := make([]*x509.Certificate, 0, len(rootKeys)) - - for _, key := range rootKeys { - cert, err := libtrust.GenerateCACert(key, key) - if err != nil { - return nil, err - } - certs = append(certs, cert) - } - - return certs, nil -} - -func makeTrustedKeyMap(rootKeys []libtrust.PrivateKey) map[string]libtrust.PublicKey { - trustedKeys := make(map[string]libtrust.PublicKey, len(rootKeys)) - - for _, key := range rootKeys { - trustedKeys[key.KeyID()] = key.PublicKey() - } - - return trustedKeys -} - -func makeTestToken(issuer, audience string, access []*ResourceActions, rootKey libtrust.PrivateKey, depth int) (*Token, error) { - signingKey, err := makeSigningKeyWithChain(rootKey, depth) - if err != nil { - return nil, fmt.Errorf("unable to amke signing key with chain: %s", err) - } - - rawJWK, err := signingKey.PublicKey().MarshalJSON() - if err != nil { - return nil, fmt.Errorf("unable to marshal signing key to JSON: %s", err) - } - - joseHeader := &Header{ - Type: "JWT", - SigningAlg: "ES256", - RawJWK: json.RawMessage(rawJWK), - } - - now := time.Now() - - randomBytes := make([]byte, 15) - if _, err = rand.Read(randomBytes); err != nil { - return nil, fmt.Errorf("unable to read random bytes for jwt id: %s", err) - } - - claimSet := &ClaimSet{ - Issuer: issuer, - Subject: "foo", - Audience: audience, - Expiration: now.Add(5 * time.Minute).Unix(), - NotBefore: now.Unix(), - IssuedAt: now.Unix(), - JWTID: base64.URLEncoding.EncodeToString(randomBytes), - Access: access, - } - - var joseHeaderBytes, claimSetBytes []byte - - if joseHeaderBytes, err = json.Marshal(joseHeader); err != nil { - return nil, fmt.Errorf("unable to marshal jose header: %s", err) - } - if claimSetBytes, err = json.Marshal(claimSet); err != nil { - return nil, fmt.Errorf("unable to marshal claim set: %s", err) - } - - encodedJoseHeader := joseBase64UrlEncode(joseHeaderBytes) - encodedClaimSet := joseBase64UrlEncode(claimSetBytes) - encodingToSign := fmt.Sprintf("%s.%s", encodedJoseHeader, encodedClaimSet) - - var signatureBytes []byte - if signatureBytes, _, err = signingKey.Sign(strings.NewReader(encodingToSign), crypto.SHA256); err != nil { - return nil, fmt.Errorf("unable to sign jwt payload: %s", err) - } - - signature := joseBase64UrlEncode(signatureBytes) - tokenString := fmt.Sprintf("%s.%s", encodingToSign, signature) - - return NewToken(tokenString) -} - -// This test makes 4 tokens with a varying number of intermediate -// certificates ranging from no intermediate chain to a length of 3 -// intermediates. -func TestTokenVerify(t *testing.T) { - var ( - numTokens = 4 - issuer = "test-issuer" - audience = "test-audience" - access = []*ResourceActions{ - { - Type: "repository", - Name: "foo/bar", - Actions: []string{"pull", "push"}, - }, - } - ) - - rootKeys, err := makeRootKeys(numTokens) - if err != nil { - t.Fatal(err) - } - - rootCerts, err := makeRootCerts(rootKeys) - if err != nil { - t.Fatal(err) - } - - rootPool := x509.NewCertPool() - for _, rootCert := range rootCerts { - rootPool.AddCert(rootCert) - } - - trustedKeys := makeTrustedKeyMap(rootKeys) - - tokens := make([]*Token, 0, numTokens) - - for i := 0; i < numTokens; i++ { - token, err := makeTestToken(issuer, audience, access, rootKeys[i], i) - if err != nil { - t.Fatal(err) - } - tokens = append(tokens, token) - } - - verifyOps := VerifyOptions{ - TrustedIssuers: []string{issuer}, - AcceptedAudiences: []string{audience}, - Roots: rootPool, - TrustedKeys: trustedKeys, - } - - for _, token := range tokens { - if err := token.Verify(verifyOps); err != nil { - t.Fatal(err) - } - } -} - -func writeTempRootCerts(rootKeys []libtrust.PrivateKey) (filename string, err error) { - rootCerts, err := makeRootCerts(rootKeys) - if err != nil { - return "", err - } - - tempFile, err := ioutil.TempFile("", "rootCertBundle") - if err != nil { - return "", err - } - defer tempFile.Close() - - for _, cert := range rootCerts { - if err = pem.Encode(tempFile, &pem.Block{ - Type: "CERTIFICATE", - Bytes: cert.Raw, - }); err != nil { - os.Remove(tempFile.Name()) - return "", err - } - } - - return tempFile.Name(), nil -} - -// TestAccessController tests complete integration of the token auth package. -// It starts by mocking the options for a token auth accessController which -// it creates. It then tries a few mock requests: -// - don't supply a token; should error with challenge -// - supply an invalid token; should error with challenge -// - supply a token with insufficient access; should error with challenge -// - supply a valid token; should not error -func TestAccessController(t *testing.T) { - // Make 2 keys; only the first is to be a trusted root key. - rootKeys, err := makeRootKeys(2) - if err != nil { - t.Fatal(err) - } - - rootCertBundleFilename, err := writeTempRootCerts(rootKeys[:1]) - if err != nil { - t.Fatal(err) - } - defer os.Remove(rootCertBundleFilename) - - realm := "https://auth.example.com/token/" - issuer := "test-issuer.example.com" - service := "test-service.example.com" - - options := map[string]interface{}{ - "realm": realm, - "issuer": issuer, - "service": service, - "rootcertbundle": rootCertBundleFilename, - } - - accessController, err := newAccessController(options) - if err != nil { - t.Fatal(err) - } - - // 1. Make a mock http.Request with no token. - req, err := http.NewRequest("GET", "http://example.com/foo", nil) - if err != nil { - t.Fatal(err) - } - - testAccess := auth.Access{ - Resource: auth.Resource{ - Type: "foo", - Name: "bar", - }, - Action: "baz", - } - - ctx := context.WithValue(nil, "http.request", req) - authCtx, err := accessController.Authorized(ctx, testAccess) - challenge, ok := err.(auth.Challenge) - if !ok { - t.Fatal("accessController did not return a challenge") - } - - if challenge.Error() != ErrTokenRequired.Error() { - t.Fatalf("accessControler did not get expected error - got %s - expected %s", challenge, ErrTokenRequired) - } - - if authCtx != nil { - t.Fatalf("expected nil auth context but got %s", authCtx) - } - - // 2. Supply an invalid token. - token, err := makeTestToken( - issuer, service, - []*ResourceActions{{ - Type: testAccess.Type, - Name: testAccess.Name, - Actions: []string{testAccess.Action}, - }}, - rootKeys[1], 1, // Everything is valid except the key which signed it. - ) - if err != nil { - t.Fatal(err) - } - - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.compactRaw())) - - authCtx, err = accessController.Authorized(ctx, testAccess) - challenge, ok = err.(auth.Challenge) - if !ok { - t.Fatal("accessController did not return a challenge") - } - - if challenge.Error() != ErrInvalidToken.Error() { - t.Fatalf("accessControler did not get expected error - got %s - expected %s", challenge, ErrTokenRequired) - } - - if authCtx != nil { - t.Fatalf("expected nil auth context but got %s", authCtx) - } - - // 3. Supply a token with insufficient access. - token, err = makeTestToken( - issuer, service, - []*ResourceActions{}, // No access specified. - rootKeys[0], 1, - ) - if err != nil { - t.Fatal(err) - } - - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.compactRaw())) - - authCtx, err = accessController.Authorized(ctx, testAccess) - challenge, ok = err.(auth.Challenge) - if !ok { - t.Fatal("accessController did not return a challenge") - } - - if challenge.Error() != ErrInsufficientScope.Error() { - t.Fatalf("accessControler did not get expected error - got %s - expected %s", challenge, ErrInsufficientScope) - } - - if authCtx != nil { - t.Fatalf("expected nil auth context but got %s", authCtx) - } - - // 4. Supply the token we need, or deserve, or whatever. - token, err = makeTestToken( - issuer, service, - []*ResourceActions{{ - Type: testAccess.Type, - Name: testAccess.Name, - Actions: []string{testAccess.Action}, - }}, - rootKeys[0], 1, - ) - if err != nil { - t.Fatal(err) - } - - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.compactRaw())) - - authCtx, err = accessController.Authorized(ctx, testAccess) - if err != nil { - t.Fatalf("accessController returned unexpected error: %s", err) - } - - userInfo, ok := authCtx.Value("auth.user").(auth.UserInfo) - if !ok { - t.Fatal("token accessController did not set auth.user context") - } - - if userInfo.Name != "foo" { - t.Fatalf("expected user name %q, got %q", "foo", userInfo.Name) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/util.go b/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/util.go deleted file mode 100644 index d7f95be42..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/registry/auth/token/util.go +++ /dev/null @@ -1,58 +0,0 @@ -package token - -import ( - "encoding/base64" - "errors" - "strings" -) - -// joseBase64UrlEncode encodes the given data using the standard base64 url -// encoding format but with all trailing '=' characters omitted in accordance -// with the jose specification. -// http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-2 -func joseBase64UrlEncode(b []byte) string { - return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") -} - -// joseBase64UrlDecode decodes the given string using the standard base64 url -// decoder but first adds the appropriate number of trailing '=' characters in -// accordance with the jose specification. -// http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-2 -func joseBase64UrlDecode(s string) ([]byte, error) { - switch len(s) % 4 { - case 0: - case 2: - s += "==" - case 3: - s += "=" - default: - return nil, errors.New("illegal base64url string") - } - return base64.URLEncoding.DecodeString(s) -} - -// actionSet is a special type of stringSet. -type actionSet struct { - stringSet -} - -func newActionSet(actions ...string) actionSet { - return actionSet{newStringSet(actions...)} -} - -// Contains calls StringSet.Contains() for -// either "*" or the given action string. -func (s actionSet) contains(action string) bool { - return s.stringSet.contains("*") || s.stringSet.contains(action) -} - -// contains returns true if q is found in ss. -func contains(ss []string, q string) bool { - for _, s := range ss { - if s == q { - return true - } - } - - return false -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/uuid/uuid.go b/Godeps/_workspace/src/github.com/docker/distribution/uuid/uuid.go deleted file mode 100644 index d433ccaf5..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/uuid/uuid.go +++ /dev/null @@ -1,126 +0,0 @@ -// Package uuid provides simple UUID generation. Only version 4 style UUIDs -// can be generated. -// -// Please see http://tools.ietf.org/html/rfc4122 for details on UUIDs. -package uuid - -import ( - "crypto/rand" - "fmt" - "io" - "os" - "syscall" - "time" -) - -const ( - // Bits is the number of bits in a UUID - Bits = 128 - - // Size is the number of bytes in a UUID - Size = Bits / 8 - - format = "%08x-%04x-%04x-%04x-%012x" -) - -var ( - // ErrUUIDInvalid indicates a parsed string is not a valid uuid. - ErrUUIDInvalid = fmt.Errorf("invalid uuid") - - // Loggerf can be used to override the default logging destination. Such - // log messages in this library should be logged at warning or higher. - Loggerf = func(format string, args ...interface{}) {} -) - -// UUID represents a UUID value. UUIDs can be compared and set to other values -// and accessed by byte. -type UUID [Size]byte - -// Generate creates a new, version 4 uuid. -func Generate() (u UUID) { - const ( - // ensures we backoff for less than 450ms total. Use the following to - // select new value, in units of 10ms: - // n*(n+1)/2 = d -> n^2 + n - 2d -> n = (sqrt(8d + 1) - 1)/2 - maxretries = 9 - backoff = time.Millisecond * 10 - ) - - var ( - totalBackoff time.Duration - count int - retries int - ) - - for { - // This should never block but the read may fail. Because of this, - // we just try to read the random number generator until we get - // something. This is a very rare condition but may happen. - b := time.Duration(retries) * backoff - time.Sleep(b) - totalBackoff += b - - n, err := io.ReadFull(rand.Reader, u[count:]) - if err != nil { - if retryOnError(err) && retries < maxretries { - count += n - retries++ - Loggerf("error generating version 4 uuid, retrying: %v", err) - continue - } - - // Any other errors represent a system problem. What did someone - // do to /dev/urandom? - panic(fmt.Errorf("error reading random number generator, retried for %v: %v", totalBackoff.String(), err)) - } - - break - } - - u[6] = (u[6] & 0x0f) | 0x40 // set version byte - u[8] = (u[8] & 0x3f) | 0x80 // set high order byte 0b10{8,9,a,b} - - return u -} - -// Parse attempts to extract a uuid from the string or returns an error. -func Parse(s string) (u UUID, err error) { - if len(s) != 36 { - return UUID{}, ErrUUIDInvalid - } - - // create stack addresses for each section of the uuid. - p := make([][]byte, 5) - - if _, err := fmt.Sscanf(s, format, &p[0], &p[1], &p[2], &p[3], &p[4]); err != nil { - return u, err - } - - copy(u[0:4], p[0]) - copy(u[4:6], p[1]) - copy(u[6:8], p[2]) - copy(u[8:10], p[3]) - copy(u[10:16], p[4]) - - return -} - -func (u UUID) String() string { - return fmt.Sprintf(format, u[:4], u[4:6], u[6:8], u[8:10], u[10:]) -} - -// retryOnError tries to detect whether or not retrying would be fruitful. -func retryOnError(err error) bool { - switch err := err.(type) { - case *os.PathError: - return retryOnError(err.Err) // unpack the target error - case syscall.Errno: - if err == syscall.EPERM { - // EPERM represents an entropy pool exhaustion, a condition under - // which we backoff and retry. - return true - } - } - - return false -} diff --git a/Godeps/_workspace/src/github.com/docker/distribution/uuid/uuid_test.go b/Godeps/_workspace/src/github.com/docker/distribution/uuid/uuid_test.go deleted file mode 100644 index 09c3a7bb4..000000000 --- a/Godeps/_workspace/src/github.com/docker/distribution/uuid/uuid_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package uuid - -import ( - "testing" -) - -const iterations = 1000 - -func TestUUID4Generation(t *testing.T) { - for i := 0; i < iterations; i++ { - u := Generate() - - if u[6]&0xf0 != 0x40 { - t.Fatalf("version byte not correctly set: %v, %08b %08b", u, u[6], u[6]&0xf0) - } - - if u[8]&0xc0 != 0x80 { - t.Fatalf("top order 8th byte not correctly set: %v, %b", u, u[8]) - } - } -} - -func TestParseAndEquality(t *testing.T) { - for i := 0; i < iterations; i++ { - u := Generate() - - parsed, err := Parse(u.String()) - if err != nil { - t.Fatalf("error parsing uuid %v: %v", u, err) - } - - if parsed != u { - t.Fatalf("parsing round trip failed: %v != %v", parsed, u) - } - } - - for _, c := range []string{ - "bad", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // correct length, incorrect format - " 20cc7775-2671-43c7-8742-51d1cfa23258", // leading space - "20cc7775-2671-43c7-8742-51d1cfa23258 ", // trailing space - "00000000-0000-0000-0000-x00000000000", // out of range character - } { - if _, err := Parse(c); err == nil { - t.Fatalf("parsing %q should have failed", c) - } - } -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/CONTRIBUTING.md b/Godeps/_workspace/src/github.com/docker/libtrust/CONTRIBUTING.md deleted file mode 100644 index 05be0f8ab..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/CONTRIBUTING.md +++ /dev/null @@ -1,13 +0,0 @@ -# Contributing to libtrust - -Want to hack on libtrust? Awesome! Here are instructions to get you -started. - -libtrust is a part of the [Docker](https://www.docker.com) project, and follows -the same rules and principles. If you're already familiar with the way -Docker does things, you'll feel right at home. - -Otherwise, go read -[Docker's contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md). - -Happy hacking! diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/LICENSE b/Godeps/_workspace/src/github.com/docker/libtrust/LICENSE deleted file mode 100644 index 27448585a..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - 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 - - Copyright 2014 Docker, Inc. - - 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/Godeps/_workspace/src/github.com/docker/libtrust/MAINTAINERS b/Godeps/_workspace/src/github.com/docker/libtrust/MAINTAINERS deleted file mode 100644 index 9768175fe..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/MAINTAINERS +++ /dev/null @@ -1,3 +0,0 @@ -Solomon Hykes -Josh Hawn (github: jlhawn) -Derek McGowan (github: dmcgowan) diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/README.md b/Godeps/_workspace/src/github.com/docker/libtrust/README.md deleted file mode 100644 index 8e7db3818..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# libtrust - -Libtrust is library for managing authentication and authorization using public key cryptography. - -Authentication is handled using the identity attached to the public key. -Libtrust provides multiple methods to prove possession of the private key associated with an identity. - - TLS x509 certificates - - Signature verification - - Key Challenge - -Authorization and access control is managed through a distributed trust graph. -Trust servers are used as the authorities of the trust graph and allow caching portions of the graph for faster access. - -## Copyright and license - -Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license. -Docs released under Creative commons. - diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/certificates.go b/Godeps/_workspace/src/github.com/docker/libtrust/certificates.go deleted file mode 100644 index 3dcca33cb..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/certificates.go +++ /dev/null @@ -1,175 +0,0 @@ -package libtrust - -import ( - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "fmt" - "io/ioutil" - "math/big" - "net" - "time" -) - -type certTemplateInfo struct { - commonName string - domains []string - ipAddresses []net.IP - isCA bool - clientAuth bool - serverAuth bool -} - -func generateCertTemplate(info *certTemplateInfo) *x509.Certificate { - // Generate a certificate template which is valid from the past week to - // 10 years from now. The usage of the certificate depends on the - // specified fields in the given certTempInfo object. - var ( - keyUsage x509.KeyUsage - extKeyUsage []x509.ExtKeyUsage - ) - - if info.isCA { - keyUsage = x509.KeyUsageCertSign - } - - if info.clientAuth { - extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageClientAuth) - } - - if info.serverAuth { - extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageServerAuth) - } - - return &x509.Certificate{ - SerialNumber: big.NewInt(0), - Subject: pkix.Name{ - CommonName: info.commonName, - }, - NotBefore: time.Now().Add(-time.Hour * 24 * 7), - NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10), - DNSNames: info.domains, - IPAddresses: info.ipAddresses, - IsCA: info.isCA, - KeyUsage: keyUsage, - ExtKeyUsage: extKeyUsage, - BasicConstraintsValid: info.isCA, - } -} - -func generateCert(pub PublicKey, priv PrivateKey, subInfo, issInfo *certTemplateInfo) (cert *x509.Certificate, err error) { - pubCertTemplate := generateCertTemplate(subInfo) - privCertTemplate := generateCertTemplate(issInfo) - - certDER, err := x509.CreateCertificate( - rand.Reader, pubCertTemplate, privCertTemplate, - pub.CryptoPublicKey(), priv.CryptoPrivateKey(), - ) - if err != nil { - return nil, fmt.Errorf("failed to create certificate: %s", err) - } - - cert, err = x509.ParseCertificate(certDER) - if err != nil { - return nil, fmt.Errorf("failed to parse certificate: %s", err) - } - - return -} - -// GenerateSelfSignedServerCert creates a self-signed certificate for the -// given key which is to be used for TLS servers with the given domains and -// IP addresses. -func GenerateSelfSignedServerCert(key PrivateKey, domains []string, ipAddresses []net.IP) (*x509.Certificate, error) { - info := &certTemplateInfo{ - commonName: key.KeyID(), - domains: domains, - ipAddresses: ipAddresses, - serverAuth: true, - } - - return generateCert(key.PublicKey(), key, info, info) -} - -// GenerateSelfSignedClientCert creates a self-signed certificate for the -// given key which is to be used for TLS clients. -func GenerateSelfSignedClientCert(key PrivateKey) (*x509.Certificate, error) { - info := &certTemplateInfo{ - commonName: key.KeyID(), - clientAuth: true, - } - - return generateCert(key.PublicKey(), key, info, info) -} - -// GenerateCACert creates a certificate which can be used as a trusted -// certificate authority. -func GenerateCACert(signer PrivateKey, trustedKey PublicKey) (*x509.Certificate, error) { - subjectInfo := &certTemplateInfo{ - commonName: trustedKey.KeyID(), - isCA: true, - } - issuerInfo := &certTemplateInfo{ - commonName: signer.KeyID(), - } - - return generateCert(trustedKey, signer, subjectInfo, issuerInfo) -} - -// GenerateCACertPool creates a certificate authority pool to be used for a -// TLS configuration. Any self-signed certificates issued by the specified -// trusted keys will be verified during a TLS handshake -func GenerateCACertPool(signer PrivateKey, trustedKeys []PublicKey) (*x509.CertPool, error) { - certPool := x509.NewCertPool() - - for _, trustedKey := range trustedKeys { - cert, err := GenerateCACert(signer, trustedKey) - if err != nil { - return nil, fmt.Errorf("failed to generate CA certificate: %s", err) - } - - certPool.AddCert(cert) - } - - return certPool, nil -} - -// LoadCertificateBundle loads certificates from the given file. The file should be pem encoded -// containing one or more certificates. The expected pem type is "CERTIFICATE". -func LoadCertificateBundle(filename string) ([]*x509.Certificate, error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - certificates := []*x509.Certificate{} - var block *pem.Block - block, b = pem.Decode(b) - for ; block != nil; block, b = pem.Decode(b) { - if block.Type == "CERTIFICATE" { - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, err - } - certificates = append(certificates, cert) - } else { - return nil, fmt.Errorf("invalid pem block type: %s", block.Type) - } - } - - return certificates, nil -} - -// LoadCertificatePool loads a CA pool from the given file. The file should be pem encoded -// containing one or more certificates. The expected pem type is "CERTIFICATE". -func LoadCertificatePool(filename string) (*x509.CertPool, error) { - certs, err := LoadCertificateBundle(filename) - if err != nil { - return nil, err - } - pool := x509.NewCertPool() - for _, cert := range certs { - pool.AddCert(cert) - } - return pool, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/certificates_test.go b/Godeps/_workspace/src/github.com/docker/libtrust/certificates_test.go deleted file mode 100644 index c111f3531..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/certificates_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package libtrust - -import ( - "encoding/pem" - "io/ioutil" - "net" - "os" - "path" - "testing" -) - -func TestGenerateCertificates(t *testing.T) { - key, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - _, err = GenerateSelfSignedServerCert(key, []string{"localhost"}, []net.IP{net.ParseIP("127.0.0.1")}) - if err != nil { - t.Fatal(err) - } - - _, err = GenerateSelfSignedClientCert(key) - if err != nil { - t.Fatal(err) - } -} - -func TestGenerateCACertPool(t *testing.T) { - key, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - caKey1, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - caKey2, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - _, err = GenerateCACertPool(key, []PublicKey{caKey1.PublicKey(), caKey2.PublicKey()}) - if err != nil { - t.Fatal(err) - } -} - -func TestLoadCertificates(t *testing.T) { - key, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - caKey1, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - caKey2, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - cert1, err := GenerateCACert(caKey1, key) - if err != nil { - t.Fatal(err) - } - cert2, err := GenerateCACert(caKey2, key) - if err != nil { - t.Fatal(err) - } - - d, err := ioutil.TempDir("/tmp", "cert-test") - if err != nil { - t.Fatal(err) - } - caFile := path.Join(d, "ca.pem") - f, err := os.OpenFile(caFile, os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - t.Fatal(err) - } - - err = pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert1.Raw}) - if err != nil { - t.Fatal(err) - } - err = pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert2.Raw}) - if err != nil { - t.Fatal(err) - } - f.Close() - - certs, err := LoadCertificateBundle(caFile) - if err != nil { - t.Fatal(err) - } - if len(certs) != 2 { - t.Fatalf("Wrong number of certs received, expected: %d, received %d", 2, len(certs)) - } - - pool, err := LoadCertificatePool(caFile) - if err != nil { - t.Fatal(err) - } - - if len(pool.Subjects()) != 2 { - t.Fatalf("Invalid certificate pool") - } -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/doc.go b/Godeps/_workspace/src/github.com/docker/libtrust/doc.go deleted file mode 100644 index ec5d2159c..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/doc.go +++ /dev/null @@ -1,9 +0,0 @@ -/* -Package libtrust provides an interface for managing authentication and -authorization using public key cryptography. Authentication is handled -using the identity attached to the public key and verified through TLS -x509 certificates, a key challenge, or signature. Authorization and -access control is managed through a trust graph distributed between -both remote trust servers and locally cached and managed data. -*/ -package libtrust diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/ec_key.go b/Godeps/_workspace/src/github.com/docker/libtrust/ec_key.go deleted file mode 100644 index 00bbe4b3c..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/ec_key.go +++ /dev/null @@ -1,428 +0,0 @@ -package libtrust - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/x509" - "encoding/json" - "encoding/pem" - "errors" - "fmt" - "io" - "math/big" -) - -/* - * EC DSA PUBLIC KEY - */ - -// ecPublicKey implements a libtrust.PublicKey using elliptic curve digital -// signature algorithms. -type ecPublicKey struct { - *ecdsa.PublicKey - curveName string - signatureAlgorithm *signatureAlgorithm - extended map[string]interface{} -} - -func fromECPublicKey(cryptoPublicKey *ecdsa.PublicKey) (*ecPublicKey, error) { - curve := cryptoPublicKey.Curve - - switch { - case curve == elliptic.P256(): - return &ecPublicKey{cryptoPublicKey, "P-256", es256, map[string]interface{}{}}, nil - case curve == elliptic.P384(): - return &ecPublicKey{cryptoPublicKey, "P-384", es384, map[string]interface{}{}}, nil - case curve == elliptic.P521(): - return &ecPublicKey{cryptoPublicKey, "P-521", es512, map[string]interface{}{}}, nil - default: - return nil, errors.New("unsupported elliptic curve") - } -} - -// KeyType returns the key type for elliptic curve keys, i.e., "EC". -func (k *ecPublicKey) KeyType() string { - return "EC" -} - -// CurveName returns the elliptic curve identifier. -// Possible values are "P-256", "P-384", and "P-521". -func (k *ecPublicKey) CurveName() string { - return k.curveName -} - -// KeyID returns a distinct identifier which is unique to this Public Key. -func (k *ecPublicKey) KeyID() string { - return keyIDFromCryptoKey(k) -} - -func (k *ecPublicKey) String() string { - return fmt.Sprintf("EC Public Key <%s>", k.KeyID()) -} - -// Verify verifyies the signature of the data in the io.Reader using this -// PublicKey. The alg parameter should identify the digital signature -// algorithm which was used to produce the signature and should be supported -// by this public key. Returns a nil error if the signature is valid. -func (k *ecPublicKey) Verify(data io.Reader, alg string, signature []byte) error { - // For EC keys there is only one supported signature algorithm depending - // on the curve parameters. - if k.signatureAlgorithm.HeaderParam() != alg { - return fmt.Errorf("unable to verify signature: EC Public Key with curve %q does not support signature algorithm %q", k.curveName, alg) - } - - // signature is the concatenation of (r, s), base64Url encoded. - sigLength := len(signature) - expectedOctetLength := 2 * ((k.Params().BitSize + 7) >> 3) - if sigLength != expectedOctetLength { - return fmt.Errorf("signature length is %d octets long, should be %d", sigLength, expectedOctetLength) - } - - rBytes, sBytes := signature[:sigLength/2], signature[sigLength/2:] - r := new(big.Int).SetBytes(rBytes) - s := new(big.Int).SetBytes(sBytes) - - hasher := k.signatureAlgorithm.HashID().New() - _, err := io.Copy(hasher, data) - if err != nil { - return fmt.Errorf("error reading data to sign: %s", err) - } - hash := hasher.Sum(nil) - - if !ecdsa.Verify(k.PublicKey, hash, r, s) { - return errors.New("invalid signature") - } - - return nil -} - -// CryptoPublicKey returns the internal object which can be used as a -// crypto.PublicKey for use with other standard library operations. The type -// is either *rsa.PublicKey or *ecdsa.PublicKey -func (k *ecPublicKey) CryptoPublicKey() crypto.PublicKey { - return k.PublicKey -} - -func (k *ecPublicKey) toMap() map[string]interface{} { - jwk := make(map[string]interface{}) - for k, v := range k.extended { - jwk[k] = v - } - jwk["kty"] = k.KeyType() - jwk["kid"] = k.KeyID() - jwk["crv"] = k.CurveName() - - xBytes := k.X.Bytes() - yBytes := k.Y.Bytes() - octetLength := (k.Params().BitSize + 7) >> 3 - // MUST include leading zeros in the output so that x, y are each - // *octetLength* bytes long. - xBuf := make([]byte, octetLength-len(xBytes), octetLength) - yBuf := make([]byte, octetLength-len(yBytes), octetLength) - xBuf = append(xBuf, xBytes...) - yBuf = append(yBuf, yBytes...) - - jwk["x"] = joseBase64UrlEncode(xBuf) - jwk["y"] = joseBase64UrlEncode(yBuf) - - return jwk -} - -// MarshalJSON serializes this Public Key using the JWK JSON serialization format for -// elliptic curve keys. -func (k *ecPublicKey) MarshalJSON() (data []byte, err error) { - return json.Marshal(k.toMap()) -} - -// PEMBlock serializes this Public Key to DER-encoded PKIX format. -func (k *ecPublicKey) PEMBlock() (*pem.Block, error) { - derBytes, err := x509.MarshalPKIXPublicKey(k.PublicKey) - if err != nil { - return nil, fmt.Errorf("unable to serialize EC PublicKey to DER-encoded PKIX format: %s", err) - } - k.extended["kid"] = k.KeyID() // For display purposes. - return createPemBlock("PUBLIC KEY", derBytes, k.extended) -} - -func (k *ecPublicKey) AddExtendedField(field string, value interface{}) { - k.extended[field] = value -} - -func (k *ecPublicKey) GetExtendedField(field string) interface{} { - v, ok := k.extended[field] - if !ok { - return nil - } - return v -} - -func ecPublicKeyFromMap(jwk map[string]interface{}) (*ecPublicKey, error) { - // JWK key type (kty) has already been determined to be "EC". - // Need to extract 'crv', 'x', 'y', and 'kid' and check for - // consistency. - - // Get the curve identifier value. - crv, err := stringFromMap(jwk, "crv") - if err != nil { - return nil, fmt.Errorf("JWK EC Public Key curve identifier: %s", err) - } - - var ( - curve elliptic.Curve - sigAlg *signatureAlgorithm - ) - - switch { - case crv == "P-256": - curve = elliptic.P256() - sigAlg = es256 - case crv == "P-384": - curve = elliptic.P384() - sigAlg = es384 - case crv == "P-521": - curve = elliptic.P521() - sigAlg = es512 - default: - return nil, fmt.Errorf("JWK EC Public Key curve identifier not supported: %q\n", crv) - } - - // Get the X and Y coordinates for the public key point. - xB64Url, err := stringFromMap(jwk, "x") - if err != nil { - return nil, fmt.Errorf("JWK EC Public Key x-coordinate: %s", err) - } - x, err := parseECCoordinate(xB64Url, curve) - if err != nil { - return nil, fmt.Errorf("JWK EC Public Key x-coordinate: %s", err) - } - - yB64Url, err := stringFromMap(jwk, "y") - if err != nil { - return nil, fmt.Errorf("JWK EC Public Key y-coordinate: %s", err) - } - y, err := parseECCoordinate(yB64Url, curve) - if err != nil { - return nil, fmt.Errorf("JWK EC Public Key y-coordinate: %s", err) - } - - key := &ecPublicKey{ - PublicKey: &ecdsa.PublicKey{Curve: curve, X: x, Y: y}, - curveName: crv, signatureAlgorithm: sigAlg, - } - - // Key ID is optional too, but if it exists, it should match the key. - _, ok := jwk["kid"] - if ok { - kid, err := stringFromMap(jwk, "kid") - if err != nil { - return nil, fmt.Errorf("JWK EC Public Key ID: %s", err) - } - if kid != key.KeyID() { - return nil, fmt.Errorf("JWK EC Public Key ID does not match: %s", kid) - } - } - - key.extended = jwk - - return key, nil -} - -/* - * EC DSA PRIVATE KEY - */ - -// ecPrivateKey implements a JWK Private Key using elliptic curve digital signature -// algorithms. -type ecPrivateKey struct { - ecPublicKey - *ecdsa.PrivateKey -} - -func fromECPrivateKey(cryptoPrivateKey *ecdsa.PrivateKey) (*ecPrivateKey, error) { - publicKey, err := fromECPublicKey(&cryptoPrivateKey.PublicKey) - if err != nil { - return nil, err - } - - return &ecPrivateKey{*publicKey, cryptoPrivateKey}, nil -} - -// PublicKey returns the Public Key data associated with this Private Key. -func (k *ecPrivateKey) PublicKey() PublicKey { - return &k.ecPublicKey -} - -func (k *ecPrivateKey) String() string { - return fmt.Sprintf("EC Private Key <%s>", k.KeyID()) -} - -// Sign signs the data read from the io.Reader using a signature algorithm supported -// by the elliptic curve private key. If the specified hashing algorithm is -// supported by this key, that hash function is used to generate the signature -// otherwise the the default hashing algorithm for this key is used. Returns -// the signature and the name of the JWK signature algorithm used, e.g., -// "ES256", "ES384", "ES512". -func (k *ecPrivateKey) Sign(data io.Reader, hashID crypto.Hash) (signature []byte, alg string, err error) { - // Generate a signature of the data using the internal alg. - // The given hashId is only a suggestion, and since EC keys only support - // on signature/hash algorithm given the curve name, we disregard it for - // the elliptic curve JWK signature implementation. - hasher := k.signatureAlgorithm.HashID().New() - _, err = io.Copy(hasher, data) - if err != nil { - return nil, "", fmt.Errorf("error reading data to sign: %s", err) - } - hash := hasher.Sum(nil) - - r, s, err := ecdsa.Sign(rand.Reader, k.PrivateKey, hash) - if err != nil { - return nil, "", fmt.Errorf("error producing signature: %s", err) - } - rBytes, sBytes := r.Bytes(), s.Bytes() - octetLength := (k.ecPublicKey.Params().BitSize + 7) >> 3 - // MUST include leading zeros in the output - rBuf := make([]byte, octetLength-len(rBytes), octetLength) - sBuf := make([]byte, octetLength-len(sBytes), octetLength) - - rBuf = append(rBuf, rBytes...) - sBuf = append(sBuf, sBytes...) - - signature = append(rBuf, sBuf...) - alg = k.signatureAlgorithm.HeaderParam() - - return -} - -// CryptoPrivateKey returns the internal object which can be used as a -// crypto.PublicKey for use with other standard library operations. The type -// is either *rsa.PublicKey or *ecdsa.PublicKey -func (k *ecPrivateKey) CryptoPrivateKey() crypto.PrivateKey { - return k.PrivateKey -} - -func (k *ecPrivateKey) toMap() map[string]interface{} { - jwk := k.ecPublicKey.toMap() - - dBytes := k.D.Bytes() - // The length of this octet string MUST be ceiling(log-base-2(n)/8) - // octets (where n is the order of the curve). This is because the private - // key d must be in the interval [1, n-1] so the bitlength of d should be - // no larger than the bitlength of n-1. The easiest way to find the octet - // length is to take bitlength(n-1), add 7 to force a carry, and shift this - // bit sequence right by 3, which is essentially dividing by 8 and adding - // 1 if there is any remainder. Thus, the private key value d should be - // output to (bitlength(n-1)+7)>>3 octets. - n := k.ecPublicKey.Params().N - octetLength := (new(big.Int).Sub(n, big.NewInt(1)).BitLen() + 7) >> 3 - // Create a buffer with the necessary zero-padding. - dBuf := make([]byte, octetLength-len(dBytes), octetLength) - dBuf = append(dBuf, dBytes...) - - jwk["d"] = joseBase64UrlEncode(dBuf) - - return jwk -} - -// MarshalJSON serializes this Private Key using the JWK JSON serialization format for -// elliptic curve keys. -func (k *ecPrivateKey) MarshalJSON() (data []byte, err error) { - return json.Marshal(k.toMap()) -} - -// PEMBlock serializes this Private Key to DER-encoded PKIX format. -func (k *ecPrivateKey) PEMBlock() (*pem.Block, error) { - derBytes, err := x509.MarshalECPrivateKey(k.PrivateKey) - if err != nil { - return nil, fmt.Errorf("unable to serialize EC PrivateKey to DER-encoded PKIX format: %s", err) - } - k.extended["keyID"] = k.KeyID() // For display purposes. - return createPemBlock("EC PRIVATE KEY", derBytes, k.extended) -} - -func ecPrivateKeyFromMap(jwk map[string]interface{}) (*ecPrivateKey, error) { - dB64Url, err := stringFromMap(jwk, "d") - if err != nil { - return nil, fmt.Errorf("JWK EC Private Key: %s", err) - } - - // JWK key type (kty) has already been determined to be "EC". - // Need to extract the public key information, then extract the private - // key value 'd'. - publicKey, err := ecPublicKeyFromMap(jwk) - if err != nil { - return nil, err - } - - d, err := parseECPrivateParam(dB64Url, publicKey.Curve) - if err != nil { - return nil, fmt.Errorf("JWK EC Private Key d-param: %s", err) - } - - key := &ecPrivateKey{ - ecPublicKey: *publicKey, - PrivateKey: &ecdsa.PrivateKey{ - PublicKey: *publicKey.PublicKey, - D: d, - }, - } - - return key, nil -} - -/* - * Key Generation Functions. - */ - -func generateECPrivateKey(curve elliptic.Curve) (k *ecPrivateKey, err error) { - k = new(ecPrivateKey) - k.PrivateKey, err = ecdsa.GenerateKey(curve, rand.Reader) - if err != nil { - return nil, err - } - - k.ecPublicKey.PublicKey = &k.PrivateKey.PublicKey - k.extended = make(map[string]interface{}) - - return -} - -// GenerateECP256PrivateKey generates a key pair using elliptic curve P-256. -func GenerateECP256PrivateKey() (PrivateKey, error) { - k, err := generateECPrivateKey(elliptic.P256()) - if err != nil { - return nil, fmt.Errorf("error generating EC P-256 key: %s", err) - } - - k.curveName = "P-256" - k.signatureAlgorithm = es256 - - return k, nil -} - -// GenerateECP384PrivateKey generates a key pair using elliptic curve P-384. -func GenerateECP384PrivateKey() (PrivateKey, error) { - k, err := generateECPrivateKey(elliptic.P384()) - if err != nil { - return nil, fmt.Errorf("error generating EC P-384 key: %s", err) - } - - k.curveName = "P-384" - k.signatureAlgorithm = es384 - - return k, nil -} - -// GenerateECP521PrivateKey generates aß key pair using elliptic curve P-521. -func GenerateECP521PrivateKey() (PrivateKey, error) { - k, err := generateECPrivateKey(elliptic.P521()) - if err != nil { - return nil, fmt.Errorf("error generating EC P-521 key: %s", err) - } - - k.curveName = "P-521" - k.signatureAlgorithm = es512 - - return k, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/ec_key_test.go b/Godeps/_workspace/src/github.com/docker/libtrust/ec_key_test.go deleted file mode 100644 index 26ac38149..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/ec_key_test.go +++ /dev/null @@ -1,157 +0,0 @@ -package libtrust - -import ( - "bytes" - "encoding/json" - "testing" -) - -func generateECTestKeys(t *testing.T) []PrivateKey { - p256Key, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - p384Key, err := GenerateECP384PrivateKey() - if err != nil { - t.Fatal(err) - } - - p521Key, err := GenerateECP521PrivateKey() - if err != nil { - t.Fatal(err) - } - - return []PrivateKey{p256Key, p384Key, p521Key} -} - -func TestECKeys(t *testing.T) { - ecKeys := generateECTestKeys(t) - - for _, ecKey := range ecKeys { - if ecKey.KeyType() != "EC" { - t.Fatalf("key type must be %q, instead got %q", "EC", ecKey.KeyType()) - } - } -} - -func TestECSignVerify(t *testing.T) { - ecKeys := generateECTestKeys(t) - - message := "Hello, World!" - data := bytes.NewReader([]byte(message)) - - sigAlgs := []*signatureAlgorithm{es256, es384, es512} - - for i, ecKey := range ecKeys { - sigAlg := sigAlgs[i] - - t.Logf("%s signature of %q with kid: %s\n", sigAlg.HeaderParam(), message, ecKey.KeyID()) - - data.Seek(0, 0) // Reset the byte reader - - // Sign - sig, alg, err := ecKey.Sign(data, sigAlg.HashID()) - if err != nil { - t.Fatal(err) - } - - data.Seek(0, 0) // Reset the byte reader - - // Verify - err = ecKey.Verify(data, alg, sig) - if err != nil { - t.Fatal(err) - } - } -} - -func TestMarshalUnmarshalECKeys(t *testing.T) { - ecKeys := generateECTestKeys(t) - data := bytes.NewReader([]byte("This is a test. I repeat: this is only a test.")) - sigAlgs := []*signatureAlgorithm{es256, es384, es512} - - for i, ecKey := range ecKeys { - sigAlg := sigAlgs[i] - privateJWKJSON, err := json.MarshalIndent(ecKey, "", " ") - if err != nil { - t.Fatal(err) - } - - publicJWKJSON, err := json.MarshalIndent(ecKey.PublicKey(), "", " ") - if err != nil { - t.Fatal(err) - } - - t.Logf("JWK Private Key: %s", string(privateJWKJSON)) - t.Logf("JWK Public Key: %s", string(publicJWKJSON)) - - privKey2, err := UnmarshalPrivateKeyJWK(privateJWKJSON) - if err != nil { - t.Fatal(err) - } - - pubKey2, err := UnmarshalPublicKeyJWK(publicJWKJSON) - if err != nil { - t.Fatal(err) - } - - // Ensure we can sign/verify a message with the unmarshalled keys. - data.Seek(0, 0) // Reset the byte reader - signature, alg, err := privKey2.Sign(data, sigAlg.HashID()) - if err != nil { - t.Fatal(err) - } - - data.Seek(0, 0) // Reset the byte reader - err = pubKey2.Verify(data, alg, signature) - if err != nil { - t.Fatal(err) - } - } -} - -func TestFromCryptoECKeys(t *testing.T) { - ecKeys := generateECTestKeys(t) - - for _, ecKey := range ecKeys { - cryptoPrivateKey := ecKey.CryptoPrivateKey() - cryptoPublicKey := ecKey.CryptoPublicKey() - - pubKey, err := FromCryptoPublicKey(cryptoPublicKey) - if err != nil { - t.Fatal(err) - } - - if pubKey.KeyID() != ecKey.KeyID() { - t.Fatal("public key key ID mismatch") - } - - privKey, err := FromCryptoPrivateKey(cryptoPrivateKey) - if err != nil { - t.Fatal(err) - } - - if privKey.KeyID() != ecKey.KeyID() { - t.Fatal("public key key ID mismatch") - } - } -} - -func TestExtendedFields(t *testing.T) { - key, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - key.AddExtendedField("test", "foobar") - val := key.GetExtendedField("test") - - gotVal, ok := val.(string) - if !ok { - t.Fatalf("value is not a string") - } else if gotVal != val { - t.Fatalf("value %q is not equal to %q", gotVal, val) - } - -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/filter.go b/Godeps/_workspace/src/github.com/docker/libtrust/filter.go deleted file mode 100644 index 5b2b4fca6..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/filter.go +++ /dev/null @@ -1,50 +0,0 @@ -package libtrust - -import ( - "path/filepath" -) - -// FilterByHosts filters the list of PublicKeys to only those which contain a -// 'hosts' pattern which matches the given host. If *includeEmpty* is true, -// then keys which do not specify any hosts are also returned. -func FilterByHosts(keys []PublicKey, host string, includeEmpty bool) ([]PublicKey, error) { - filtered := make([]PublicKey, 0, len(keys)) - - for _, pubKey := range keys { - var hosts []string - switch v := pubKey.GetExtendedField("hosts").(type) { - case []string: - hosts = v - case []interface{}: - for _, value := range v { - h, ok := value.(string) - if !ok { - continue - } - hosts = append(hosts, h) - } - } - - if len(hosts) == 0 { - if includeEmpty { - filtered = append(filtered, pubKey) - } - continue - } - - // Check if any hosts match pattern - for _, hostPattern := range hosts { - match, err := filepath.Match(hostPattern, host) - if err != nil { - return nil, err - } - - if match { - filtered = append(filtered, pubKey) - continue - } - } - } - - return filtered, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/filter_test.go b/Godeps/_workspace/src/github.com/docker/libtrust/filter_test.go deleted file mode 100644 index 997e554c0..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/filter_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package libtrust - -import ( - "testing" -) - -func compareKeySlices(t *testing.T, sliceA, sliceB []PublicKey) { - if len(sliceA) != len(sliceB) { - t.Fatalf("slice size %d, expected %d", len(sliceA), len(sliceB)) - } - - for i, itemA := range sliceA { - itemB := sliceB[i] - if itemA != itemB { - t.Fatalf("slice index %d not equal: %#v != %#v", i, itemA, itemB) - } - } -} - -func TestFilter(t *testing.T) { - keys := make([]PublicKey, 0, 8) - - // Create 8 keys and add host entries. - for i := 0; i < cap(keys); i++ { - key, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - // we use both []interface{} and []string here because jwt uses - // []interface{} format, while PEM uses []string - switch { - case i == 0: - // Don't add entries for this key, key 0. - break - case i%2 == 0: - // Should catch keys 2, 4, and 6. - key.AddExtendedField("hosts", []interface{}{"*.even.example.com"}) - case i == 7: - // Should catch only the last key, and make it match any hostname. - key.AddExtendedField("hosts", []string{"*"}) - default: - // should catch keys 1, 3, 5. - key.AddExtendedField("hosts", []string{"*.example.com"}) - } - - keys = append(keys, key) - } - - // Should match 2 keys, the empty one, and the one that matches all hosts. - matchedKeys, err := FilterByHosts(keys, "foo.bar.com", true) - if err != nil { - t.Fatal(err) - } - expectedMatch := []PublicKey{keys[0], keys[7]} - compareKeySlices(t, expectedMatch, matchedKeys) - - // Should match 1 key, the one that matches any host. - matchedKeys, err = FilterByHosts(keys, "foo.bar.com", false) - if err != nil { - t.Fatal(err) - } - expectedMatch = []PublicKey{keys[7]} - compareKeySlices(t, expectedMatch, matchedKeys) - - // Should match keys that end in "example.com", and the key that matches anything. - matchedKeys, err = FilterByHosts(keys, "foo.example.com", false) - if err != nil { - t.Fatal(err) - } - expectedMatch = []PublicKey{keys[1], keys[3], keys[5], keys[7]} - compareKeySlices(t, expectedMatch, matchedKeys) - - // Should match all of the keys except the empty key. - matchedKeys, err = FilterByHosts(keys, "foo.even.example.com", false) - if err != nil { - t.Fatal(err) - } - expectedMatch = keys[1:] - compareKeySlices(t, expectedMatch, matchedKeys) -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/hash.go b/Godeps/_workspace/src/github.com/docker/libtrust/hash.go deleted file mode 100644 index a2df787dd..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/hash.go +++ /dev/null @@ -1,56 +0,0 @@ -package libtrust - -import ( - "crypto" - _ "crypto/sha256" // Registrer SHA224 and SHA256 - _ "crypto/sha512" // Registrer SHA384 and SHA512 - "fmt" -) - -type signatureAlgorithm struct { - algHeaderParam string - hashID crypto.Hash -} - -func (h *signatureAlgorithm) HeaderParam() string { - return h.algHeaderParam -} - -func (h *signatureAlgorithm) HashID() crypto.Hash { - return h.hashID -} - -var ( - rs256 = &signatureAlgorithm{"RS256", crypto.SHA256} - rs384 = &signatureAlgorithm{"RS384", crypto.SHA384} - rs512 = &signatureAlgorithm{"RS512", crypto.SHA512} - es256 = &signatureAlgorithm{"ES256", crypto.SHA256} - es384 = &signatureAlgorithm{"ES384", crypto.SHA384} - es512 = &signatureAlgorithm{"ES512", crypto.SHA512} -) - -func rsaSignatureAlgorithmByName(alg string) (*signatureAlgorithm, error) { - switch { - case alg == "RS256": - return rs256, nil - case alg == "RS384": - return rs384, nil - case alg == "RS512": - return rs512, nil - default: - return nil, fmt.Errorf("RSA Digital Signature Algorithm %q not supported", alg) - } -} - -func rsaPKCS1v15SignatureAlgorithmForHashID(hashID crypto.Hash) *signatureAlgorithm { - switch { - case hashID == crypto.SHA512: - return rs512 - case hashID == crypto.SHA384: - return rs384 - case hashID == crypto.SHA256: - fallthrough - default: - return rs256 - } -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/jsonsign.go b/Godeps/_workspace/src/github.com/docker/libtrust/jsonsign.go deleted file mode 100644 index cb2ca9a76..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/jsonsign.go +++ /dev/null @@ -1,657 +0,0 @@ -package libtrust - -import ( - "bytes" - "crypto" - "crypto/x509" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "sort" - "time" - "unicode" -) - -var ( - // ErrInvalidSignContent is used when the content to be signed is invalid. - ErrInvalidSignContent = errors.New("invalid sign content") - - // ErrInvalidJSONContent is used when invalid json is encountered. - ErrInvalidJSONContent = errors.New("invalid json content") - - // ErrMissingSignatureKey is used when the specified signature key - // does not exist in the JSON content. - ErrMissingSignatureKey = errors.New("missing signature key") -) - -type jsHeader struct { - JWK PublicKey `json:"jwk,omitempty"` - Algorithm string `json:"alg"` - Chain []string `json:"x5c,omitempty"` -} - -type jsSignature struct { - Header jsHeader `json:"header"` - Signature string `json:"signature"` - Protected string `json:"protected,omitempty"` -} - -type jsSignaturesSorted []jsSignature - -func (jsbkid jsSignaturesSorted) Swap(i, j int) { jsbkid[i], jsbkid[j] = jsbkid[j], jsbkid[i] } -func (jsbkid jsSignaturesSorted) Len() int { return len(jsbkid) } - -func (jsbkid jsSignaturesSorted) Less(i, j int) bool { - ki, kj := jsbkid[i].Header.JWK.KeyID(), jsbkid[j].Header.JWK.KeyID() - si, sj := jsbkid[i].Signature, jsbkid[j].Signature - - if ki == kj { - return si < sj - } - - return ki < kj -} - -type signKey struct { - PrivateKey - Chain []*x509.Certificate -} - -// JSONSignature represents a signature of a json object. -type JSONSignature struct { - payload string - signatures []jsSignature - indent string - formatLength int - formatTail []byte -} - -func newJSONSignature() *JSONSignature { - return &JSONSignature{ - signatures: make([]jsSignature, 0, 1), - } -} - -// Payload returns the encoded payload of the signature. This -// payload should not be signed directly -func (js *JSONSignature) Payload() ([]byte, error) { - return joseBase64UrlDecode(js.payload) -} - -func (js *JSONSignature) protectedHeader() (string, error) { - protected := map[string]interface{}{ - "formatLength": js.formatLength, - "formatTail": joseBase64UrlEncode(js.formatTail), - "time": time.Now().UTC().Format(time.RFC3339), - } - protectedBytes, err := json.Marshal(protected) - if err != nil { - return "", err - } - - return joseBase64UrlEncode(protectedBytes), nil -} - -func (js *JSONSignature) signBytes(protectedHeader string) ([]byte, error) { - buf := make([]byte, len(js.payload)+len(protectedHeader)+1) - copy(buf, protectedHeader) - buf[len(protectedHeader)] = '.' - copy(buf[len(protectedHeader)+1:], js.payload) - return buf, nil -} - -// Sign adds a signature using the given private key. -func (js *JSONSignature) Sign(key PrivateKey) error { - protected, err := js.protectedHeader() - if err != nil { - return err - } - signBytes, err := js.signBytes(protected) - if err != nil { - return err - } - sigBytes, algorithm, err := key.Sign(bytes.NewReader(signBytes), crypto.SHA256) - if err != nil { - return err - } - - js.signatures = append(js.signatures, jsSignature{ - Header: jsHeader{ - JWK: key.PublicKey(), - Algorithm: algorithm, - }, - Signature: joseBase64UrlEncode(sigBytes), - Protected: protected, - }) - - return nil -} - -// SignWithChain adds a signature using the given private key -// and setting the x509 chain. The public key of the first element -// in the chain must be the public key corresponding with the sign key. -func (js *JSONSignature) SignWithChain(key PrivateKey, chain []*x509.Certificate) error { - // Ensure key.Chain[0] is public key for key - //key.Chain.PublicKey - //key.PublicKey().CryptoPublicKey() - - // Verify chain - protected, err := js.protectedHeader() - if err != nil { - return err - } - signBytes, err := js.signBytes(protected) - if err != nil { - return err - } - sigBytes, algorithm, err := key.Sign(bytes.NewReader(signBytes), crypto.SHA256) - if err != nil { - return err - } - - header := jsHeader{ - Chain: make([]string, len(chain)), - Algorithm: algorithm, - } - - for i, cert := range chain { - header.Chain[i] = base64.StdEncoding.EncodeToString(cert.Raw) - } - - js.signatures = append(js.signatures, jsSignature{ - Header: header, - Signature: joseBase64UrlEncode(sigBytes), - Protected: protected, - }) - - return nil -} - -// Verify verifies all the signatures and returns the list of -// public keys used to sign. Any x509 chains are not checked. -func (js *JSONSignature) Verify() ([]PublicKey, error) { - keys := make([]PublicKey, len(js.signatures)) - for i, signature := range js.signatures { - signBytes, err := js.signBytes(signature.Protected) - if err != nil { - return nil, err - } - var publicKey PublicKey - if len(signature.Header.Chain) > 0 { - certBytes, err := base64.StdEncoding.DecodeString(signature.Header.Chain[0]) - if err != nil { - return nil, err - } - cert, err := x509.ParseCertificate(certBytes) - if err != nil { - return nil, err - } - publicKey, err = FromCryptoPublicKey(cert.PublicKey) - if err != nil { - return nil, err - } - } else if signature.Header.JWK != nil { - publicKey = signature.Header.JWK - } else { - return nil, errors.New("missing public key") - } - - sigBytes, err := joseBase64UrlDecode(signature.Signature) - if err != nil { - return nil, err - } - - err = publicKey.Verify(bytes.NewReader(signBytes), signature.Header.Algorithm, sigBytes) - if err != nil { - return nil, err - } - - keys[i] = publicKey - } - return keys, nil -} - -// VerifyChains verifies all the signatures and the chains associated -// with each signature and returns the list of verified chains. -// Signatures without an x509 chain are not checked. -func (js *JSONSignature) VerifyChains(ca *x509.CertPool) ([][]*x509.Certificate, error) { - chains := make([][]*x509.Certificate, 0, len(js.signatures)) - for _, signature := range js.signatures { - signBytes, err := js.signBytes(signature.Protected) - if err != nil { - return nil, err - } - var publicKey PublicKey - if len(signature.Header.Chain) > 0 { - certBytes, err := base64.StdEncoding.DecodeString(signature.Header.Chain[0]) - if err != nil { - return nil, err - } - cert, err := x509.ParseCertificate(certBytes) - if err != nil { - return nil, err - } - publicKey, err = FromCryptoPublicKey(cert.PublicKey) - if err != nil { - return nil, err - } - intermediates := x509.NewCertPool() - if len(signature.Header.Chain) > 1 { - intermediateChain := signature.Header.Chain[1:] - for i := range intermediateChain { - certBytes, err := base64.StdEncoding.DecodeString(intermediateChain[i]) - if err != nil { - return nil, err - } - intermediate, err := x509.ParseCertificate(certBytes) - if err != nil { - return nil, err - } - intermediates.AddCert(intermediate) - } - } - - verifyOptions := x509.VerifyOptions{ - Intermediates: intermediates, - Roots: ca, - } - - verifiedChains, err := cert.Verify(verifyOptions) - if err != nil { - return nil, err - } - chains = append(chains, verifiedChains...) - - sigBytes, err := joseBase64UrlDecode(signature.Signature) - if err != nil { - return nil, err - } - - err = publicKey.Verify(bytes.NewReader(signBytes), signature.Header.Algorithm, sigBytes) - if err != nil { - return nil, err - } - } - - } - return chains, nil -} - -// JWS returns JSON serialized JWS according to -// http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-7.2 -func (js *JSONSignature) JWS() ([]byte, error) { - if len(js.signatures) == 0 { - return nil, errors.New("missing signature") - } - - sort.Sort(jsSignaturesSorted(js.signatures)) - - jsonMap := map[string]interface{}{ - "payload": js.payload, - "signatures": js.signatures, - } - - return json.MarshalIndent(jsonMap, "", " ") -} - -func notSpace(r rune) bool { - return !unicode.IsSpace(r) -} - -func detectJSONIndent(jsonContent []byte) (indent string) { - if len(jsonContent) > 2 && jsonContent[0] == '{' && jsonContent[1] == '\n' { - quoteIndex := bytes.IndexRune(jsonContent[1:], '"') - if quoteIndex > 0 { - indent = string(jsonContent[2 : quoteIndex+1]) - } - } - return -} - -type jsParsedHeader struct { - JWK json.RawMessage `json:"jwk"` - Algorithm string `json:"alg"` - Chain []string `json:"x5c"` -} - -type jsParsedSignature struct { - Header jsParsedHeader `json:"header"` - Signature string `json:"signature"` - Protected string `json:"protected"` -} - -// ParseJWS parses a JWS serialized JSON object into a Json Signature. -func ParseJWS(content []byte) (*JSONSignature, error) { - type jsParsed struct { - Payload string `json:"payload"` - Signatures []jsParsedSignature `json:"signatures"` - } - parsed := &jsParsed{} - err := json.Unmarshal(content, parsed) - if err != nil { - return nil, err - } - if len(parsed.Signatures) == 0 { - return nil, errors.New("missing signatures") - } - payload, err := joseBase64UrlDecode(parsed.Payload) - if err != nil { - return nil, err - } - - js, err := NewJSONSignature(payload) - if err != nil { - return nil, err - } - js.signatures = make([]jsSignature, len(parsed.Signatures)) - for i, signature := range parsed.Signatures { - header := jsHeader{ - Algorithm: signature.Header.Algorithm, - } - if signature.Header.Chain != nil { - header.Chain = signature.Header.Chain - } - if signature.Header.JWK != nil { - publicKey, err := UnmarshalPublicKeyJWK([]byte(signature.Header.JWK)) - if err != nil { - return nil, err - } - header.JWK = publicKey - } - js.signatures[i] = jsSignature{ - Header: header, - Signature: signature.Signature, - Protected: signature.Protected, - } - } - - return js, nil -} - -// NewJSONSignature returns a new unsigned JWS from a json byte array. -// JSONSignature will need to be signed before serializing or storing. -// Optionally, one or more signatures can be provided as byte buffers, -// containing serialized JWS signatures, to assemble a fully signed JWS -// package. It is the callers responsibility to ensure uniqueness of the -// provided signatures. -func NewJSONSignature(content []byte, signatures ...[]byte) (*JSONSignature, error) { - var dataMap map[string]interface{} - err := json.Unmarshal(content, &dataMap) - if err != nil { - return nil, err - } - - js := newJSONSignature() - js.indent = detectJSONIndent(content) - - js.payload = joseBase64UrlEncode(content) - - // Find trailing } and whitespace, put in protected header - closeIndex := bytes.LastIndexFunc(content, notSpace) - if content[closeIndex] != '}' { - return nil, ErrInvalidJSONContent - } - lastRuneIndex := bytes.LastIndexFunc(content[:closeIndex], notSpace) - if content[lastRuneIndex] == ',' { - return nil, ErrInvalidJSONContent - } - js.formatLength = lastRuneIndex + 1 - js.formatTail = content[js.formatLength:] - - if len(signatures) > 0 { - for _, signature := range signatures { - var parsedJSig jsParsedSignature - - if err := json.Unmarshal(signature, &parsedJSig); err != nil { - return nil, err - } - - // TODO(stevvooe): A lot of the code below is repeated in - // ParseJWS. It will require more refactoring to fix that. - jsig := jsSignature{ - Header: jsHeader{ - Algorithm: parsedJSig.Header.Algorithm, - }, - Signature: parsedJSig.Signature, - Protected: parsedJSig.Protected, - } - - if parsedJSig.Header.Chain != nil { - jsig.Header.Chain = parsedJSig.Header.Chain - } - - if parsedJSig.Header.JWK != nil { - publicKey, err := UnmarshalPublicKeyJWK([]byte(parsedJSig.Header.JWK)) - if err != nil { - return nil, err - } - jsig.Header.JWK = publicKey - } - - js.signatures = append(js.signatures, jsig) - } - } - - return js, nil -} - -// NewJSONSignatureFromMap returns a new unsigned JSONSignature from a map or -// struct. JWS will need to be signed before serializing or storing. -func NewJSONSignatureFromMap(content interface{}) (*JSONSignature, error) { - switch content.(type) { - case map[string]interface{}: - case struct{}: - default: - return nil, errors.New("invalid data type") - } - - js := newJSONSignature() - js.indent = " " - - payload, err := json.MarshalIndent(content, "", js.indent) - if err != nil { - return nil, err - } - js.payload = joseBase64UrlEncode(payload) - - // Remove '\n}' from formatted section, put in protected header - js.formatLength = len(payload) - 2 - js.formatTail = payload[js.formatLength:] - - return js, nil -} - -func readIntFromMap(key string, m map[string]interface{}) (int, bool) { - value, ok := m[key] - if !ok { - return 0, false - } - switch v := value.(type) { - case int: - return v, true - case float64: - return int(v), true - default: - return 0, false - } -} - -func readStringFromMap(key string, m map[string]interface{}) (v string, ok bool) { - value, ok := m[key] - if !ok { - return "", false - } - v, ok = value.(string) - return -} - -// ParsePrettySignature parses a formatted signature into a -// JSON signature. If the signatures are missing the format information -// an error is thrown. The formatted signature must be created by -// the same method as format signature. -func ParsePrettySignature(content []byte, signatureKey string) (*JSONSignature, error) { - var contentMap map[string]json.RawMessage - err := json.Unmarshal(content, &contentMap) - if err != nil { - return nil, fmt.Errorf("error unmarshalling content: %s", err) - } - sigMessage, ok := contentMap[signatureKey] - if !ok { - return nil, ErrMissingSignatureKey - } - - var signatureBlocks []jsParsedSignature - err = json.Unmarshal([]byte(sigMessage), &signatureBlocks) - if err != nil { - return nil, fmt.Errorf("error unmarshalling signatures: %s", err) - } - - js := newJSONSignature() - js.signatures = make([]jsSignature, len(signatureBlocks)) - - for i, signatureBlock := range signatureBlocks { - protectedBytes, err := joseBase64UrlDecode(signatureBlock.Protected) - if err != nil { - return nil, fmt.Errorf("base64 decode error: %s", err) - } - var protectedHeader map[string]interface{} - err = json.Unmarshal(protectedBytes, &protectedHeader) - if err != nil { - return nil, fmt.Errorf("error unmarshalling protected header: %s", err) - } - - formatLength, ok := readIntFromMap("formatLength", protectedHeader) - if !ok { - return nil, errors.New("missing formatted length") - } - encodedTail, ok := readStringFromMap("formatTail", protectedHeader) - if !ok { - return nil, errors.New("missing formatted tail") - } - formatTail, err := joseBase64UrlDecode(encodedTail) - if err != nil { - return nil, fmt.Errorf("base64 decode error on tail: %s", err) - } - if js.formatLength == 0 { - js.formatLength = formatLength - } else if js.formatLength != formatLength { - return nil, errors.New("conflicting format length") - } - if len(js.formatTail) == 0 { - js.formatTail = formatTail - } else if bytes.Compare(js.formatTail, formatTail) != 0 { - return nil, errors.New("conflicting format tail") - } - - header := jsHeader{ - Algorithm: signatureBlock.Header.Algorithm, - Chain: signatureBlock.Header.Chain, - } - if signatureBlock.Header.JWK != nil { - publicKey, err := UnmarshalPublicKeyJWK([]byte(signatureBlock.Header.JWK)) - if err != nil { - return nil, fmt.Errorf("error unmarshalling public key: %s", err) - } - header.JWK = publicKey - } - js.signatures[i] = jsSignature{ - Header: header, - Signature: signatureBlock.Signature, - Protected: signatureBlock.Protected, - } - } - if js.formatLength > len(content) { - return nil, errors.New("invalid format length") - } - formatted := make([]byte, js.formatLength+len(js.formatTail)) - copy(formatted, content[:js.formatLength]) - copy(formatted[js.formatLength:], js.formatTail) - js.indent = detectJSONIndent(formatted) - js.payload = joseBase64UrlEncode(formatted) - - return js, nil -} - -// PrettySignature formats a json signature into an easy to read -// single json serialized object. -func (js *JSONSignature) PrettySignature(signatureKey string) ([]byte, error) { - if len(js.signatures) == 0 { - return nil, errors.New("no signatures") - } - payload, err := joseBase64UrlDecode(js.payload) - if err != nil { - return nil, err - } - payload = payload[:js.formatLength] - - sort.Sort(jsSignaturesSorted(js.signatures)) - - var marshalled []byte - var marshallErr error - if js.indent != "" { - marshalled, marshallErr = json.MarshalIndent(js.signatures, js.indent, js.indent) - } else { - marshalled, marshallErr = json.Marshal(js.signatures) - } - if marshallErr != nil { - return nil, marshallErr - } - - buf := bytes.NewBuffer(make([]byte, 0, len(payload)+len(marshalled)+34)) - buf.Write(payload) - buf.WriteByte(',') - if js.indent != "" { - buf.WriteByte('\n') - buf.WriteString(js.indent) - buf.WriteByte('"') - buf.WriteString(signatureKey) - buf.WriteString("\": ") - buf.Write(marshalled) - buf.WriteByte('\n') - } else { - buf.WriteByte('"') - buf.WriteString(signatureKey) - buf.WriteString("\":") - buf.Write(marshalled) - } - buf.WriteByte('}') - - return buf.Bytes(), nil -} - -// Signatures provides the signatures on this JWS as opaque blobs, sorted by -// keyID. These blobs can be stored and reassembled with payloads. Internally, -// they are simply marshaled json web signatures but implementations should -// not rely on this. -func (js *JSONSignature) Signatures() ([][]byte, error) { - sort.Sort(jsSignaturesSorted(js.signatures)) - - var sb [][]byte - for _, jsig := range js.signatures { - p, err := json.Marshal(jsig) - if err != nil { - return nil, err - } - - sb = append(sb, p) - } - - return sb, nil -} - -// Merge combines the signatures from one or more other signatures into the -// method receiver. If the payloads differ for any argument, an error will be -// returned and the receiver will not be modified. -func (js *JSONSignature) Merge(others ...*JSONSignature) error { - merged := js.signatures - for _, other := range others { - if js.payload != other.payload { - return fmt.Errorf("payloads differ from merge target") - } - merged = append(merged, other.signatures...) - } - - js.signatures = merged - return nil -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/jsonsign_test.go b/Godeps/_workspace/src/github.com/docker/libtrust/jsonsign_test.go deleted file mode 100644 index b4f269798..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/jsonsign_test.go +++ /dev/null @@ -1,380 +0,0 @@ -package libtrust - -import ( - "bytes" - "crypto/rand" - "crypto/x509" - "encoding/json" - "fmt" - "io" - "testing" - - "github.com/docker/libtrust/testutil" -) - -func createTestJSON(sigKey string, indent string) (map[string]interface{}, []byte) { - testMap := map[string]interface{}{ - "name": "dmcgowan/mycontainer", - "config": map[string]interface{}{ - "ports": []int{9101, 9102}, - "run": "/bin/echo \"Hello\"", - }, - "layers": []string{ - "2893c080-27f5-11e4-8c21-0800200c9a66", - "c54bc25b-fbb2-497b-a899-a8bc1b5b9d55", - "4d5d7e03-f908-49f3-a7f6-9ba28dfe0fb4", - "0b6da891-7f7f-4abf-9c97-7887549e696c", - "1d960389-ae4f-4011-85fd-18d0f96a67ad", - }, - } - formattedSection := `{"config":{"ports":[9101,9102],"run":"/bin/echo \"Hello\""},"layers":["2893c080-27f5-11e4-8c21-0800200c9a66","c54bc25b-fbb2-497b-a899-a8bc1b5b9d55","4d5d7e03-f908-49f3-a7f6-9ba28dfe0fb4","0b6da891-7f7f-4abf-9c97-7887549e696c","1d960389-ae4f-4011-85fd-18d0f96a67ad"],"name":"dmcgowan/mycontainer","%s":[{"header":{` - formattedSection = fmt.Sprintf(formattedSection, sigKey) - if indent != "" { - buf := bytes.NewBuffer(nil) - json.Indent(buf, []byte(formattedSection), "", indent) - return testMap, buf.Bytes() - } - return testMap, []byte(formattedSection) - -} - -func TestSignJSON(t *testing.T) { - key, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("Error generating EC key: %s", err) - } - - testMap, _ := createTestJSON("buildSignatures", " ") - indented, err := json.MarshalIndent(testMap, "", " ") - if err != nil { - t.Fatalf("Marshall error: %s", err) - } - - js, err := NewJSONSignature(indented) - if err != nil { - t.Fatalf("Error creating JSON signature: %s", err) - } - err = js.Sign(key) - if err != nil { - t.Fatalf("Error signing content: %s", err) - } - - keys, err := js.Verify() - if err != nil { - t.Fatalf("Error verifying signature: %s", err) - } - if len(keys) != 1 { - t.Fatalf("Error wrong number of keys returned") - } - if keys[0].KeyID() != key.KeyID() { - t.Fatalf("Unexpected public key returned") - } - -} - -func TestSignMap(t *testing.T) { - key, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("Error generating EC key: %s", err) - } - - testMap, _ := createTestJSON("buildSignatures", " ") - js, err := NewJSONSignatureFromMap(testMap) - if err != nil { - t.Fatalf("Error creating JSON signature: %s", err) - } - err = js.Sign(key) - if err != nil { - t.Fatalf("Error signing JSON signature: %s", err) - } - - keys, err := js.Verify() - if err != nil { - t.Fatalf("Error verifying signature: %s", err) - } - if len(keys) != 1 { - t.Fatalf("Error wrong number of keys returned") - } - if keys[0].KeyID() != key.KeyID() { - t.Fatalf("Unexpected public key returned") - } -} - -func TestFormattedJson(t *testing.T) { - key, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("Error generating EC key: %s", err) - } - - testMap, firstSection := createTestJSON("buildSignatures", " ") - indented, err := json.MarshalIndent(testMap, "", " ") - if err != nil { - t.Fatalf("Marshall error: %s", err) - } - - js, err := NewJSONSignature(indented) - if err != nil { - t.Fatalf("Error creating JSON signature: %s", err) - } - err = js.Sign(key) - if err != nil { - t.Fatalf("Error signing content: %s", err) - } - - b, err := js.PrettySignature("buildSignatures") - if err != nil { - t.Fatalf("Error signing map: %s", err) - } - - if bytes.Compare(b[:len(firstSection)], firstSection) != 0 { - t.Fatalf("Wrong signed value\nExpected:\n%s\nActual:\n%s", firstSection, b[:len(firstSection)]) - } - - parsed, err := ParsePrettySignature(b, "buildSignatures") - if err != nil { - t.Fatalf("Error parsing formatted signature: %s", err) - } - - keys, err := parsed.Verify() - if err != nil { - t.Fatalf("Error verifying signature: %s", err) - } - if len(keys) != 1 { - t.Fatalf("Error wrong number of keys returned") - } - if keys[0].KeyID() != key.KeyID() { - t.Fatalf("Unexpected public key returned") - } - - var unmarshalled map[string]interface{} - err = json.Unmarshal(b, &unmarshalled) - if err != nil { - t.Fatalf("Could not unmarshall after parse: %s", err) - } - -} - -func TestFormattedFlatJson(t *testing.T) { - key, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("Error generating EC key: %s", err) - } - - testMap, firstSection := createTestJSON("buildSignatures", "") - unindented, err := json.Marshal(testMap) - if err != nil { - t.Fatalf("Marshall error: %s", err) - } - - js, err := NewJSONSignature(unindented) - if err != nil { - t.Fatalf("Error creating JSON signature: %s", err) - } - err = js.Sign(key) - if err != nil { - t.Fatalf("Error signing JSON signature: %s", err) - } - - b, err := js.PrettySignature("buildSignatures") - if err != nil { - t.Fatalf("Error signing map: %s", err) - } - - if bytes.Compare(b[:len(firstSection)], firstSection) != 0 { - t.Fatalf("Wrong signed value\nExpected:\n%s\nActual:\n%s", firstSection, b[:len(firstSection)]) - } - - parsed, err := ParsePrettySignature(b, "buildSignatures") - if err != nil { - t.Fatalf("Error parsing formatted signature: %s", err) - } - - keys, err := parsed.Verify() - if err != nil { - t.Fatalf("Error verifying signature: %s", err) - } - if len(keys) != 1 { - t.Fatalf("Error wrong number of keys returned") - } - if keys[0].KeyID() != key.KeyID() { - t.Fatalf("Unexpected public key returned") - } -} - -func generateTrustChain(t *testing.T, key PrivateKey, ca *x509.Certificate) (PrivateKey, []*x509.Certificate) { - parent := ca - parentKey := key - chain := make([]*x509.Certificate, 6) - for i := 5; i > 0; i-- { - intermediatekey, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("Error generate key: %s", err) - } - chain[i], err = testutil.GenerateIntermediate(intermediatekey.CryptoPublicKey(), parentKey.CryptoPrivateKey(), parent) - if err != nil { - t.Fatalf("Error generating intermdiate certificate: %s", err) - } - parent = chain[i] - parentKey = intermediatekey - } - trustKey, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("Error generate key: %s", err) - } - chain[0], err = testutil.GenerateTrustCert(trustKey.CryptoPublicKey(), parentKey.CryptoPrivateKey(), parent) - if err != nil { - t.Fatalf("Error generate trust cert: %s", err) - } - - return trustKey, chain -} - -func TestChainVerify(t *testing.T) { - caKey, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("Error generating key: %s", err) - } - ca, err := testutil.GenerateTrustCA(caKey.CryptoPublicKey(), caKey.CryptoPrivateKey()) - if err != nil { - t.Fatalf("Error generating ca: %s", err) - } - trustKey, chain := generateTrustChain(t, caKey, ca) - - testMap, _ := createTestJSON("verifySignatures", " ") - js, err := NewJSONSignatureFromMap(testMap) - if err != nil { - t.Fatalf("Error creating JSONSignature from map: %s", err) - } - - err = js.SignWithChain(trustKey, chain) - if err != nil { - t.Fatalf("Error signing with chain: %s", err) - } - - pool := x509.NewCertPool() - pool.AddCert(ca) - chains, err := js.VerifyChains(pool) - if err != nil { - t.Fatalf("Error verifying content: %s", err) - } - if len(chains) != 1 { - t.Fatalf("Unexpected chains length: %d", len(chains)) - } - if len(chains[0]) != 7 { - t.Fatalf("Unexpected chain length: %d", len(chains[0])) - } -} - -func TestInvalidChain(t *testing.T) { - caKey, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("Error generating key: %s", err) - } - ca, err := testutil.GenerateTrustCA(caKey.CryptoPublicKey(), caKey.CryptoPrivateKey()) - if err != nil { - t.Fatalf("Error generating ca: %s", err) - } - trustKey, chain := generateTrustChain(t, caKey, ca) - - testMap, _ := createTestJSON("verifySignatures", " ") - js, err := NewJSONSignatureFromMap(testMap) - if err != nil { - t.Fatalf("Error creating JSONSignature from map: %s", err) - } - - err = js.SignWithChain(trustKey, chain[:5]) - if err != nil { - t.Fatalf("Error signing with chain: %s", err) - } - - pool := x509.NewCertPool() - pool.AddCert(ca) - chains, err := js.VerifyChains(pool) - if err == nil { - t.Fatalf("Expected error verifying with bad chain") - } - if len(chains) != 0 { - t.Fatalf("Unexpected chains returned from invalid verify") - } -} - -func TestMergeSignatures(t *testing.T) { - pk1, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("unexpected error generating private key 1: %v", err) - } - - pk2, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("unexpected error generating private key 2: %v", err) - } - - payload := make([]byte, 1<<10) - if _, err = io.ReadFull(rand.Reader, payload); err != nil { - t.Fatalf("error generating payload: %v", err) - } - - payload, _ = json.Marshal(map[string]interface{}{"data": payload}) - - sig1, err := NewJSONSignature(payload) - if err != nil { - t.Fatalf("unexpected error creating signature 1: %v", err) - } - - if err := sig1.Sign(pk1); err != nil { - t.Fatalf("unexpected error signing with pk1: %v", err) - } - - sig2, err := NewJSONSignature(payload) - if err != nil { - t.Fatalf("unexpected error creating signature 2: %v", err) - } - - if err := sig2.Sign(pk2); err != nil { - t.Fatalf("unexpected error signing with pk2: %v", err) - } - - // Now, we actually merge into sig1 - if err := sig1.Merge(sig2); err != nil { - t.Fatalf("unexpected error merging: %v", err) - } - - // Verify the new signature package - pubkeys, err := sig1.Verify() - if err != nil { - t.Fatalf("unexpected error during verify: %v", err) - } - - // Make sure the pubkeys match the two private keys from before - privkeys := map[string]PrivateKey{ - pk1.KeyID(): pk1, - pk2.KeyID(): pk2, - } - - found := map[string]struct{}{} - - for _, pubkey := range pubkeys { - if _, ok := privkeys[pubkey.KeyID()]; !ok { - t.Fatalf("unexpected public key found during verification: %v", pubkey) - } - - found[pubkey.KeyID()] = struct{}{} - } - - // Make sure we've found all the private keys from verification - for keyid, _ := range privkeys { - if _, ok := found[keyid]; !ok { - t.Fatalf("public key %v not found during verification", keyid) - } - } - - // Create another signature, with a different payload, and ensure we get an error. - sig3, err := NewJSONSignature([]byte("{}")) - if err != nil { - t.Fatalf("unexpected error making signature for sig3: %v", err) - } - - if err := sig1.Merge(sig3); err == nil { - t.Fatalf("error expected during invalid merge with different payload") - } -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/key.go b/Godeps/_workspace/src/github.com/docker/libtrust/key.go deleted file mode 100644 index 73642db2a..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/key.go +++ /dev/null @@ -1,253 +0,0 @@ -package libtrust - -import ( - "crypto" - "crypto/ecdsa" - "crypto/rsa" - "crypto/x509" - "encoding/json" - "encoding/pem" - "errors" - "fmt" - "io" -) - -// PublicKey is a generic interface for a Public Key. -type PublicKey interface { - // KeyType returns the key type for this key. For elliptic curve keys, - // this value should be "EC". For RSA keys, this value should be "RSA". - KeyType() string - // KeyID returns a distinct identifier which is unique to this Public Key. - // The format generated by this library is a base32 encoding of a 240 bit - // hash of the public key data divided into 12 groups like so: - // ABCD:EFGH:IJKL:MNOP:QRST:UVWX:YZ23:4567:ABCD:EFGH:IJKL:MNOP - KeyID() string - // Verify verifyies the signature of the data in the io.Reader using this - // Public Key. The alg parameter should identify the digital signature - // algorithm which was used to produce the signature and should be - // supported by this public key. Returns a nil error if the signature - // is valid. - Verify(data io.Reader, alg string, signature []byte) error - // CryptoPublicKey returns the internal object which can be used as a - // crypto.PublicKey for use with other standard library operations. The type - // is either *rsa.PublicKey or *ecdsa.PublicKey - CryptoPublicKey() crypto.PublicKey - // These public keys can be serialized to the standard JSON encoding for - // JSON Web Keys. See section 6 of the IETF draft RFC for JOSE JSON Web - // Algorithms. - MarshalJSON() ([]byte, error) - // These keys can also be serialized to the standard PEM encoding. - PEMBlock() (*pem.Block, error) - // The string representation of a key is its key type and ID. - String() string - AddExtendedField(string, interface{}) - GetExtendedField(string) interface{} -} - -// PrivateKey is a generic interface for a Private Key. -type PrivateKey interface { - // A PrivateKey contains all fields and methods of a PublicKey of the - // same type. The MarshalJSON method also outputs the private key as a - // JSON Web Key, and the PEMBlock method outputs the private key as a - // PEM block. - PublicKey - // PublicKey returns the PublicKey associated with this PrivateKey. - PublicKey() PublicKey - // Sign signs the data read from the io.Reader using a signature algorithm - // supported by the private key. If the specified hashing algorithm is - // supported by this key, that hash function is used to generate the - // signature otherwise the the default hashing algorithm for this key is - // used. Returns the signature and identifier of the algorithm used. - Sign(data io.Reader, hashID crypto.Hash) (signature []byte, alg string, err error) - // CryptoPrivateKey returns the internal object which can be used as a - // crypto.PublicKey for use with other standard library operations. The - // type is either *rsa.PublicKey or *ecdsa.PublicKey - CryptoPrivateKey() crypto.PrivateKey -} - -// FromCryptoPublicKey returns a libtrust PublicKey representation of the given -// *ecdsa.PublicKey or *rsa.PublicKey. Returns a non-nil error when the given -// key is of an unsupported type. -func FromCryptoPublicKey(cryptoPublicKey crypto.PublicKey) (PublicKey, error) { - switch cryptoPublicKey := cryptoPublicKey.(type) { - case *ecdsa.PublicKey: - return fromECPublicKey(cryptoPublicKey) - case *rsa.PublicKey: - return fromRSAPublicKey(cryptoPublicKey), nil - default: - return nil, fmt.Errorf("public key type %T is not supported", cryptoPublicKey) - } -} - -// FromCryptoPrivateKey returns a libtrust PrivateKey representation of the given -// *ecdsa.PrivateKey or *rsa.PrivateKey. Returns a non-nil error when the given -// key is of an unsupported type. -func FromCryptoPrivateKey(cryptoPrivateKey crypto.PrivateKey) (PrivateKey, error) { - switch cryptoPrivateKey := cryptoPrivateKey.(type) { - case *ecdsa.PrivateKey: - return fromECPrivateKey(cryptoPrivateKey) - case *rsa.PrivateKey: - return fromRSAPrivateKey(cryptoPrivateKey), nil - default: - return nil, fmt.Errorf("private key type %T is not supported", cryptoPrivateKey) - } -} - -// UnmarshalPublicKeyPEM parses the PEM encoded data and returns a libtrust -// PublicKey or an error if there is a problem with the encoding. -func UnmarshalPublicKeyPEM(data []byte) (PublicKey, error) { - pemBlock, _ := pem.Decode(data) - if pemBlock == nil { - return nil, errors.New("unable to find PEM encoded data") - } else if pemBlock.Type != "PUBLIC KEY" { - return nil, fmt.Errorf("unable to get PublicKey from PEM type: %s", pemBlock.Type) - } - - return pubKeyFromPEMBlock(pemBlock) -} - -// UnmarshalPublicKeyPEMBundle parses the PEM encoded data as a bundle of -// PEM blocks appended one after the other and returns a slice of PublicKey -// objects that it finds. -func UnmarshalPublicKeyPEMBundle(data []byte) ([]PublicKey, error) { - pubKeys := []PublicKey{} - - for { - var pemBlock *pem.Block - pemBlock, data = pem.Decode(data) - if pemBlock == nil { - break - } else if pemBlock.Type != "PUBLIC KEY" { - return nil, fmt.Errorf("unable to get PublicKey from PEM type: %s", pemBlock.Type) - } - - pubKey, err := pubKeyFromPEMBlock(pemBlock) - if err != nil { - return nil, err - } - - pubKeys = append(pubKeys, pubKey) - } - - return pubKeys, nil -} - -// UnmarshalPrivateKeyPEM parses the PEM encoded data and returns a libtrust -// PrivateKey or an error if there is a problem with the encoding. -func UnmarshalPrivateKeyPEM(data []byte) (PrivateKey, error) { - pemBlock, _ := pem.Decode(data) - if pemBlock == nil { - return nil, errors.New("unable to find PEM encoded data") - } - - var key PrivateKey - - switch { - case pemBlock.Type == "RSA PRIVATE KEY": - rsaPrivateKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes) - if err != nil { - return nil, fmt.Errorf("unable to decode RSA Private Key PEM data: %s", err) - } - key = fromRSAPrivateKey(rsaPrivateKey) - case pemBlock.Type == "EC PRIVATE KEY": - ecPrivateKey, err := x509.ParseECPrivateKey(pemBlock.Bytes) - if err != nil { - return nil, fmt.Errorf("unable to decode EC Private Key PEM data: %s", err) - } - key, err = fromECPrivateKey(ecPrivateKey) - if err != nil { - return nil, err - } - default: - return nil, fmt.Errorf("unable to get PrivateKey from PEM type: %s", pemBlock.Type) - } - - addPEMHeadersToKey(pemBlock, key.PublicKey()) - - return key, nil -} - -// UnmarshalPublicKeyJWK unmarshals the given JSON Web Key into a generic -// Public Key to be used with libtrust. -func UnmarshalPublicKeyJWK(data []byte) (PublicKey, error) { - jwk := make(map[string]interface{}) - - err := json.Unmarshal(data, &jwk) - if err != nil { - return nil, fmt.Errorf( - "decoding JWK Public Key JSON data: %s\n", err, - ) - } - - // Get the Key Type value. - kty, err := stringFromMap(jwk, "kty") - if err != nil { - return nil, fmt.Errorf("JWK Public Key type: %s", err) - } - - switch { - case kty == "EC": - // Call out to unmarshal EC public key. - return ecPublicKeyFromMap(jwk) - case kty == "RSA": - // Call out to unmarshal RSA public key. - return rsaPublicKeyFromMap(jwk) - default: - return nil, fmt.Errorf( - "JWK Public Key type not supported: %q\n", kty, - ) - } -} - -// UnmarshalPublicKeyJWKSet parses the JSON encoded data as a JSON Web Key Set -// and returns a slice of Public Key objects. -func UnmarshalPublicKeyJWKSet(data []byte) ([]PublicKey, error) { - rawKeys, err := loadJSONKeySetRaw(data) - if err != nil { - return nil, err - } - - pubKeys := make([]PublicKey, 0, len(rawKeys)) - - for _, rawKey := range rawKeys { - pubKey, err := UnmarshalPublicKeyJWK(rawKey) - if err != nil { - return nil, err - } - pubKeys = append(pubKeys, pubKey) - } - - return pubKeys, nil -} - -// UnmarshalPrivateKeyJWK unmarshals the given JSON Web Key into a generic -// Private Key to be used with libtrust. -func UnmarshalPrivateKeyJWK(data []byte) (PrivateKey, error) { - jwk := make(map[string]interface{}) - - err := json.Unmarshal(data, &jwk) - if err != nil { - return nil, fmt.Errorf( - "decoding JWK Private Key JSON data: %s\n", err, - ) - } - - // Get the Key Type value. - kty, err := stringFromMap(jwk, "kty") - if err != nil { - return nil, fmt.Errorf("JWK Private Key type: %s", err) - } - - switch { - case kty == "EC": - // Call out to unmarshal EC private key. - return ecPrivateKeyFromMap(jwk) - case kty == "RSA": - // Call out to unmarshal RSA private key. - return rsaPrivateKeyFromMap(jwk) - default: - return nil, fmt.Errorf( - "JWK Private Key type not supported: %q\n", kty, - ) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/key_files.go b/Godeps/_workspace/src/github.com/docker/libtrust/key_files.go deleted file mode 100644 index c526de545..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/key_files.go +++ /dev/null @@ -1,255 +0,0 @@ -package libtrust - -import ( - "encoding/json" - "encoding/pem" - "errors" - "fmt" - "io/ioutil" - "os" - "strings" -) - -var ( - // ErrKeyFileDoesNotExist indicates that the private key file does not exist. - ErrKeyFileDoesNotExist = errors.New("key file does not exist") -) - -func readKeyFileBytes(filename string) ([]byte, error) { - data, err := ioutil.ReadFile(filename) - if err != nil { - if os.IsNotExist(err) { - err = ErrKeyFileDoesNotExist - } else { - err = fmt.Errorf("unable to read key file %s: %s", filename, err) - } - - return nil, err - } - - return data, nil -} - -/* - Loading and Saving of Public and Private Keys in either PEM or JWK format. -*/ - -// LoadKeyFile opens the given filename and attempts to read a Private Key -// encoded in either PEM or JWK format (if .json or .jwk file extension). -func LoadKeyFile(filename string) (PrivateKey, error) { - contents, err := readKeyFileBytes(filename) - if err != nil { - return nil, err - } - - var key PrivateKey - - if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") { - key, err = UnmarshalPrivateKeyJWK(contents) - if err != nil { - return nil, fmt.Errorf("unable to decode private key JWK: %s", err) - } - } else { - key, err = UnmarshalPrivateKeyPEM(contents) - if err != nil { - return nil, fmt.Errorf("unable to decode private key PEM: %s", err) - } - } - - return key, nil -} - -// LoadPublicKeyFile opens the given filename and attempts to read a Public Key -// encoded in either PEM or JWK format (if .json or .jwk file extension). -func LoadPublicKeyFile(filename string) (PublicKey, error) { - contents, err := readKeyFileBytes(filename) - if err != nil { - return nil, err - } - - var key PublicKey - - if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") { - key, err = UnmarshalPublicKeyJWK(contents) - if err != nil { - return nil, fmt.Errorf("unable to decode public key JWK: %s", err) - } - } else { - key, err = UnmarshalPublicKeyPEM(contents) - if err != nil { - return nil, fmt.Errorf("unable to decode public key PEM: %s", err) - } - } - - return key, nil -} - -// SaveKey saves the given key to a file using the provided filename. -// This process will overwrite any existing file at the provided location. -func SaveKey(filename string, key PrivateKey) error { - var encodedKey []byte - var err error - - if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") { - // Encode in JSON Web Key format. - encodedKey, err = json.MarshalIndent(key, "", " ") - if err != nil { - return fmt.Errorf("unable to encode private key JWK: %s", err) - } - } else { - // Encode in PEM format. - pemBlock, err := key.PEMBlock() - if err != nil { - return fmt.Errorf("unable to encode private key PEM: %s", err) - } - encodedKey = pem.EncodeToMemory(pemBlock) - } - - err = ioutil.WriteFile(filename, encodedKey, os.FileMode(0600)) - if err != nil { - return fmt.Errorf("unable to write private key file %s: %s", filename, err) - } - - return nil -} - -// SavePublicKey saves the given public key to the file. -func SavePublicKey(filename string, key PublicKey) error { - var encodedKey []byte - var err error - - if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") { - // Encode in JSON Web Key format. - encodedKey, err = json.MarshalIndent(key, "", " ") - if err != nil { - return fmt.Errorf("unable to encode public key JWK: %s", err) - } - } else { - // Encode in PEM format. - pemBlock, err := key.PEMBlock() - if err != nil { - return fmt.Errorf("unable to encode public key PEM: %s", err) - } - encodedKey = pem.EncodeToMemory(pemBlock) - } - - err = ioutil.WriteFile(filename, encodedKey, os.FileMode(0644)) - if err != nil { - return fmt.Errorf("unable to write public key file %s: %s", filename, err) - } - - return nil -} - -// Public Key Set files - -type jwkSet struct { - Keys []json.RawMessage `json:"keys"` -} - -// LoadKeySetFile loads a key set -func LoadKeySetFile(filename string) ([]PublicKey, error) { - if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") { - return loadJSONKeySetFile(filename) - } - - // Must be a PEM format file - return loadPEMKeySetFile(filename) -} - -func loadJSONKeySetRaw(data []byte) ([]json.RawMessage, error) { - if len(data) == 0 { - // This is okay, just return an empty slice. - return []json.RawMessage{}, nil - } - - keySet := jwkSet{} - - err := json.Unmarshal(data, &keySet) - if err != nil { - return nil, fmt.Errorf("unable to decode JSON Web Key Set: %s", err) - } - - return keySet.Keys, nil -} - -func loadJSONKeySetFile(filename string) ([]PublicKey, error) { - contents, err := readKeyFileBytes(filename) - if err != nil && err != ErrKeyFileDoesNotExist { - return nil, err - } - - return UnmarshalPublicKeyJWKSet(contents) -} - -func loadPEMKeySetFile(filename string) ([]PublicKey, error) { - data, err := readKeyFileBytes(filename) - if err != nil && err != ErrKeyFileDoesNotExist { - return nil, err - } - - return UnmarshalPublicKeyPEMBundle(data) -} - -// AddKeySetFile adds a key to a key set -func AddKeySetFile(filename string, key PublicKey) error { - if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") { - return addKeySetJSONFile(filename, key) - } - - // Must be a PEM format file - return addKeySetPEMFile(filename, key) -} - -func addKeySetJSONFile(filename string, key PublicKey) error { - encodedKey, err := json.Marshal(key) - if err != nil { - return fmt.Errorf("unable to encode trusted client key: %s", err) - } - - contents, err := readKeyFileBytes(filename) - if err != nil && err != ErrKeyFileDoesNotExist { - return err - } - - rawEntries, err := loadJSONKeySetRaw(contents) - if err != nil { - return err - } - - rawEntries = append(rawEntries, json.RawMessage(encodedKey)) - entriesWrapper := jwkSet{Keys: rawEntries} - - encodedEntries, err := json.MarshalIndent(entriesWrapper, "", " ") - if err != nil { - return fmt.Errorf("unable to encode trusted client keys: %s", err) - } - - err = ioutil.WriteFile(filename, encodedEntries, os.FileMode(0644)) - if err != nil { - return fmt.Errorf("unable to write trusted client keys file %s: %s", filename, err) - } - - return nil -} - -func addKeySetPEMFile(filename string, key PublicKey) error { - // Encode to PEM, open file for appending, write PEM. - file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_RDWR, os.FileMode(0644)) - if err != nil { - return fmt.Errorf("unable to open trusted client keys file %s: %s", filename, err) - } - defer file.Close() - - pemBlock, err := key.PEMBlock() - if err != nil { - return fmt.Errorf("unable to encoded trusted key: %s", err) - } - - _, err = file.Write(pem.EncodeToMemory(pemBlock)) - if err != nil { - return fmt.Errorf("unable to write trusted keys file: %s", err) - } - - return nil -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/key_files_test.go b/Godeps/_workspace/src/github.com/docker/libtrust/key_files_test.go deleted file mode 100644 index 57e691f2e..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/key_files_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package libtrust - -import ( - "errors" - "io/ioutil" - "os" - "testing" -) - -func makeTempFile(t *testing.T, prefix string) (filename string) { - file, err := ioutil.TempFile("", prefix) - if err != nil { - t.Fatal(err) - } - - filename = file.Name() - file.Close() - - return -} - -func TestKeyFiles(t *testing.T) { - key, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - testKeyFiles(t, key) - - key, err = GenerateRSA2048PrivateKey() - if err != nil { - t.Fatal(err) - } - - testKeyFiles(t, key) -} - -func testKeyFiles(t *testing.T, key PrivateKey) { - var err error - - privateKeyFilename := makeTempFile(t, "private_key") - privateKeyFilenamePEM := privateKeyFilename + ".pem" - privateKeyFilenameJWK := privateKeyFilename + ".jwk" - - publicKeyFilename := makeTempFile(t, "public_key") - publicKeyFilenamePEM := publicKeyFilename + ".pem" - publicKeyFilenameJWK := publicKeyFilename + ".jwk" - - if err = SaveKey(privateKeyFilenamePEM, key); err != nil { - t.Fatal(err) - } - - if err = SaveKey(privateKeyFilenameJWK, key); err != nil { - t.Fatal(err) - } - - if err = SavePublicKey(publicKeyFilenamePEM, key.PublicKey()); err != nil { - t.Fatal(err) - } - - if err = SavePublicKey(publicKeyFilenameJWK, key.PublicKey()); err != nil { - t.Fatal(err) - } - - loadedPEMKey, err := LoadKeyFile(privateKeyFilenamePEM) - if err != nil { - t.Fatal(err) - } - - loadedJWKKey, err := LoadKeyFile(privateKeyFilenameJWK) - if err != nil { - t.Fatal(err) - } - - loadedPEMPublicKey, err := LoadPublicKeyFile(publicKeyFilenamePEM) - if err != nil { - t.Fatal(err) - } - - loadedJWKPublicKey, err := LoadPublicKeyFile(publicKeyFilenameJWK) - if err != nil { - t.Fatal(err) - } - - if key.KeyID() != loadedPEMKey.KeyID() { - t.Fatal(errors.New("key IDs do not match")) - } - - if key.KeyID() != loadedJWKKey.KeyID() { - t.Fatal(errors.New("key IDs do not match")) - } - - if key.KeyID() != loadedPEMPublicKey.KeyID() { - t.Fatal(errors.New("key IDs do not match")) - } - - if key.KeyID() != loadedJWKPublicKey.KeyID() { - t.Fatal(errors.New("key IDs do not match")) - } - - os.Remove(privateKeyFilename) - os.Remove(privateKeyFilenamePEM) - os.Remove(privateKeyFilenameJWK) - os.Remove(publicKeyFilename) - os.Remove(publicKeyFilenamePEM) - os.Remove(publicKeyFilenameJWK) -} - -func TestTrustedHostKeysFile(t *testing.T) { - trustedHostKeysFilename := makeTempFile(t, "trusted_host_keys") - trustedHostKeysFilenamePEM := trustedHostKeysFilename + ".pem" - trustedHostKeysFilenameJWK := trustedHostKeysFilename + ".json" - - testTrustedHostKeysFile(t, trustedHostKeysFilenamePEM) - testTrustedHostKeysFile(t, trustedHostKeysFilenameJWK) - - os.Remove(trustedHostKeysFilename) - os.Remove(trustedHostKeysFilenamePEM) - os.Remove(trustedHostKeysFilenameJWK) -} - -func testTrustedHostKeysFile(t *testing.T, trustedHostKeysFilename string) { - hostAddress1 := "docker.example.com:2376" - hostKey1, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - hostKey1.AddExtendedField("hosts", []string{hostAddress1}) - err = AddKeySetFile(trustedHostKeysFilename, hostKey1.PublicKey()) - if err != nil { - t.Fatal(err) - } - - trustedHostKeysMapping, err := LoadKeySetFile(trustedHostKeysFilename) - if err != nil { - t.Fatal(err) - } - - for addr, hostKey := range trustedHostKeysMapping { - t.Logf("Host Address: %d\n", addr) - t.Logf("Host Key: %s\n\n", hostKey) - } - - hostAddress2 := "192.168.59.103:2376" - hostKey2, err := GenerateRSA2048PrivateKey() - if err != nil { - t.Fatal(err) - } - - hostKey2.AddExtendedField("hosts", hostAddress2) - err = AddKeySetFile(trustedHostKeysFilename, hostKey2.PublicKey()) - if err != nil { - t.Fatal(err) - } - - trustedHostKeysMapping, err = LoadKeySetFile(trustedHostKeysFilename) - if err != nil { - t.Fatal(err) - } - - for addr, hostKey := range trustedHostKeysMapping { - t.Logf("Host Address: %d\n", addr) - t.Logf("Host Key: %s\n\n", hostKey) - } - -} - -func TestTrustedClientKeysFile(t *testing.T) { - trustedClientKeysFilename := makeTempFile(t, "trusted_client_keys") - trustedClientKeysFilenamePEM := trustedClientKeysFilename + ".pem" - trustedClientKeysFilenameJWK := trustedClientKeysFilename + ".json" - - testTrustedClientKeysFile(t, trustedClientKeysFilenamePEM) - testTrustedClientKeysFile(t, trustedClientKeysFilenameJWK) - - os.Remove(trustedClientKeysFilename) - os.Remove(trustedClientKeysFilenamePEM) - os.Remove(trustedClientKeysFilenameJWK) -} - -func testTrustedClientKeysFile(t *testing.T, trustedClientKeysFilename string) { - clientKey1, err := GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } - - err = AddKeySetFile(trustedClientKeysFilename, clientKey1.PublicKey()) - if err != nil { - t.Fatal(err) - } - - trustedClientKeys, err := LoadKeySetFile(trustedClientKeysFilename) - if err != nil { - t.Fatal(err) - } - - for _, clientKey := range trustedClientKeys { - t.Logf("Client Key: %s\n", clientKey) - } - - clientKey2, err := GenerateRSA2048PrivateKey() - if err != nil { - t.Fatal(err) - } - - err = AddKeySetFile(trustedClientKeysFilename, clientKey2.PublicKey()) - if err != nil { - t.Fatal(err) - } - - trustedClientKeys, err = LoadKeySetFile(trustedClientKeysFilename) - if err != nil { - t.Fatal(err) - } - - for _, clientKey := range trustedClientKeys { - t.Logf("Client Key: %s\n", clientKey) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/key_manager.go b/Godeps/_workspace/src/github.com/docker/libtrust/key_manager.go deleted file mode 100644 index 9a98ae357..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/key_manager.go +++ /dev/null @@ -1,175 +0,0 @@ -package libtrust - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "io/ioutil" - "net" - "os" - "path" - "sync" -) - -// ClientKeyManager manages client keys on the filesystem -type ClientKeyManager struct { - key PrivateKey - clientFile string - clientDir string - - clientLock sync.RWMutex - clients []PublicKey - - configLock sync.Mutex - configs []*tls.Config -} - -// NewClientKeyManager loads a new manager from a set of key files -// and managed by the given private key. -func NewClientKeyManager(trustKey PrivateKey, clientFile, clientDir string) (*ClientKeyManager, error) { - m := &ClientKeyManager{ - key: trustKey, - clientFile: clientFile, - clientDir: clientDir, - } - if err := m.loadKeys(); err != nil { - return nil, err - } - // TODO Start watching file and directory - - return m, nil -} - -func (c *ClientKeyManager) loadKeys() (err error) { - // Load authorized keys file - var clients []PublicKey - if c.clientFile != "" { - clients, err = LoadKeySetFile(c.clientFile) - if err != nil { - return fmt.Errorf("unable to load authorized keys: %s", err) - } - } - - // Add clients from authorized keys directory - files, err := ioutil.ReadDir(c.clientDir) - if err != nil && !os.IsNotExist(err) { - return fmt.Errorf("unable to open authorized keys directory: %s", err) - } - for _, f := range files { - if !f.IsDir() { - publicKey, err := LoadPublicKeyFile(path.Join(c.clientDir, f.Name())) - if err != nil { - return fmt.Errorf("unable to load authorized key file: %s", err) - } - clients = append(clients, publicKey) - } - } - - c.clientLock.Lock() - c.clients = clients - c.clientLock.Unlock() - - return nil -} - -// RegisterTLSConfig registers a tls configuration to manager -// such that any changes to the keys may be reflected in -// the tls client CA pool -func (c *ClientKeyManager) RegisterTLSConfig(tlsConfig *tls.Config) error { - c.clientLock.RLock() - certPool, err := GenerateCACertPool(c.key, c.clients) - if err != nil { - return fmt.Errorf("CA pool generation error: %s", err) - } - c.clientLock.RUnlock() - - tlsConfig.ClientCAs = certPool - - c.configLock.Lock() - c.configs = append(c.configs, tlsConfig) - c.configLock.Unlock() - - return nil -} - -// NewIdentityAuthTLSConfig creates a tls.Config for the server to use for -// libtrust identity authentication for the domain specified -func NewIdentityAuthTLSConfig(trustKey PrivateKey, clients *ClientKeyManager, addr string, domain string) (*tls.Config, error) { - tlsConfig := newTLSConfig() - - tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert - if err := clients.RegisterTLSConfig(tlsConfig); err != nil { - return nil, err - } - - // Generate cert - ips, domains, err := parseAddr(addr) - if err != nil { - return nil, err - } - // add domain that it expects clients to use - domains = append(domains, domain) - x509Cert, err := GenerateSelfSignedServerCert(trustKey, domains, ips) - if err != nil { - return nil, fmt.Errorf("certificate generation error: %s", err) - } - tlsConfig.Certificates = []tls.Certificate{{ - Certificate: [][]byte{x509Cert.Raw}, - PrivateKey: trustKey.CryptoPrivateKey(), - Leaf: x509Cert, - }} - - return tlsConfig, nil -} - -// NewCertAuthTLSConfig creates a tls.Config for the server to use for -// certificate authentication -func NewCertAuthTLSConfig(caPath, certPath, keyPath string) (*tls.Config, error) { - tlsConfig := newTLSConfig() - - cert, err := tls.LoadX509KeyPair(certPath, keyPath) - if err != nil { - return nil, fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?", certPath, keyPath, err) - } - tlsConfig.Certificates = []tls.Certificate{cert} - - // Verify client certificates against a CA? - if caPath != "" { - certPool := x509.NewCertPool() - file, err := ioutil.ReadFile(caPath) - if err != nil { - return nil, fmt.Errorf("Couldn't read CA certificate: %s", err) - } - certPool.AppendCertsFromPEM(file) - - tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert - tlsConfig.ClientCAs = certPool - } - - return tlsConfig, nil -} - -func newTLSConfig() *tls.Config { - return &tls.Config{ - NextProtos: []string{"http/1.1"}, - // Avoid fallback on insecure SSL protocols - MinVersion: tls.VersionTLS10, - } -} - -// parseAddr parses an address into an array of IPs and domains -func parseAddr(addr string) ([]net.IP, []string, error) { - host, _, err := net.SplitHostPort(addr) - if err != nil { - return nil, nil, err - } - var domains []string - var ips []net.IP - ip := net.ParseIP(host) - if ip != nil { - ips = []net.IP{ip} - } else { - domains = []string{host} - } - return ips, domains, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/key_test.go b/Godeps/_workspace/src/github.com/docker/libtrust/key_test.go deleted file mode 100644 index f6c59cc42..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/key_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package libtrust - -import ( - "testing" -) - -type generateFunc func() (PrivateKey, error) - -func runGenerateBench(b *testing.B, f generateFunc, name string) { - for i := 0; i < b.N; i++ { - _, err := f() - if err != nil { - b.Fatalf("Error generating %s: %s", name, err) - } - } -} - -func runFingerprintBench(b *testing.B, f generateFunc, name string) { - b.StopTimer() - // Don't count this relatively slow generation call. - key, err := f() - if err != nil { - b.Fatalf("Error generating %s: %s", name, err) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - if key.KeyID() == "" { - b.Fatalf("Error generating key ID for %s", name) - } - } -} - -func BenchmarkECP256Generate(b *testing.B) { - runGenerateBench(b, GenerateECP256PrivateKey, "P256") -} - -func BenchmarkECP384Generate(b *testing.B) { - runGenerateBench(b, GenerateECP384PrivateKey, "P384") -} - -func BenchmarkECP521Generate(b *testing.B) { - runGenerateBench(b, GenerateECP521PrivateKey, "P521") -} - -func BenchmarkRSA2048Generate(b *testing.B) { - runGenerateBench(b, GenerateRSA2048PrivateKey, "RSA2048") -} - -func BenchmarkRSA3072Generate(b *testing.B) { - runGenerateBench(b, GenerateRSA3072PrivateKey, "RSA3072") -} - -func BenchmarkRSA4096Generate(b *testing.B) { - runGenerateBench(b, GenerateRSA4096PrivateKey, "RSA4096") -} - -func BenchmarkECP256Fingerprint(b *testing.B) { - runFingerprintBench(b, GenerateECP256PrivateKey, "P256") -} - -func BenchmarkECP384Fingerprint(b *testing.B) { - runFingerprintBench(b, GenerateECP384PrivateKey, "P384") -} - -func BenchmarkECP521Fingerprint(b *testing.B) { - runFingerprintBench(b, GenerateECP521PrivateKey, "P521") -} - -func BenchmarkRSA2048Fingerprint(b *testing.B) { - runFingerprintBench(b, GenerateRSA2048PrivateKey, "RSA2048") -} - -func BenchmarkRSA3072Fingerprint(b *testing.B) { - runFingerprintBench(b, GenerateRSA3072PrivateKey, "RSA3072") -} - -func BenchmarkRSA4096Fingerprint(b *testing.B) { - runFingerprintBench(b, GenerateRSA4096PrivateKey, "RSA4096") -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/rsa_key.go b/Godeps/_workspace/src/github.com/docker/libtrust/rsa_key.go deleted file mode 100644 index dac4cacf2..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/rsa_key.go +++ /dev/null @@ -1,427 +0,0 @@ -package libtrust - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/json" - "encoding/pem" - "errors" - "fmt" - "io" - "math/big" -) - -/* - * RSA DSA PUBLIC KEY - */ - -// rsaPublicKey implements a JWK Public Key using RSA digital signature algorithms. -type rsaPublicKey struct { - *rsa.PublicKey - extended map[string]interface{} -} - -func fromRSAPublicKey(cryptoPublicKey *rsa.PublicKey) *rsaPublicKey { - return &rsaPublicKey{cryptoPublicKey, map[string]interface{}{}} -} - -// KeyType returns the JWK key type for RSA keys, i.e., "RSA". -func (k *rsaPublicKey) KeyType() string { - return "RSA" -} - -// KeyID returns a distinct identifier which is unique to this Public Key. -func (k *rsaPublicKey) KeyID() string { - return keyIDFromCryptoKey(k) -} - -func (k *rsaPublicKey) String() string { - return fmt.Sprintf("RSA Public Key <%s>", k.KeyID()) -} - -// Verify verifyies the signature of the data in the io.Reader using this Public Key. -// The alg parameter should be the name of the JWA digital signature algorithm -// which was used to produce the signature and should be supported by this -// public key. Returns a nil error if the signature is valid. -func (k *rsaPublicKey) Verify(data io.Reader, alg string, signature []byte) error { - // Verify the signature of the given date, return non-nil error if valid. - sigAlg, err := rsaSignatureAlgorithmByName(alg) - if err != nil { - return fmt.Errorf("unable to verify Signature: %s", err) - } - - hasher := sigAlg.HashID().New() - _, err = io.Copy(hasher, data) - if err != nil { - return fmt.Errorf("error reading data to sign: %s", err) - } - hash := hasher.Sum(nil) - - err = rsa.VerifyPKCS1v15(k.PublicKey, sigAlg.HashID(), hash, signature) - if err != nil { - return fmt.Errorf("invalid %s signature: %s", sigAlg.HeaderParam(), err) - } - - return nil -} - -// CryptoPublicKey returns the internal object which can be used as a -// crypto.PublicKey for use with other standard library operations. The type -// is either *rsa.PublicKey or *ecdsa.PublicKey -func (k *rsaPublicKey) CryptoPublicKey() crypto.PublicKey { - return k.PublicKey -} - -func (k *rsaPublicKey) toMap() map[string]interface{} { - jwk := make(map[string]interface{}) - for k, v := range k.extended { - jwk[k] = v - } - jwk["kty"] = k.KeyType() - jwk["kid"] = k.KeyID() - jwk["n"] = joseBase64UrlEncode(k.N.Bytes()) - jwk["e"] = joseBase64UrlEncode(serializeRSAPublicExponentParam(k.E)) - - return jwk -} - -// MarshalJSON serializes this Public Key using the JWK JSON serialization format for -// RSA keys. -func (k *rsaPublicKey) MarshalJSON() (data []byte, err error) { - return json.Marshal(k.toMap()) -} - -// PEMBlock serializes this Public Key to DER-encoded PKIX format. -func (k *rsaPublicKey) PEMBlock() (*pem.Block, error) { - derBytes, err := x509.MarshalPKIXPublicKey(k.PublicKey) - if err != nil { - return nil, fmt.Errorf("unable to serialize RSA PublicKey to DER-encoded PKIX format: %s", err) - } - k.extended["kid"] = k.KeyID() // For display purposes. - return createPemBlock("PUBLIC KEY", derBytes, k.extended) -} - -func (k *rsaPublicKey) AddExtendedField(field string, value interface{}) { - k.extended[field] = value -} - -func (k *rsaPublicKey) GetExtendedField(field string) interface{} { - v, ok := k.extended[field] - if !ok { - return nil - } - return v -} - -func rsaPublicKeyFromMap(jwk map[string]interface{}) (*rsaPublicKey, error) { - // JWK key type (kty) has already been determined to be "RSA". - // Need to extract 'n', 'e', and 'kid' and check for - // consistency. - - // Get the modulus parameter N. - nB64Url, err := stringFromMap(jwk, "n") - if err != nil { - return nil, fmt.Errorf("JWK RSA Public Key modulus: %s", err) - } - - n, err := parseRSAModulusParam(nB64Url) - if err != nil { - return nil, fmt.Errorf("JWK RSA Public Key modulus: %s", err) - } - - // Get the public exponent E. - eB64Url, err := stringFromMap(jwk, "e") - if err != nil { - return nil, fmt.Errorf("JWK RSA Public Key exponent: %s", err) - } - - e, err := parseRSAPublicExponentParam(eB64Url) - if err != nil { - return nil, fmt.Errorf("JWK RSA Public Key exponent: %s", err) - } - - key := &rsaPublicKey{ - PublicKey: &rsa.PublicKey{N: n, E: e}, - } - - // Key ID is optional, but if it exists, it should match the key. - _, ok := jwk["kid"] - if ok { - kid, err := stringFromMap(jwk, "kid") - if err != nil { - return nil, fmt.Errorf("JWK RSA Public Key ID: %s", err) - } - if kid != key.KeyID() { - return nil, fmt.Errorf("JWK RSA Public Key ID does not match: %s", kid) - } - } - - if _, ok := jwk["d"]; ok { - return nil, fmt.Errorf("JWK RSA Public Key cannot contain private exponent") - } - - key.extended = jwk - - return key, nil -} - -/* - * RSA DSA PRIVATE KEY - */ - -// rsaPrivateKey implements a JWK Private Key using RSA digital signature algorithms. -type rsaPrivateKey struct { - rsaPublicKey - *rsa.PrivateKey -} - -func fromRSAPrivateKey(cryptoPrivateKey *rsa.PrivateKey) *rsaPrivateKey { - return &rsaPrivateKey{ - *fromRSAPublicKey(&cryptoPrivateKey.PublicKey), - cryptoPrivateKey, - } -} - -// PublicKey returns the Public Key data associated with this Private Key. -func (k *rsaPrivateKey) PublicKey() PublicKey { - return &k.rsaPublicKey -} - -func (k *rsaPrivateKey) String() string { - return fmt.Sprintf("RSA Private Key <%s>", k.KeyID()) -} - -// Sign signs the data read from the io.Reader using a signature algorithm supported -// by the RSA private key. If the specified hashing algorithm is supported by -// this key, that hash function is used to generate the signature otherwise the -// the default hashing algorithm for this key is used. Returns the signature -// and the name of the JWK signature algorithm used, e.g., "RS256", "RS384", -// "RS512". -func (k *rsaPrivateKey) Sign(data io.Reader, hashID crypto.Hash) (signature []byte, alg string, err error) { - // Generate a signature of the data using the internal alg. - sigAlg := rsaPKCS1v15SignatureAlgorithmForHashID(hashID) - hasher := sigAlg.HashID().New() - - _, err = io.Copy(hasher, data) - if err != nil { - return nil, "", fmt.Errorf("error reading data to sign: %s", err) - } - hash := hasher.Sum(nil) - - signature, err = rsa.SignPKCS1v15(rand.Reader, k.PrivateKey, sigAlg.HashID(), hash) - if err != nil { - return nil, "", fmt.Errorf("error producing signature: %s", err) - } - - alg = sigAlg.HeaderParam() - - return -} - -// CryptoPrivateKey returns the internal object which can be used as a -// crypto.PublicKey for use with other standard library operations. The type -// is either *rsa.PublicKey or *ecdsa.PublicKey -func (k *rsaPrivateKey) CryptoPrivateKey() crypto.PrivateKey { - return k.PrivateKey -} - -func (k *rsaPrivateKey) toMap() map[string]interface{} { - k.Precompute() // Make sure the precomputed values are stored. - jwk := k.rsaPublicKey.toMap() - - jwk["d"] = joseBase64UrlEncode(k.D.Bytes()) - jwk["p"] = joseBase64UrlEncode(k.Primes[0].Bytes()) - jwk["q"] = joseBase64UrlEncode(k.Primes[1].Bytes()) - jwk["dp"] = joseBase64UrlEncode(k.Precomputed.Dp.Bytes()) - jwk["dq"] = joseBase64UrlEncode(k.Precomputed.Dq.Bytes()) - jwk["qi"] = joseBase64UrlEncode(k.Precomputed.Qinv.Bytes()) - - otherPrimes := k.Primes[2:] - - if len(otherPrimes) > 0 { - otherPrimesInfo := make([]interface{}, len(otherPrimes)) - for i, r := range otherPrimes { - otherPrimeInfo := make(map[string]string, 3) - otherPrimeInfo["r"] = joseBase64UrlEncode(r.Bytes()) - crtVal := k.Precomputed.CRTValues[i] - otherPrimeInfo["d"] = joseBase64UrlEncode(crtVal.Exp.Bytes()) - otherPrimeInfo["t"] = joseBase64UrlEncode(crtVal.Coeff.Bytes()) - otherPrimesInfo[i] = otherPrimeInfo - } - jwk["oth"] = otherPrimesInfo - } - - return jwk -} - -// MarshalJSON serializes this Private Key using the JWK JSON serialization format for -// RSA keys. -func (k *rsaPrivateKey) MarshalJSON() (data []byte, err error) { - return json.Marshal(k.toMap()) -} - -// PEMBlock serializes this Private Key to DER-encoded PKIX format. -func (k *rsaPrivateKey) PEMBlock() (*pem.Block, error) { - derBytes := x509.MarshalPKCS1PrivateKey(k.PrivateKey) - k.extended["keyID"] = k.KeyID() // For display purposes. - return createPemBlock("RSA PRIVATE KEY", derBytes, k.extended) -} - -func rsaPrivateKeyFromMap(jwk map[string]interface{}) (*rsaPrivateKey, error) { - // The JWA spec for RSA Private Keys (draft rfc section 5.3.2) states that - // only the private key exponent 'd' is REQUIRED, the others are just for - // signature/decryption optimizations and SHOULD be included when the JWK - // is produced. We MAY choose to accept a JWK which only includes 'd', but - // we're going to go ahead and not choose to accept it without the extra - // fields. Only the 'oth' field will be optional (for multi-prime keys). - privateExponent, err := parseRSAPrivateKeyParamFromMap(jwk, "d") - if err != nil { - return nil, fmt.Errorf("JWK RSA Private Key exponent: %s", err) - } - firstPrimeFactor, err := parseRSAPrivateKeyParamFromMap(jwk, "p") - if err != nil { - return nil, fmt.Errorf("JWK RSA Private Key prime factor: %s", err) - } - secondPrimeFactor, err := parseRSAPrivateKeyParamFromMap(jwk, "q") - if err != nil { - return nil, fmt.Errorf("JWK RSA Private Key prime factor: %s", err) - } - firstFactorCRT, err := parseRSAPrivateKeyParamFromMap(jwk, "dp") - if err != nil { - return nil, fmt.Errorf("JWK RSA Private Key CRT exponent: %s", err) - } - secondFactorCRT, err := parseRSAPrivateKeyParamFromMap(jwk, "dq") - if err != nil { - return nil, fmt.Errorf("JWK RSA Private Key CRT exponent: %s", err) - } - crtCoeff, err := parseRSAPrivateKeyParamFromMap(jwk, "qi") - if err != nil { - return nil, fmt.Errorf("JWK RSA Private Key CRT coefficient: %s", err) - } - - var oth interface{} - if _, ok := jwk["oth"]; ok { - oth = jwk["oth"] - delete(jwk, "oth") - } - - // JWK key type (kty) has already been determined to be "RSA". - // Need to extract the public key information, then extract the private - // key values. - publicKey, err := rsaPublicKeyFromMap(jwk) - if err != nil { - return nil, err - } - - privateKey := &rsa.PrivateKey{ - PublicKey: *publicKey.PublicKey, - D: privateExponent, - Primes: []*big.Int{firstPrimeFactor, secondPrimeFactor}, - Precomputed: rsa.PrecomputedValues{ - Dp: firstFactorCRT, - Dq: secondFactorCRT, - Qinv: crtCoeff, - }, - } - - if oth != nil { - // Should be an array of more JSON objects. - otherPrimesInfo, ok := oth.([]interface{}) - if !ok { - return nil, errors.New("JWK RSA Private Key: Invalid other primes info: must be an array") - } - numOtherPrimeFactors := len(otherPrimesInfo) - if numOtherPrimeFactors == 0 { - return nil, errors.New("JWK RSA Privake Key: Invalid other primes info: must be absent or non-empty") - } - otherPrimeFactors := make([]*big.Int, numOtherPrimeFactors) - productOfPrimes := new(big.Int).Mul(firstPrimeFactor, secondPrimeFactor) - crtValues := make([]rsa.CRTValue, numOtherPrimeFactors) - - for i, val := range otherPrimesInfo { - otherPrimeinfo, ok := val.(map[string]interface{}) - if !ok { - return nil, errors.New("JWK RSA Private Key: Invalid other prime info: must be a JSON object") - } - - otherPrimeFactor, err := parseRSAPrivateKeyParamFromMap(otherPrimeinfo, "r") - if err != nil { - return nil, fmt.Errorf("JWK RSA Private Key prime factor: %s", err) - } - otherFactorCRT, err := parseRSAPrivateKeyParamFromMap(otherPrimeinfo, "d") - if err != nil { - return nil, fmt.Errorf("JWK RSA Private Key CRT exponent: %s", err) - } - otherCrtCoeff, err := parseRSAPrivateKeyParamFromMap(otherPrimeinfo, "t") - if err != nil { - return nil, fmt.Errorf("JWK RSA Private Key CRT coefficient: %s", err) - } - - crtValue := crtValues[i] - crtValue.Exp = otherFactorCRT - crtValue.Coeff = otherCrtCoeff - crtValue.R = productOfPrimes - otherPrimeFactors[i] = otherPrimeFactor - productOfPrimes = new(big.Int).Mul(productOfPrimes, otherPrimeFactor) - } - - privateKey.Primes = append(privateKey.Primes, otherPrimeFactors...) - privateKey.Precomputed.CRTValues = crtValues - } - - key := &rsaPrivateKey{ - rsaPublicKey: *publicKey, - PrivateKey: privateKey, - } - - return key, nil -} - -/* - * Key Generation Functions. - */ - -func generateRSAPrivateKey(bits int) (k *rsaPrivateKey, err error) { - k = new(rsaPrivateKey) - k.PrivateKey, err = rsa.GenerateKey(rand.Reader, bits) - if err != nil { - return nil, err - } - - k.rsaPublicKey.PublicKey = &k.PrivateKey.PublicKey - k.extended = make(map[string]interface{}) - - return -} - -// GenerateRSA2048PrivateKey generates a key pair using 2048-bit RSA. -func GenerateRSA2048PrivateKey() (PrivateKey, error) { - k, err := generateRSAPrivateKey(2048) - if err != nil { - return nil, fmt.Errorf("error generating RSA 2048-bit key: %s", err) - } - - return k, nil -} - -// GenerateRSA3072PrivateKey generates a key pair using 3072-bit RSA. -func GenerateRSA3072PrivateKey() (PrivateKey, error) { - k, err := generateRSAPrivateKey(3072) - if err != nil { - return nil, fmt.Errorf("error generating RSA 3072-bit key: %s", err) - } - - return k, nil -} - -// GenerateRSA4096PrivateKey generates a key pair using 4096-bit RSA. -func GenerateRSA4096PrivateKey() (PrivateKey, error) { - k, err := generateRSAPrivateKey(4096) - if err != nil { - return nil, fmt.Errorf("error generating RSA 4096-bit key: %s", err) - } - - return k, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/rsa_key_test.go b/Godeps/_workspace/src/github.com/docker/libtrust/rsa_key_test.go deleted file mode 100644 index 5ec7707aa..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/rsa_key_test.go +++ /dev/null @@ -1,157 +0,0 @@ -package libtrust - -import ( - "bytes" - "encoding/json" - "log" - "testing" -) - -var rsaKeys []PrivateKey - -func init() { - var err error - rsaKeys, err = generateRSATestKeys() - if err != nil { - log.Fatal(err) - } -} - -func generateRSATestKeys() (keys []PrivateKey, err error) { - log.Println("Generating RSA 2048-bit Test Key") - rsa2048Key, err := GenerateRSA2048PrivateKey() - if err != nil { - return - } - - log.Println("Generating RSA 3072-bit Test Key") - rsa3072Key, err := GenerateRSA3072PrivateKey() - if err != nil { - return - } - - log.Println("Generating RSA 4096-bit Test Key") - rsa4096Key, err := GenerateRSA4096PrivateKey() - if err != nil { - return - } - - log.Println("Done generating RSA Test Keys!") - keys = []PrivateKey{rsa2048Key, rsa3072Key, rsa4096Key} - - return -} - -func TestRSAKeys(t *testing.T) { - for _, rsaKey := range rsaKeys { - if rsaKey.KeyType() != "RSA" { - t.Fatalf("key type must be %q, instead got %q", "RSA", rsaKey.KeyType()) - } - } -} - -func TestRSASignVerify(t *testing.T) { - message := "Hello, World!" - data := bytes.NewReader([]byte(message)) - - sigAlgs := []*signatureAlgorithm{rs256, rs384, rs512} - - for i, rsaKey := range rsaKeys { - sigAlg := sigAlgs[i] - - t.Logf("%s signature of %q with kid: %s\n", sigAlg.HeaderParam(), message, rsaKey.KeyID()) - - data.Seek(0, 0) // Reset the byte reader - - // Sign - sig, alg, err := rsaKey.Sign(data, sigAlg.HashID()) - if err != nil { - t.Fatal(err) - } - - data.Seek(0, 0) // Reset the byte reader - - // Verify - err = rsaKey.Verify(data, alg, sig) - if err != nil { - t.Fatal(err) - } - } -} - -func TestMarshalUnmarshalRSAKeys(t *testing.T) { - data := bytes.NewReader([]byte("This is a test. I repeat: this is only a test.")) - sigAlgs := []*signatureAlgorithm{rs256, rs384, rs512} - - for i, rsaKey := range rsaKeys { - sigAlg := sigAlgs[i] - privateJWKJSON, err := json.MarshalIndent(rsaKey, "", " ") - if err != nil { - t.Fatal(err) - } - - publicJWKJSON, err := json.MarshalIndent(rsaKey.PublicKey(), "", " ") - if err != nil { - t.Fatal(err) - } - - t.Logf("JWK Private Key: %s", string(privateJWKJSON)) - t.Logf("JWK Public Key: %s", string(publicJWKJSON)) - - privKey2, err := UnmarshalPrivateKeyJWK(privateJWKJSON) - if err != nil { - t.Fatal(err) - } - - pubKey2, err := UnmarshalPublicKeyJWK(publicJWKJSON) - if err != nil { - t.Fatal(err) - } - - // Ensure we can sign/verify a message with the unmarshalled keys. - data.Seek(0, 0) // Reset the byte reader - signature, alg, err := privKey2.Sign(data, sigAlg.HashID()) - if err != nil { - t.Fatal(err) - } - - data.Seek(0, 0) // Reset the byte reader - err = pubKey2.Verify(data, alg, signature) - if err != nil { - t.Fatal(err) - } - - // It's a good idea to validate the Private Key to make sure our - // (un)marshal process didn't corrupt the extra parameters. - k := privKey2.(*rsaPrivateKey) - err = k.PrivateKey.Validate() - if err != nil { - t.Fatal(err) - } - } -} - -func TestFromCryptoRSAKeys(t *testing.T) { - for _, rsaKey := range rsaKeys { - cryptoPrivateKey := rsaKey.CryptoPrivateKey() - cryptoPublicKey := rsaKey.CryptoPublicKey() - - pubKey, err := FromCryptoPublicKey(cryptoPublicKey) - if err != nil { - t.Fatal(err) - } - - if pubKey.KeyID() != rsaKey.KeyID() { - t.Fatal("public key key ID mismatch") - } - - privKey, err := FromCryptoPrivateKey(cryptoPrivateKey) - if err != nil { - t.Fatal(err) - } - - if privKey.KeyID() != rsaKey.KeyID() { - t.Fatal("public key key ID mismatch") - } - } -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/testutil/certificates.go b/Godeps/_workspace/src/github.com/docker/libtrust/testutil/certificates.go deleted file mode 100644 index 89debf6b6..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/testutil/certificates.go +++ /dev/null @@ -1,94 +0,0 @@ -package testutil - -import ( - "crypto" - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "math/big" - "time" -) - -// GenerateTrustCA generates a new certificate authority for testing. -func GenerateTrustCA(pub crypto.PublicKey, priv crypto.PrivateKey) (*x509.Certificate, error) { - cert := &x509.Certificate{ - SerialNumber: big.NewInt(0), - Subject: pkix.Name{ - CommonName: "CA Root", - }, - NotBefore: time.Now().Add(-time.Second), - NotAfter: time.Now().Add(time.Hour), - IsCA: true, - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - } - - certDER, err := x509.CreateCertificate(rand.Reader, cert, cert, pub, priv) - if err != nil { - return nil, err - } - - cert, err = x509.ParseCertificate(certDER) - if err != nil { - return nil, err - } - - return cert, nil -} - -// GenerateIntermediate generates an intermediate certificate for testing using -// the parent certificate (likely a CA) and the provided keys. -func GenerateIntermediate(key crypto.PublicKey, parentKey crypto.PrivateKey, parent *x509.Certificate) (*x509.Certificate, error) { - cert := &x509.Certificate{ - SerialNumber: big.NewInt(0), - Subject: pkix.Name{ - CommonName: "Intermediate", - }, - NotBefore: time.Now().Add(-time.Second), - NotAfter: time.Now().Add(time.Hour), - IsCA: true, - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - } - - certDER, err := x509.CreateCertificate(rand.Reader, cert, parent, key, parentKey) - if err != nil { - return nil, err - } - - cert, err = x509.ParseCertificate(certDER) - if err != nil { - return nil, err - } - - return cert, nil -} - -// GenerateTrustCert generates a new trust certificate for testing. Unlike the -// intermediate certificates, this certificate should be used for signature -// only, not creating certificates. -func GenerateTrustCert(key crypto.PublicKey, parentKey crypto.PrivateKey, parent *x509.Certificate) (*x509.Certificate, error) { - cert := &x509.Certificate{ - SerialNumber: big.NewInt(0), - Subject: pkix.Name{ - CommonName: "Trust Cert", - }, - NotBefore: time.Now().Add(-time.Second), - NotAfter: time.Now().Add(time.Hour), - IsCA: true, - KeyUsage: x509.KeyUsageDigitalSignature, - BasicConstraintsValid: true, - } - - certDER, err := x509.CreateCertificate(rand.Reader, cert, parent, key, parentKey) - if err != nil { - return nil, err - } - - cert, err = x509.ParseCertificate(certDER) - if err != nil { - return nil, err - } - - return cert, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/README.md b/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/README.md deleted file mode 100644 index 24124db21..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/README.md +++ /dev/null @@ -1,50 +0,0 @@ -## Libtrust TLS Config Demo - -This program generates key pairs and trust files for a TLS client and server. - -To generate the keys, run: - -``` -$ go run genkeys.go -``` - -The generated files are: - -``` -$ ls -l client_data/ server_data/ -client_data/: -total 24 --rw------- 1 jlhawn staff 281 Aug 8 16:21 private_key.json --rw-r--r-- 1 jlhawn staff 225 Aug 8 16:21 public_key.json --rw-r--r-- 1 jlhawn staff 275 Aug 8 16:21 trusted_hosts.json - -server_data/: -total 24 --rw-r--r-- 1 jlhawn staff 348 Aug 8 16:21 trusted_clients.json --rw------- 1 jlhawn staff 281 Aug 8 16:21 private_key.json --rw-r--r-- 1 jlhawn staff 225 Aug 8 16:21 public_key.json -``` - -The private key and public key for the client and server are stored in `private_key.json` and `public_key.json`, respectively, and in their respective directories. They are represented as JSON Web Keys: JSON objects which represent either an ECDSA or RSA private key. The host keys trusted by the client are stored in `trusted_hosts.json` and contain a mapping of an internet address, `:`, to a JSON Web Key which is a JSON object representing either an ECDSA or RSA public key of the trusted server. The client keys trusted by the server are stored in `trusted_clients.json` and contain an array of JSON objects which contain a comment field which can be used describe the key and a JSON Web Key which is a JSON object representing either an ECDSA or RSA public key of the trusted client. - -To start the server, run: - -``` -$ go run server.go -``` - -This starts an HTTPS server which listens on `localhost:8888`. The server configures itself with a certificate which is valid for both `localhost` and `127.0.0.1` and uses the key from `server_data/private_key.json`. It accepts connections from clients which present a certificate for a key that it is configured to trust from the `trusted_clients.json` file and returns a simple 'hello' message. - -To make a request using the client, run: - -``` -$ go run client.go -``` - -This command creates an HTTPS client which makes a GET request to `https://localhost:8888`. The client configures itself with a certificate using the key from `client_data/private_key.json`. It only connects to a server which presents a certificate signed by the key specified for the `localhost:8888` address from `client_data/trusted_hosts.json` and made to be used for the `localhost` hostname. If the connection succeeds, it prints the response from the server. - -The file `gencert.go` can be used to generate PEM encoded version of the client key and certificate. If you save them to `key.pem` and `cert.pem` respectively, you can use them with `curl` to test out the server (if it is still running). - -``` -curl --cert cert.pem --key key.pem -k https://localhost:8888 -``` diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/client.go b/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/client.go deleted file mode 100644 index 0a699a0ee..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/client.go +++ /dev/null @@ -1,89 +0,0 @@ -package main - -import ( - "crypto/tls" - "fmt" - "io/ioutil" - "log" - "net" - "net/http" - - "github.com/docker/libtrust" -) - -var ( - serverAddress = "localhost:8888" - privateKeyFilename = "client_data/private_key.pem" - trustedHostsFilename = "client_data/trusted_hosts.pem" -) - -func main() { - // Load Client Key. - clientKey, err := libtrust.LoadKeyFile(privateKeyFilename) - if err != nil { - log.Fatal(err) - } - - // Generate Client Certificate. - selfSignedClientCert, err := libtrust.GenerateSelfSignedClientCert(clientKey) - if err != nil { - log.Fatal(err) - } - - // Load trusted host keys. - hostKeys, err := libtrust.LoadKeySetFile(trustedHostsFilename) - if err != nil { - log.Fatal(err) - } - - // Ensure the host we want to connect to is trusted! - host, _, err := net.SplitHostPort(serverAddress) - if err != nil { - log.Fatal(err) - } - serverKeys, err := libtrust.FilterByHosts(hostKeys, host, false) - if err != nil { - log.Fatalf("%q is not a known and trusted host", host) - } - - // Generate a CA pool with the trusted host's key. - caPool, err := libtrust.GenerateCACertPool(clientKey, serverKeys) - if err != nil { - log.Fatal(err) - } - - // Create HTTP Client. - client := &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - Certificates: []tls.Certificate{ - tls.Certificate{ - Certificate: [][]byte{selfSignedClientCert.Raw}, - PrivateKey: clientKey.CryptoPrivateKey(), - Leaf: selfSignedClientCert, - }, - }, - RootCAs: caPool, - }, - }, - } - - var makeRequest = func(url string) { - resp, err := client.Get(url) - if err != nil { - log.Fatal(err) - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Fatal(err) - } - - log.Println(resp.Status) - log.Println(string(body)) - } - - // Make the request to the trusted server! - makeRequest(fmt.Sprintf("https://%s", serverAddress)) -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/gencert.go b/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/gencert.go deleted file mode 100644 index c65f3b6b4..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/gencert.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "encoding/pem" - "fmt" - "log" - "net" - - "github.com/docker/libtrust" -) - -var ( - serverAddress = "localhost:8888" - clientPrivateKeyFilename = "client_data/private_key.pem" - trustedHostsFilename = "client_data/trusted_hosts.pem" -) - -func main() { - key, err := libtrust.LoadKeyFile(clientPrivateKeyFilename) - if err != nil { - log.Fatal(err) - } - - keyPEMBlock, err := key.PEMBlock() - if err != nil { - log.Fatal(err) - } - - encodedPrivKey := pem.EncodeToMemory(keyPEMBlock) - fmt.Printf("Client Key:\n\n%s\n", string(encodedPrivKey)) - - cert, err := libtrust.GenerateSelfSignedClientCert(key) - if err != nil { - log.Fatal(err) - } - - encodedCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) - fmt.Printf("Client Cert:\n\n%s\n", string(encodedCert)) - - trustedServerKeys, err := libtrust.LoadKeySetFile(trustedHostsFilename) - if err != nil { - log.Fatal(err) - } - - hostname, _, err := net.SplitHostPort(serverAddress) - if err != nil { - log.Fatal(err) - } - - trustedServerKeys, err = libtrust.FilterByHosts(trustedServerKeys, hostname, false) - if err != nil { - log.Fatal(err) - } - - caCert, err := libtrust.GenerateCACert(key, trustedServerKeys[0]) - if err != nil { - log.Fatal(err) - } - - encodedCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: caCert.Raw}) - fmt.Printf("CA Cert:\n\n%s\n", string(encodedCert)) -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/genkeys.go b/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/genkeys.go deleted file mode 100644 index 9dc8842ad..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/genkeys.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "log" - - "github.com/docker/libtrust" -) - -func main() { - // Generate client key. - clientKey, err := libtrust.GenerateECP256PrivateKey() - if err != nil { - log.Fatal(err) - } - - // Add a comment for the client key. - clientKey.AddExtendedField("comment", "TLS Demo Client") - - // Save the client key, public and private versions. - err = libtrust.SaveKey("client_data/private_key.pem", clientKey) - if err != nil { - log.Fatal(err) - } - - err = libtrust.SavePublicKey("client_data/public_key.pem", clientKey.PublicKey()) - if err != nil { - log.Fatal(err) - } - - // Generate server key. - serverKey, err := libtrust.GenerateECP256PrivateKey() - if err != nil { - log.Fatal(err) - } - - // Set the list of addresses to use for the server. - serverKey.AddExtendedField("hosts", []string{"localhost", "docker.example.com"}) - - // Save the server key, public and private versions. - err = libtrust.SaveKey("server_data/private_key.pem", serverKey) - if err != nil { - log.Fatal(err) - } - - err = libtrust.SavePublicKey("server_data/public_key.pem", serverKey.PublicKey()) - if err != nil { - log.Fatal(err) - } - - // Generate Authorized Keys file for server. - err = libtrust.AddKeySetFile("server_data/trusted_clients.pem", clientKey.PublicKey()) - if err != nil { - log.Fatal(err) - } - - // Generate Known Host Keys file for client. - err = libtrust.AddKeySetFile("client_data/trusted_hosts.pem", serverKey.PublicKey()) - if err != nil { - log.Fatal(err) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/server.go b/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/server.go deleted file mode 100644 index d3cb2ea91..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/tlsdemo/server.go +++ /dev/null @@ -1,80 +0,0 @@ -package main - -import ( - "crypto/tls" - "fmt" - "html" - "log" - "net" - "net/http" - - "github.com/docker/libtrust" -) - -var ( - serverAddress = "localhost:8888" - privateKeyFilename = "server_data/private_key.pem" - authorizedClientsFilename = "server_data/trusted_clients.pem" -) - -func requestHandler(w http.ResponseWriter, r *http.Request) { - clientCert := r.TLS.PeerCertificates[0] - keyID := clientCert.Subject.CommonName - log.Printf("Request from keyID: %s\n", keyID) - fmt.Fprintf(w, "Hello, client! I'm a server! And you are %T: %s.\n", clientCert.PublicKey, html.EscapeString(keyID)) -} - -func main() { - // Load server key. - serverKey, err := libtrust.LoadKeyFile(privateKeyFilename) - if err != nil { - log.Fatal(err) - } - - // Generate server certificate. - selfSignedServerCert, err := libtrust.GenerateSelfSignedServerCert( - serverKey, []string{"localhost"}, []net.IP{net.ParseIP("127.0.0.1")}, - ) - if err != nil { - log.Fatal(err) - } - - // Load authorized client keys. - authorizedClients, err := libtrust.LoadKeySetFile(authorizedClientsFilename) - if err != nil { - log.Fatal(err) - } - - // Create CA pool using trusted client keys. - caPool, err := libtrust.GenerateCACertPool(serverKey, authorizedClients) - if err != nil { - log.Fatal(err) - } - - // Create TLS config, requiring client certificates. - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{ - tls.Certificate{ - Certificate: [][]byte{selfSignedServerCert.Raw}, - PrivateKey: serverKey.CryptoPrivateKey(), - Leaf: selfSignedServerCert, - }, - }, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: caPool, - } - - // Create HTTP server with simple request handler. - server := &http.Server{ - Addr: serverAddress, - Handler: http.HandlerFunc(requestHandler), - } - - // Listen and server HTTPS using the libtrust TLS config. - listener, err := net.Listen("tcp", server.Addr) - if err != nil { - log.Fatal(err) - } - tlsListener := tls.NewListener(listener, tlsConfig) - server.Serve(tlsListener) -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/graph.go b/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/graph.go deleted file mode 100644 index 72b0fc366..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/graph.go +++ /dev/null @@ -1,50 +0,0 @@ -package trustgraph - -import "github.com/docker/libtrust" - -// TrustGraph represents a graph of authorization mapping -// public keys to nodes and grants between nodes. -type TrustGraph interface { - // Verifies that the given public key is allowed to perform - // the given action on the given node according to the trust - // graph. - Verify(libtrust.PublicKey, string, uint16) (bool, error) - - // GetGrants returns an array of all grant chains which are used to - // allow the requested permission. - GetGrants(libtrust.PublicKey, string, uint16) ([][]*Grant, error) -} - -// Grant represents a transfer of permission from one part of the -// trust graph to another. This is the only way to delegate -// permission between two different sub trees in the graph. -type Grant struct { - // Subject is the namespace being granted - Subject string - - // Permissions is a bit map of permissions - Permission uint16 - - // Grantee represents the node being granted - // a permission scope. The grantee can be - // either a namespace item or a key id where namespace - // items will always start with a '/'. - Grantee string - - // statement represents the statement used to create - // this object. - statement *Statement -} - -// Permissions -// Read node 0x01 (can read node, no sub nodes) -// Write node 0x02 (can write to node object, cannot create subnodes) -// Read subtree 0x04 (delegates read to each sub node) -// Write subtree 0x08 (delegates write to each sub node, included create on the subject) -// -// Permission shortcuts -// ReadItem = 0x01 -// WriteItem = 0x03 -// ReadAccess = 0x07 -// WriteAccess = 0x0F -// Delegate = 0x0F diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/memory_graph.go b/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/memory_graph.go deleted file mode 100644 index 247bfa7aa..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/memory_graph.go +++ /dev/null @@ -1,133 +0,0 @@ -package trustgraph - -import ( - "strings" - - "github.com/docker/libtrust" -) - -type grantNode struct { - grants []*Grant - children map[string]*grantNode -} - -type memoryGraph struct { - roots map[string]*grantNode -} - -func newGrantNode() *grantNode { - return &grantNode{ - grants: []*Grant{}, - children: map[string]*grantNode{}, - } -} - -// NewMemoryGraph returns a new in memory trust graph created from -// a static list of grants. This graph is immutable after creation -// and any alterations should create a new instance. -func NewMemoryGraph(grants []*Grant) TrustGraph { - roots := map[string]*grantNode{} - for _, grant := range grants { - parts := strings.Split(grant.Grantee, "/") - nodes := roots - var node *grantNode - var nodeOk bool - for _, part := range parts { - node, nodeOk = nodes[part] - if !nodeOk { - node = newGrantNode() - nodes[part] = node - } - if part != "" { - node.grants = append(node.grants, grant) - } - nodes = node.children - } - } - return &memoryGraph{roots} -} - -func (g *memoryGraph) getGrants(name string) []*Grant { - nameParts := strings.Split(name, "/") - nodes := g.roots - var node *grantNode - var nodeOk bool - for _, part := range nameParts { - node, nodeOk = nodes[part] - if !nodeOk { - return nil - } - nodes = node.children - } - return node.grants -} - -func isSubName(name, sub string) bool { - if strings.HasPrefix(name, sub) { - if len(name) == len(sub) || name[len(sub)] == '/' { - return true - } - } - return false -} - -type walkFunc func(*Grant, []*Grant) bool - -func foundWalkFunc(*Grant, []*Grant) bool { - return true -} - -func (g *memoryGraph) walkGrants(start, target string, permission uint16, f walkFunc, chain []*Grant, visited map[*Grant]bool, collect bool) bool { - if visited == nil { - visited = map[*Grant]bool{} - } - grants := g.getGrants(start) - subGrants := make([]*Grant, 0, len(grants)) - for _, grant := range grants { - if visited[grant] { - continue - } - visited[grant] = true - if grant.Permission&permission == permission { - if isSubName(target, grant.Subject) { - if f(grant, chain) { - return true - } - } else { - subGrants = append(subGrants, grant) - } - } - } - for _, grant := range subGrants { - var chainCopy []*Grant - if collect { - chainCopy = make([]*Grant, len(chain)+1) - copy(chainCopy, chain) - chainCopy[len(chainCopy)-1] = grant - } else { - chainCopy = nil - } - - if g.walkGrants(grant.Subject, target, permission, f, chainCopy, visited, collect) { - return true - } - } - return false -} - -func (g *memoryGraph) Verify(key libtrust.PublicKey, node string, permission uint16) (bool, error) { - return g.walkGrants(key.KeyID(), node, permission, foundWalkFunc, nil, nil, false), nil -} - -func (g *memoryGraph) GetGrants(key libtrust.PublicKey, node string, permission uint16) ([][]*Grant, error) { - grants := [][]*Grant{} - collect := func(grant *Grant, chain []*Grant) bool { - grantChain := make([]*Grant, len(chain)+1) - copy(grantChain, chain) - grantChain[len(grantChain)-1] = grant - grants = append(grants, grantChain) - return false - } - g.walkGrants(key.KeyID(), node, permission, collect, nil, nil, true) - return grants, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/memory_graph_test.go b/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/memory_graph_test.go deleted file mode 100644 index 49fd0f3b5..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/memory_graph_test.go +++ /dev/null @@ -1,174 +0,0 @@ -package trustgraph - -import ( - "fmt" - "testing" - - "github.com/docker/libtrust" -) - -func createTestKeysAndGrants(count int) ([]*Grant, []libtrust.PrivateKey) { - grants := make([]*Grant, count) - keys := make([]libtrust.PrivateKey, count) - for i := 0; i < count; i++ { - pk, err := libtrust.GenerateECP256PrivateKey() - if err != nil { - panic(err) - } - grant := &Grant{ - Subject: fmt.Sprintf("/user-%d", i+1), - Permission: 0x0f, - Grantee: pk.KeyID(), - } - keys[i] = pk - grants[i] = grant - } - return grants, keys -} - -func testVerified(t *testing.T, g TrustGraph, k libtrust.PublicKey, keyName, target string, permission uint16) { - if ok, err := g.Verify(k, target, permission); err != nil { - t.Fatalf("Unexpected error during verification: %s", err) - } else if !ok { - t.Errorf("key failed verification\n\tKey: %s(%s)\n\tNamespace: %s", keyName, k.KeyID(), target) - } -} - -func testNotVerified(t *testing.T, g TrustGraph, k libtrust.PublicKey, keyName, target string, permission uint16) { - if ok, err := g.Verify(k, target, permission); err != nil { - t.Fatalf("Unexpected error during verification: %s", err) - } else if ok { - t.Errorf("key should have failed verification\n\tKey: %s(%s)\n\tNamespace: %s", keyName, k.KeyID(), target) - } -} - -func TestVerify(t *testing.T) { - grants, keys := createTestKeysAndGrants(4) - extraGrants := make([]*Grant, 3) - extraGrants[0] = &Grant{ - Subject: "/user-3", - Permission: 0x0f, - Grantee: "/user-2", - } - extraGrants[1] = &Grant{ - Subject: "/user-3/sub-project", - Permission: 0x0f, - Grantee: "/user-4", - } - extraGrants[2] = &Grant{ - Subject: "/user-4", - Permission: 0x07, - Grantee: "/user-1", - } - grants = append(grants, extraGrants...) - - g := NewMemoryGraph(grants) - - testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f) - testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1/some-project/sub-value", 0x0f) - testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-4", 0x07) - testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2/", 0x0f) - testVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-3/sub-value", 0x0f) - testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3/sub-value", 0x0f) - testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3", 0x0f) - testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3/", 0x0f) - testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3/sub-project", 0x0f) - testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3/sub-project/app", 0x0f) - testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-4", 0x0f) - - testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-2", 0x0f) - testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-3/sub-value", 0x0f) - testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-4", 0x0f) - testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-1/", 0x0f) - testNotVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-2", 0x0f) - testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-4", 0x0f) - testNotVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3", 0x0f) -} - -func TestCircularWalk(t *testing.T) { - grants, keys := createTestKeysAndGrants(3) - user1Grant := &Grant{ - Subject: "/user-2", - Permission: 0x0f, - Grantee: "/user-1", - } - user2Grant := &Grant{ - Subject: "/user-1", - Permission: 0x0f, - Grantee: "/user-2", - } - grants = append(grants, user1Grant, user2Grant) - - g := NewMemoryGraph(grants) - - testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f) - testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-2", 0x0f) - testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x0f) - testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-1", 0x0f) - testVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-3", 0x0f) - - testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-3", 0x0f) - testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3", 0x0f) -} - -func assertGrantSame(t *testing.T, actual, expected *Grant) { - if actual != expected { - t.Fatalf("Unexpected grant retrieved\n\tExpected: %v\n\tActual: %v", expected, actual) - } -} - -func TestGetGrants(t *testing.T) { - grants, keys := createTestKeysAndGrants(5) - extraGrants := make([]*Grant, 4) - extraGrants[0] = &Grant{ - Subject: "/user-3/friend-project", - Permission: 0x0f, - Grantee: "/user-2/friends", - } - extraGrants[1] = &Grant{ - Subject: "/user-3/sub-project", - Permission: 0x0f, - Grantee: "/user-4", - } - extraGrants[2] = &Grant{ - Subject: "/user-2/friends", - Permission: 0x0f, - Grantee: "/user-5/fun-project", - } - extraGrants[3] = &Grant{ - Subject: "/user-5/fun-project", - Permission: 0x0f, - Grantee: "/user-1", - } - grants = append(grants, extraGrants...) - - g := NewMemoryGraph(grants) - - grantChains, err := g.GetGrants(keys[3], "/user-3/sub-project/specific-app", 0x0f) - if err != nil { - t.Fatalf("Error getting grants: %s", err) - } - if len(grantChains) != 1 { - t.Fatalf("Expected number of grant chains returned, expected %d, received %d", 1, len(grantChains)) - } - if len(grantChains[0]) != 2 { - t.Fatalf("Unexpected number of grants retrieved\n\tExpected: %d\n\tActual: %d", 2, len(grantChains[0])) - } - assertGrantSame(t, grantChains[0][0], grants[3]) - assertGrantSame(t, grantChains[0][1], extraGrants[1]) - - grantChains, err = g.GetGrants(keys[0], "/user-3/friend-project/fun-app", 0x0f) - if err != nil { - t.Fatalf("Error getting grants: %s", err) - } - if len(grantChains) != 1 { - t.Fatalf("Expected number of grant chains returned, expected %d, received %d", 1, len(grantChains)) - } - if len(grantChains[0]) != 4 { - t.Fatalf("Unexpected number of grants retrieved\n\tExpected: %d\n\tActual: %d", 2, len(grantChains[0])) - } - assertGrantSame(t, grantChains[0][0], grants[0]) - assertGrantSame(t, grantChains[0][1], extraGrants[3]) - assertGrantSame(t, grantChains[0][2], extraGrants[2]) - assertGrantSame(t, grantChains[0][3], extraGrants[0]) -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/statement.go b/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/statement.go deleted file mode 100644 index 7a74b553c..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/statement.go +++ /dev/null @@ -1,227 +0,0 @@ -package trustgraph - -import ( - "crypto/x509" - "encoding/json" - "io" - "io/ioutil" - "sort" - "strings" - "time" - - "github.com/docker/libtrust" -) - -type jsonGrant struct { - Subject string `json:"subject"` - Permission uint16 `json:"permission"` - Grantee string `json:"grantee"` -} - -type jsonRevocation struct { - Subject string `json:"subject"` - Revocation uint16 `json:"revocation"` - Grantee string `json:"grantee"` -} - -type jsonStatement struct { - Revocations []*jsonRevocation `json:"revocations"` - Grants []*jsonGrant `json:"grants"` - Expiration time.Time `json:"expiration"` - IssuedAt time.Time `json:"issuedAt"` -} - -func (g *jsonGrant) Grant(statement *Statement) *Grant { - return &Grant{ - Subject: g.Subject, - Permission: g.Permission, - Grantee: g.Grantee, - statement: statement, - } -} - -// Statement represents a set of grants made from a verifiable -// authority. A statement has an expiration associated with it -// set by the authority. -type Statement struct { - jsonStatement - - signature *libtrust.JSONSignature -} - -// IsExpired returns whether the statement has expired -func (s *Statement) IsExpired() bool { - return s.Expiration.Before(time.Now().Add(-10 * time.Second)) -} - -// Bytes returns an indented json representation of the statement -// in a byte array. This value can be written to a file or stream -// without alteration. -func (s *Statement) Bytes() ([]byte, error) { - return s.signature.PrettySignature("signatures") -} - -// LoadStatement loads and verifies a statement from an input stream. -func LoadStatement(r io.Reader, authority *x509.CertPool) (*Statement, error) { - b, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - js, err := libtrust.ParsePrettySignature(b, "signatures") - if err != nil { - return nil, err - } - payload, err := js.Payload() - if err != nil { - return nil, err - } - var statement Statement - err = json.Unmarshal(payload, &statement.jsonStatement) - if err != nil { - return nil, err - } - - if authority == nil { - _, err = js.Verify() - if err != nil { - return nil, err - } - } else { - _, err = js.VerifyChains(authority) - if err != nil { - return nil, err - } - } - statement.signature = js - - return &statement, nil -} - -// CreateStatements creates and signs a statement from a stream of grants -// and revocations in a JSON array. -func CreateStatement(grants, revocations io.Reader, expiration time.Duration, key libtrust.PrivateKey, chain []*x509.Certificate) (*Statement, error) { - var statement Statement - err := json.NewDecoder(grants).Decode(&statement.jsonStatement.Grants) - if err != nil { - return nil, err - } - err = json.NewDecoder(revocations).Decode(&statement.jsonStatement.Revocations) - if err != nil { - return nil, err - } - statement.jsonStatement.Expiration = time.Now().UTC().Add(expiration) - statement.jsonStatement.IssuedAt = time.Now().UTC() - - b, err := json.MarshalIndent(&statement.jsonStatement, "", " ") - if err != nil { - return nil, err - } - - statement.signature, err = libtrust.NewJSONSignature(b) - if err != nil { - return nil, err - } - err = statement.signature.SignWithChain(key, chain) - if err != nil { - return nil, err - } - - return &statement, nil -} - -type statementList []*Statement - -func (s statementList) Len() int { - return len(s) -} - -func (s statementList) Less(i, j int) bool { - return s[i].IssuedAt.Before(s[j].IssuedAt) -} - -func (s statementList) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -// CollapseStatements returns a single list of the valid statements as well as the -// time when the next grant will expire. -func CollapseStatements(statements []*Statement, useExpired bool) ([]*Grant, time.Time, error) { - sorted := make(statementList, 0, len(statements)) - for _, statement := range statements { - if useExpired || !statement.IsExpired() { - sorted = append(sorted, statement) - } - } - sort.Sort(sorted) - - var minExpired time.Time - var grantCount int - roots := map[string]*grantNode{} - for i, statement := range sorted { - if statement.Expiration.Before(minExpired) || i == 0 { - minExpired = statement.Expiration - } - for _, grant := range statement.Grants { - parts := strings.Split(grant.Grantee, "/") - nodes := roots - g := grant.Grant(statement) - grantCount = grantCount + 1 - - for _, part := range parts { - node, nodeOk := nodes[part] - if !nodeOk { - node = newGrantNode() - nodes[part] = node - } - node.grants = append(node.grants, g) - nodes = node.children - } - } - - for _, revocation := range statement.Revocations { - parts := strings.Split(revocation.Grantee, "/") - nodes := roots - - var node *grantNode - var nodeOk bool - for _, part := range parts { - node, nodeOk = nodes[part] - if !nodeOk { - break - } - nodes = node.children - } - if node != nil { - for _, grant := range node.grants { - if isSubName(grant.Subject, revocation.Subject) { - grant.Permission = grant.Permission &^ revocation.Revocation - } - } - } - } - } - - retGrants := make([]*Grant, 0, grantCount) - for _, rootNodes := range roots { - retGrants = append(retGrants, rootNodes.grants...) - } - - return retGrants, minExpired, nil -} - -// FilterStatements filters the statements to statements including the given grants. -func FilterStatements(grants []*Grant) ([]*Statement, error) { - statements := map[*Statement]bool{} - for _, grant := range grants { - if grant.statement != nil { - statements[grant.statement] = true - } - } - retStatements := make([]*Statement, len(statements)) - var i int - for statement := range statements { - retStatements[i] = statement - i++ - } - return retStatements, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/statement_test.go b/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/statement_test.go deleted file mode 100644 index e50946865..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/trustgraph/statement_test.go +++ /dev/null @@ -1,417 +0,0 @@ -package trustgraph - -import ( - "bytes" - "crypto/x509" - "encoding/json" - "testing" - "time" - - "github.com/docker/libtrust" - "github.com/docker/libtrust/testutil" -) - -const testStatementExpiration = time.Hour * 5 - -func generateStatement(grants []*Grant, key libtrust.PrivateKey, chain []*x509.Certificate) (*Statement, error) { - var statement Statement - - statement.Grants = make([]*jsonGrant, len(grants)) - for i, grant := range grants { - statement.Grants[i] = &jsonGrant{ - Subject: grant.Subject, - Permission: grant.Permission, - Grantee: grant.Grantee, - } - } - statement.IssuedAt = time.Now() - statement.Expiration = time.Now().Add(testStatementExpiration) - statement.Revocations = make([]*jsonRevocation, 0) - - marshalled, err := json.MarshalIndent(statement.jsonStatement, "", " ") - if err != nil { - return nil, err - } - - sig, err := libtrust.NewJSONSignature(marshalled) - if err != nil { - return nil, err - } - err = sig.SignWithChain(key, chain) - if err != nil { - return nil, err - } - statement.signature = sig - - return &statement, nil -} - -func generateTrustChain(t *testing.T, chainLen int) (libtrust.PrivateKey, *x509.CertPool, []*x509.Certificate) { - caKey, err := libtrust.GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("Error generating key: %s", err) - } - ca, err := testutil.GenerateTrustCA(caKey.CryptoPublicKey(), caKey.CryptoPrivateKey()) - if err != nil { - t.Fatalf("Error generating ca: %s", err) - } - - parent := ca - parentKey := caKey - chain := make([]*x509.Certificate, chainLen) - for i := chainLen - 1; i > 0; i-- { - intermediatekey, err := libtrust.GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("Error generate key: %s", err) - } - chain[i], err = testutil.GenerateIntermediate(intermediatekey.CryptoPublicKey(), parentKey.CryptoPrivateKey(), parent) - if err != nil { - t.Fatalf("Error generating intermdiate certificate: %s", err) - } - parent = chain[i] - parentKey = intermediatekey - } - trustKey, err := libtrust.GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("Error generate key: %s", err) - } - chain[0], err = testutil.GenerateTrustCert(trustKey.CryptoPublicKey(), parentKey.CryptoPrivateKey(), parent) - if err != nil { - t.Fatalf("Error generate trust cert: %s", err) - } - - caPool := x509.NewCertPool() - caPool.AddCert(ca) - - return trustKey, caPool, chain -} - -func TestLoadStatement(t *testing.T) { - grantCount := 4 - grants, _ := createTestKeysAndGrants(grantCount) - - trustKey, caPool, chain := generateTrustChain(t, 6) - - statement, err := generateStatement(grants, trustKey, chain) - if err != nil { - t.Fatalf("Error generating statement: %s", err) - } - - statementBytes, err := statement.Bytes() - if err != nil { - t.Fatalf("Error getting statement bytes: %s", err) - } - - s2, err := LoadStatement(bytes.NewReader(statementBytes), caPool) - if err != nil { - t.Fatalf("Error loading statement: %s", err) - } - if len(s2.Grants) != grantCount { - t.Fatalf("Unexpected grant length\n\tExpected: %d\n\tActual: %d", grantCount, len(s2.Grants)) - } - - pool := x509.NewCertPool() - _, err = LoadStatement(bytes.NewReader(statementBytes), pool) - if err == nil { - t.Fatalf("No error thrown verifying without an authority") - } else if _, ok := err.(x509.UnknownAuthorityError); !ok { - t.Fatalf("Unexpected error verifying without authority: %s", err) - } - - s2, err = LoadStatement(bytes.NewReader(statementBytes), nil) - if err != nil { - t.Fatalf("Error loading statement: %s", err) - } - if len(s2.Grants) != grantCount { - t.Fatalf("Unexpected grant length\n\tExpected: %d\n\tActual: %d", grantCount, len(s2.Grants)) - } - - badData := make([]byte, len(statementBytes)) - copy(badData, statementBytes) - badData[0] = '[' - _, err = LoadStatement(bytes.NewReader(badData), nil) - if err == nil { - t.Fatalf("No error thrown parsing bad json") - } - - alteredData := make([]byte, len(statementBytes)) - copy(alteredData, statementBytes) - alteredData[30] = '0' - _, err = LoadStatement(bytes.NewReader(alteredData), nil) - if err == nil { - t.Fatalf("No error thrown from bad data") - } -} - -func TestCollapseGrants(t *testing.T) { - grantCount := 8 - grants, keys := createTestKeysAndGrants(grantCount) - linkGrants := make([]*Grant, 4) - linkGrants[0] = &Grant{ - Subject: "/user-3", - Permission: 0x0f, - Grantee: "/user-2", - } - linkGrants[1] = &Grant{ - Subject: "/user-3/sub-project", - Permission: 0x0f, - Grantee: "/user-4", - } - linkGrants[2] = &Grant{ - Subject: "/user-6", - Permission: 0x0f, - Grantee: "/user-7", - } - linkGrants[3] = &Grant{ - Subject: "/user-6/sub-project/specific-app", - Permission: 0x0f, - Grantee: "/user-5", - } - trustKey, pool, chain := generateTrustChain(t, 3) - - statements := make([]*Statement, 3) - var err error - statements[0], err = generateStatement(grants[0:4], trustKey, chain) - if err != nil { - t.Fatalf("Error generating statement: %s", err) - } - statements[1], err = generateStatement(grants[4:], trustKey, chain) - if err != nil { - t.Fatalf("Error generating statement: %s", err) - } - statements[2], err = generateStatement(linkGrants, trustKey, chain) - if err != nil { - t.Fatalf("Error generating statement: %s", err) - } - - statementsCopy := make([]*Statement, len(statements)) - for i, statement := range statements { - b, err := statement.Bytes() - if err != nil { - t.Fatalf("Error getting statement bytes: %s", err) - } - verifiedStatement, err := LoadStatement(bytes.NewReader(b), pool) - if err != nil { - t.Fatalf("Error loading statement: %s", err) - } - // Force sort by reversing order - statementsCopy[len(statementsCopy)-i-1] = verifiedStatement - } - statements = statementsCopy - - collapsedGrants, expiration, err := CollapseStatements(statements, false) - if len(collapsedGrants) != 12 { - t.Fatalf("Unexpected number of grants\n\tExpected: %d\n\tActual: %d", 12, len(collapsedGrants)) - } - if expiration.After(time.Now().Add(time.Hour*5)) || expiration.Before(time.Now()) { - t.Fatalf("Unexpected expiration time: %s", expiration.String()) - } - g := NewMemoryGraph(collapsedGrants) - - testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f) - testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x0f) - testVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-3", 0x0f) - testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-4", 0x0f) - testVerified(t, g, keys[4].PublicKey(), "user-key-5", "/user-5", 0x0f) - testVerified(t, g, keys[5].PublicKey(), "user-key-6", "/user-6", 0x0f) - testVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-7", 0x0f) - testVerified(t, g, keys[7].PublicKey(), "user-key-8", "/user-8", 0x0f) - testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3", 0x0f) - testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3/sub-project/specific-app", 0x0f) - testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3/sub-project", 0x0f) - testVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-6", 0x0f) - testVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-6/sub-project/specific-app", 0x0f) - testVerified(t, g, keys[4].PublicKey(), "user-key-5", "/user-6/sub-project/specific-app", 0x0f) - - testNotVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3", 0x0f) - testNotVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-6/sub-project", 0x0f) - testNotVerified(t, g, keys[4].PublicKey(), "user-key-5", "/user-6/sub-project", 0x0f) - - // Add revocation grant - statements = append(statements, &Statement{ - jsonStatement{ - IssuedAt: time.Now(), - Expiration: time.Now().Add(testStatementExpiration), - Grants: []*jsonGrant{}, - Revocations: []*jsonRevocation{ - &jsonRevocation{ - Subject: "/user-1", - Revocation: 0x0f, - Grantee: keys[0].KeyID(), - }, - &jsonRevocation{ - Subject: "/user-2", - Revocation: 0x08, - Grantee: keys[1].KeyID(), - }, - &jsonRevocation{ - Subject: "/user-6", - Revocation: 0x0f, - Grantee: "/user-7", - }, - &jsonRevocation{ - Subject: "/user-9", - Revocation: 0x0f, - Grantee: "/user-10", - }, - }, - }, - nil, - }) - - collapsedGrants, expiration, err = CollapseStatements(statements, false) - if len(collapsedGrants) != 12 { - t.Fatalf("Unexpected number of grants\n\tExpected: %d\n\tActual: %d", 12, len(collapsedGrants)) - } - if expiration.After(time.Now().Add(time.Hour*5)) || expiration.Before(time.Now()) { - t.Fatalf("Unexpected expiration time: %s", expiration.String()) - } - g = NewMemoryGraph(collapsedGrants) - - testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f) - testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x0f) - testNotVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-6/sub-project/specific-app", 0x0f) - - testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x07) -} - -func TestFilterStatements(t *testing.T) { - grantCount := 8 - grants, keys := createTestKeysAndGrants(grantCount) - linkGrants := make([]*Grant, 3) - linkGrants[0] = &Grant{ - Subject: "/user-3", - Permission: 0x0f, - Grantee: "/user-2", - } - linkGrants[1] = &Grant{ - Subject: "/user-5", - Permission: 0x0f, - Grantee: "/user-4", - } - linkGrants[2] = &Grant{ - Subject: "/user-7", - Permission: 0x0f, - Grantee: "/user-6", - } - - trustKey, _, chain := generateTrustChain(t, 3) - - statements := make([]*Statement, 5) - var err error - statements[0], err = generateStatement(grants[0:2], trustKey, chain) - if err != nil { - t.Fatalf("Error generating statement: %s", err) - } - statements[1], err = generateStatement(grants[2:4], trustKey, chain) - if err != nil { - t.Fatalf("Error generating statement: %s", err) - } - statements[2], err = generateStatement(grants[4:6], trustKey, chain) - if err != nil { - t.Fatalf("Error generating statement: %s", err) - } - statements[3], err = generateStatement(grants[6:], trustKey, chain) - if err != nil { - t.Fatalf("Error generating statement: %s", err) - } - statements[4], err = generateStatement(linkGrants, trustKey, chain) - if err != nil { - t.Fatalf("Error generating statement: %s", err) - } - collapsed, _, err := CollapseStatements(statements, false) - if err != nil { - t.Fatalf("Error collapsing grants: %s", err) - } - - // Filter 1, all 5 statements - filter1, err := FilterStatements(collapsed) - if err != nil { - t.Fatalf("Error filtering statements: %s", err) - } - if len(filter1) != 5 { - t.Fatalf("Wrong number of statements, expected %d, received %d", 5, len(filter1)) - } - - // Filter 2, one statement - filter2, err := FilterStatements([]*Grant{collapsed[0]}) - if err != nil { - t.Fatalf("Error filtering statements: %s", err) - } - if len(filter2) != 1 { - t.Fatalf("Wrong number of statements, expected %d, received %d", 1, len(filter2)) - } - - // Filter 3, 2 statements, from graph lookup - g := NewMemoryGraph(collapsed) - lookupGrants, err := g.GetGrants(keys[1], "/user-3", 0x0f) - if err != nil { - t.Fatalf("Error looking up grants: %s", err) - } - if len(lookupGrants) != 1 { - t.Fatalf("Wrong numberof grant chains returned from lookup, expected %d, received %d", 1, len(lookupGrants)) - } - if len(lookupGrants[0]) != 2 { - t.Fatalf("Wrong number of grants looked up, expected %d, received %d", 2, len(lookupGrants)) - } - filter3, err := FilterStatements(lookupGrants[0]) - if err != nil { - t.Fatalf("Error filtering statements: %s", err) - } - if len(filter3) != 2 { - t.Fatalf("Wrong number of statements, expected %d, received %d", 2, len(filter3)) - } - -} - -func TestCreateStatement(t *testing.T) { - grantJSON := bytes.NewReader([]byte(`[ - { - "subject": "/user-2", - "permission": 15, - "grantee": "/user-1" - }, - { - "subject": "/user-7", - "permission": 1, - "grantee": "/user-9" - }, - { - "subject": "/user-3", - "permission": 15, - "grantee": "/user-2" - } -]`)) - revocationJSON := bytes.NewReader([]byte(`[ - { - "subject": "user-8", - "revocation": 12, - "grantee": "user-9" - } -]`)) - - trustKey, pool, chain := generateTrustChain(t, 3) - - statement, err := CreateStatement(grantJSON, revocationJSON, testStatementExpiration, trustKey, chain) - if err != nil { - t.Fatalf("Error creating statement: %s", err) - } - - b, err := statement.Bytes() - if err != nil { - t.Fatalf("Error retrieving bytes: %s", err) - } - - verified, err := LoadStatement(bytes.NewReader(b), pool) - if err != nil { - t.Fatalf("Error loading statement: %s", err) - } - - if len(verified.Grants) != 3 { - t.Errorf("Unexpected number of grants, expected %d, received %d", 3, len(verified.Grants)) - } - - if len(verified.Revocations) != 1 { - t.Errorf("Unexpected number of revocations, expected %d, received %d", 1, len(verified.Revocations)) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/util.go b/Godeps/_workspace/src/github.com/docker/libtrust/util.go deleted file mode 100644 index d88176cc3..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/util.go +++ /dev/null @@ -1,363 +0,0 @@ -package libtrust - -import ( - "bytes" - "crypto" - "crypto/elliptic" - "crypto/tls" - "crypto/x509" - "encoding/base32" - "encoding/base64" - "encoding/binary" - "encoding/pem" - "errors" - "fmt" - "math/big" - "net/url" - "os" - "path/filepath" - "strings" - "time" -) - -// LoadOrCreateTrustKey will load a PrivateKey from the specified path -func LoadOrCreateTrustKey(trustKeyPath string) (PrivateKey, error) { - if err := os.MkdirAll(filepath.Dir(trustKeyPath), 0700); err != nil { - return nil, err - } - - trustKey, err := LoadKeyFile(trustKeyPath) - if err == ErrKeyFileDoesNotExist { - trustKey, err = GenerateECP256PrivateKey() - if err != nil { - return nil, fmt.Errorf("error generating key: %s", err) - } - - if err := SaveKey(trustKeyPath, trustKey); err != nil { - return nil, fmt.Errorf("error saving key file: %s", err) - } - - dir, file := filepath.Split(trustKeyPath) - if err := SavePublicKey(filepath.Join(dir, "public-"+file), trustKey.PublicKey()); err != nil { - return nil, fmt.Errorf("error saving public key file: %s", err) - } - } else if err != nil { - return nil, fmt.Errorf("error loading key file: %s", err) - } - return trustKey, nil -} - -// NewIdentityAuthTLSClientConfig returns a tls.Config configured to use identity -// based authentication from the specified dockerUrl, the rootConfigPath and -// the server name to which it is connecting. -// If trustUnknownHosts is true it will automatically add the host to the -// known-hosts.json in rootConfigPath. -func NewIdentityAuthTLSClientConfig(dockerUrl string, trustUnknownHosts bool, rootConfigPath string, serverName string) (*tls.Config, error) { - tlsConfig := newTLSConfig() - - trustKeyPath := filepath.Join(rootConfigPath, "key.json") - knownHostsPath := filepath.Join(rootConfigPath, "known-hosts.json") - - u, err := url.Parse(dockerUrl) - if err != nil { - return nil, fmt.Errorf("unable to parse machine url") - } - - if u.Scheme == "unix" { - return nil, nil - } - - addr := u.Host - proto := "tcp" - - trustKey, err := LoadOrCreateTrustKey(trustKeyPath) - if err != nil { - return nil, fmt.Errorf("unable to load trust key: %s", err) - } - - knownHosts, err := LoadKeySetFile(knownHostsPath) - if err != nil { - return nil, fmt.Errorf("could not load trusted hosts file: %s", err) - } - - allowedHosts, err := FilterByHosts(knownHosts, addr, false) - if err != nil { - return nil, fmt.Errorf("error filtering hosts: %s", err) - } - - certPool, err := GenerateCACertPool(trustKey, allowedHosts) - if err != nil { - return nil, fmt.Errorf("Could not create CA pool: %s", err) - } - - tlsConfig.ServerName = serverName - tlsConfig.RootCAs = certPool - - x509Cert, err := GenerateSelfSignedClientCert(trustKey) - if err != nil { - return nil, fmt.Errorf("certificate generation error: %s", err) - } - - tlsConfig.Certificates = []tls.Certificate{{ - Certificate: [][]byte{x509Cert.Raw}, - PrivateKey: trustKey.CryptoPrivateKey(), - Leaf: x509Cert, - }} - - tlsConfig.InsecureSkipVerify = true - - testConn, err := tls.Dial(proto, addr, tlsConfig) - if err != nil { - return nil, fmt.Errorf("tls Handshake error: %s", err) - } - - opts := x509.VerifyOptions{ - Roots: tlsConfig.RootCAs, - CurrentTime: time.Now(), - DNSName: tlsConfig.ServerName, - Intermediates: x509.NewCertPool(), - } - - certs := testConn.ConnectionState().PeerCertificates - for i, cert := range certs { - if i == 0 { - continue - } - opts.Intermediates.AddCert(cert) - } - - if _, err := certs[0].Verify(opts); err != nil { - if _, ok := err.(x509.UnknownAuthorityError); ok { - if trustUnknownHosts { - pubKey, err := FromCryptoPublicKey(certs[0].PublicKey) - if err != nil { - return nil, fmt.Errorf("error extracting public key from cert: %s", err) - } - - pubKey.AddExtendedField("hosts", []string{addr}) - - if err := AddKeySetFile(knownHostsPath, pubKey); err != nil { - return nil, fmt.Errorf("error adding machine to known hosts: %s", err) - } - } else { - return nil, fmt.Errorf("unable to connect. unknown host: %s", addr) - } - } - } - - testConn.Close() - tlsConfig.InsecureSkipVerify = false - - return tlsConfig, nil -} - -// joseBase64UrlEncode encodes the given data using the standard base64 url -// encoding format but with all trailing '=' characters ommitted in accordance -// with the jose specification. -// http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-2 -func joseBase64UrlEncode(b []byte) string { - return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") -} - -// joseBase64UrlDecode decodes the given string using the standard base64 url -// decoder but first adds the appropriate number of trailing '=' characters in -// accordance with the jose specification. -// http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-2 -func joseBase64UrlDecode(s string) ([]byte, error) { - s = strings.Replace(s, "\n", "", -1) - s = strings.Replace(s, " ", "", -1) - switch len(s) % 4 { - case 0: - case 2: - s += "==" - case 3: - s += "=" - default: - return nil, errors.New("illegal base64url string") - } - return base64.URLEncoding.DecodeString(s) -} - -func keyIDEncode(b []byte) string { - s := strings.TrimRight(base32.StdEncoding.EncodeToString(b), "=") - var buf bytes.Buffer - var i int - for i = 0; i < len(s)/4-1; i++ { - start := i * 4 - end := start + 4 - buf.WriteString(s[start:end] + ":") - } - buf.WriteString(s[i*4:]) - return buf.String() -} - -func keyIDFromCryptoKey(pubKey PublicKey) string { - // Generate and return a 'libtrust' fingerprint of the public key. - // For an RSA key this should be: - // SHA256(DER encoded ASN1) - // Then truncated to 240 bits and encoded into 12 base32 groups like so: - // ABCD:EFGH:IJKL:MNOP:QRST:UVWX:YZ23:4567:ABCD:EFGH:IJKL:MNOP - derBytes, err := x509.MarshalPKIXPublicKey(pubKey.CryptoPublicKey()) - if err != nil { - return "" - } - hasher := crypto.SHA256.New() - hasher.Write(derBytes) - return keyIDEncode(hasher.Sum(nil)[:30]) -} - -func stringFromMap(m map[string]interface{}, key string) (string, error) { - val, ok := m[key] - if !ok { - return "", fmt.Errorf("%q value not specified", key) - } - - str, ok := val.(string) - if !ok { - return "", fmt.Errorf("%q value must be a string", key) - } - delete(m, key) - - return str, nil -} - -func parseECCoordinate(cB64Url string, curve elliptic.Curve) (*big.Int, error) { - curveByteLen := (curve.Params().BitSize + 7) >> 3 - - cBytes, err := joseBase64UrlDecode(cB64Url) - if err != nil { - return nil, fmt.Errorf("invalid base64 URL encoding: %s", err) - } - cByteLength := len(cBytes) - if cByteLength != curveByteLen { - return nil, fmt.Errorf("invalid number of octets: got %d, should be %d", cByteLength, curveByteLen) - } - return new(big.Int).SetBytes(cBytes), nil -} - -func parseECPrivateParam(dB64Url string, curve elliptic.Curve) (*big.Int, error) { - dBytes, err := joseBase64UrlDecode(dB64Url) - if err != nil { - return nil, fmt.Errorf("invalid base64 URL encoding: %s", err) - } - - // The length of this octet string MUST be ceiling(log-base-2(n)/8) - // octets (where n is the order of the curve). This is because the private - // key d must be in the interval [1, n-1] so the bitlength of d should be - // no larger than the bitlength of n-1. The easiest way to find the octet - // length is to take bitlength(n-1), add 7 to force a carry, and shift this - // bit sequence right by 3, which is essentially dividing by 8 and adding - // 1 if there is any remainder. Thus, the private key value d should be - // output to (bitlength(n-1)+7)>>3 octets. - n := curve.Params().N - octetLength := (new(big.Int).Sub(n, big.NewInt(1)).BitLen() + 7) >> 3 - dByteLength := len(dBytes) - - if dByteLength != octetLength { - return nil, fmt.Errorf("invalid number of octets: got %d, should be %d", dByteLength, octetLength) - } - - return new(big.Int).SetBytes(dBytes), nil -} - -func parseRSAModulusParam(nB64Url string) (*big.Int, error) { - nBytes, err := joseBase64UrlDecode(nB64Url) - if err != nil { - return nil, fmt.Errorf("invalid base64 URL encoding: %s", err) - } - - return new(big.Int).SetBytes(nBytes), nil -} - -func serializeRSAPublicExponentParam(e int) []byte { - // We MUST use the minimum number of octets to represent E. - // E is supposed to be 65537 for performance and security reasons - // and is what golang's rsa package generates, but it might be - // different if imported from some other generator. - buf := make([]byte, 4) - binary.BigEndian.PutUint32(buf, uint32(e)) - var i int - for i = 0; i < 8; i++ { - if buf[i] != 0 { - break - } - } - return buf[i:] -} - -func parseRSAPublicExponentParam(eB64Url string) (int, error) { - eBytes, err := joseBase64UrlDecode(eB64Url) - if err != nil { - return 0, fmt.Errorf("invalid base64 URL encoding: %s", err) - } - // Only the minimum number of bytes were used to represent E, but - // binary.BigEndian.Uint32 expects at least 4 bytes, so we need - // to add zero padding if necassary. - byteLen := len(eBytes) - buf := make([]byte, 4-byteLen, 4) - eBytes = append(buf, eBytes...) - - return int(binary.BigEndian.Uint32(eBytes)), nil -} - -func parseRSAPrivateKeyParamFromMap(m map[string]interface{}, key string) (*big.Int, error) { - b64Url, err := stringFromMap(m, key) - if err != nil { - return nil, err - } - - paramBytes, err := joseBase64UrlDecode(b64Url) - if err != nil { - return nil, fmt.Errorf("invaled base64 URL encoding: %s", err) - } - - return new(big.Int).SetBytes(paramBytes), nil -} - -func createPemBlock(name string, derBytes []byte, headers map[string]interface{}) (*pem.Block, error) { - pemBlock := &pem.Block{Type: name, Bytes: derBytes, Headers: map[string]string{}} - for k, v := range headers { - switch val := v.(type) { - case string: - pemBlock.Headers[k] = val - case []string: - if k == "hosts" { - pemBlock.Headers[k] = strings.Join(val, ",") - } else { - // Return error, non-encodable type - } - default: - // Return error, non-encodable type - } - } - - return pemBlock, nil -} - -func pubKeyFromPEMBlock(pemBlock *pem.Block) (PublicKey, error) { - cryptoPublicKey, err := x509.ParsePKIXPublicKey(pemBlock.Bytes) - if err != nil { - return nil, fmt.Errorf("unable to decode Public Key PEM data: %s", err) - } - - pubKey, err := FromCryptoPublicKey(cryptoPublicKey) - if err != nil { - return nil, err - } - - addPEMHeadersToKey(pemBlock, pubKey) - - return pubKey, nil -} - -func addPEMHeadersToKey(pemBlock *pem.Block, pubKey PublicKey) { - for key, value := range pemBlock.Headers { - var safeVal interface{} - if key == "hosts" { - safeVal = strings.Split(value, ",") - } else { - safeVal = value - } - pubKey.AddExtendedField(key, safeVal) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/libtrust/util_test.go b/Godeps/_workspace/src/github.com/docker/libtrust/util_test.go deleted file mode 100644 index 83b7cfb18..000000000 --- a/Godeps/_workspace/src/github.com/docker/libtrust/util_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package libtrust - -import ( - "encoding/pem" - "reflect" - "testing" -) - -func TestAddPEMHeadersToKey(t *testing.T) { - pk := &rsaPublicKey{nil, map[string]interface{}{}} - blk := &pem.Block{Headers: map[string]string{"hosts": "localhost,127.0.0.1"}} - addPEMHeadersToKey(blk, pk) - - val := pk.GetExtendedField("hosts") - hosts, ok := val.([]string) - if !ok { - t.Fatalf("hosts type(%v), expected []string", reflect.TypeOf(val)) - } - expected := []string{"localhost", "127.0.0.1"} - if !reflect.DeepEqual(hosts, expected) { - t.Errorf("hosts(%v), expected %v", hosts, expected) - } -} - -func TestBase64URL(t *testing.T) { - clean := "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJwMnMiOiIyV0NUY0paMVJ2ZF9DSnVKcmlwUTF3IiwicDJjIjo0MDk2LCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiY3R5IjoiandrK2pzb24ifQ" - - tests := []string{ - clean, // clean roundtrip - "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJwMnMiOiIyV0NUY0paMVJ2\nZF9DSnVKcmlwUTF3IiwicDJjIjo0MDk2LCJlbmMiOiJBMTI4Q0JDLUhTMjU2\nIiwiY3R5IjoiandrK2pzb24ifQ", // with newlines - "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJwMnMiOiIyV0NUY0paMVJ2 \n ZF9DSnVKcmlwUTF3IiwicDJjIjo0MDk2LCJlbmMiOiJBMTI4Q0JDLUhTMjU2 \n IiwiY3R5IjoiandrK2pzb24ifQ", // with newlines and spaces - } - - for i, test := range tests { - b, err := joseBase64UrlDecode(test) - if err != nil { - t.Fatalf("on test %d: %s", i, err) - } - got := joseBase64UrlEncode(b) - - if got != clean { - t.Errorf("expected %q, got %q", clean, got) - } - } -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore deleted file mode 100644 index ba8e0cb3a..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -Icon? -ehthumbs.db -Thumbs.db diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml deleted file mode 100644 index 2f4e3c2f0..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -sudo: false -language: go -go: - - 1.2 - - 1.3 - - 1.4 - - tip - -before_script: - - mysql -e 'create database gotest;' diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/AUTHORS b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/AUTHORS deleted file mode 100644 index 6fc4c6f7b..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/AUTHORS +++ /dev/null @@ -1,44 +0,0 @@ -# This is the official list of Go-MySQL-Driver authors for copyright purposes. - -# If you are submitting a patch, please add your name or the name of the -# organization which holds the copyright to this list in alphabetical order. - -# Names should be added to this file as -# Name -# The email address is not required for organizations. -# Please keep the list sorted. - - -# Individual Persons - -Aaron Hopkins -Arne Hormann -Carlos Nieto -Chris Moos -DisposaBoy -Frederick Mayle -Gustavo Kristic -Hanno Braun -Henri Yandell -INADA Naoki -James Harr -Jian Zhen -Joshua Prunier -Julien Schmidt -Kamil Dziedzic -Leonardo YongUk Kim -Lucas Liu -Luke Scott -Michael Woolnough -Nicola Peduzzi -Runrioter Wung -Soroush Pour -Stan Putrya -Xiaobing Jiang -Xiuming Chen - -# Organizations - -Barracuda Networks, Inc. -Google Inc. -Stripe Inc. diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/CHANGELOG.md b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/CHANGELOG.md deleted file mode 100644 index 161ad0fcc..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/CHANGELOG.md +++ /dev/null @@ -1,92 +0,0 @@ -## HEAD - -Changes: - - - Go 1.1 is no longer supported - - Use decimals field from MySQL to format time types (#249) - - Buffer optimizations (#269) - - TLS ServerName defaults to the host (#283) - -Bugfixes: - - - Enable microsecond resolution on TIME, DATETIME and TIMESTAMP (#249) - - Fixed handling of queries without columns and rows (#255) - - Fixed a panic when SetKeepAlive() failed (#298) - -New Features: - - Support for returning table alias on Columns() (#289) - - Placeholder interpolation, can be actived with the DSN parameter `interpolateParams=true` (#309, #318) - - -## Version 1.2 (2014-06-03) - -Changes: - - - We switched back to a "rolling release". `go get` installs the current master branch again - - Version v1 of the driver will not be maintained anymore. Go 1.0 is no longer supported by this driver - - Exported errors to allow easy checking from application code - - Enabled TCP Keepalives on TCP connections - - Optimized INFILE handling (better buffer size calculation, lazy init, ...) - - The DSN parser also checks for a missing separating slash - - Faster binary date / datetime to string formatting - - Also exported the MySQLWarning type - - mysqlConn.Close returns the first error encountered instead of ignoring all errors - - writePacket() automatically writes the packet size to the header - - readPacket() uses an iterative approach instead of the recursive approach to merge splitted packets - -New Features: - - - `RegisterDial` allows the usage of a custom dial function to establish the network connection - - Setting the connection collation is possible with the `collation` DSN parameter. This parameter should be preferred over the `charset` parameter - - Logging of critical errors is configurable with `SetLogger` - - Google CloudSQL support - -Bugfixes: - - - Allow more than 32 parameters in prepared statements - - Various old_password fixes - - Fixed TestConcurrent test to pass Go's race detection - - Fixed appendLengthEncodedInteger for large numbers - - Renamed readLengthEnodedString to readLengthEncodedString and skipLengthEnodedString to skipLengthEncodedString (fixed typo) - - -## Version 1.1 (2013-11-02) - -Changes: - - - Go-MySQL-Driver now requires Go 1.1 - - Connections now use the collation `utf8_general_ci` by default. Adding `&charset=UTF8` to the DSN should not be necessary anymore - - Made closing rows and connections error tolerant. This allows for example deferring rows.Close() without checking for errors - - `[]byte(nil)` is now treated as a NULL value. Before, it was treated like an empty string / `[]byte("")` - - DSN parameter values must now be url.QueryEscape'ed. This allows text values to contain special characters, such as '&'. - - Use the IO buffer also for writing. This results in zero allocations (by the driver) for most queries - - Optimized the buffer for reading - - stmt.Query now caches column metadata - - New Logo - - Changed the copyright header to include all contributors - - Improved the LOAD INFILE documentation - - The driver struct is now exported to make the driver directly accessible - - Refactored the driver tests - - Added more benchmarks and moved all to a separate file - - Other small refactoring - -New Features: - - - Added *old_passwords* support: Required in some cases, but must be enabled by adding `allowOldPasswords=true` to the DSN since it is insecure - - Added a `clientFoundRows` parameter: Return the number of matching rows instead of the number of rows changed on UPDATEs - - Added TLS/SSL support: Use a TLS/SSL encrypted connection to the server. Custom TLS configs can be registered and used - -Bugfixes: - - - Fixed MySQL 4.1 support: MySQL 4.1 sends packets with lengths which differ from the specification - - Convert to DB timezone when inserting `time.Time` - - Splitted packets (more than 16MB) are now merged correctly - - Fixed false positive `io.EOF` errors when the data was fully read - - Avoid panics on reuse of closed connections - - Fixed empty string producing false nil values - - Fixed sign byte for positive TIME fields - - -## Version 1.0 (2013-05-14) - -Initial Release diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/CONTRIBUTING.md b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/CONTRIBUTING.md deleted file mode 100644 index f87c19824..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/CONTRIBUTING.md +++ /dev/null @@ -1,40 +0,0 @@ -# Contributing Guidelines - -## Reporting Issues - -Before creating a new Issue, please check first if a similar Issue [already exists](https://github.com/go-sql-driver/mysql/issues?state=open) or was [recently closed](https://github.com/go-sql-driver/mysql/issues?direction=desc&page=1&sort=updated&state=closed). - -Please provide the following minimum information: -* Your Go-MySQL-Driver version (or git SHA) -* Your Go version (run `go version` in your console) -* A detailed issue description -* Error Log if present -* If possible, a short example - - -## Contributing Code - -By contributing to this project, you share your code under the Mozilla Public License 2, as specified in the LICENSE file. -Don't forget to add yourself to the AUTHORS file. - -### Pull Requests Checklist - -Please check the following points before submitting your pull request: -- [x] Code compiles correctly -- [x] Created tests, if possible -- [x] All tests pass -- [x] Extended the README / documentation, if necessary -- [x] Added yourself to the AUTHORS file - -### Code Review - -Everyone is invited to review and comment on pull requests. -If it looks fine to you, comment with "LGTM" (Looks good to me). - -If changes are required, notice the reviewers with "PTAL" (Please take another look) after committing the fixes. - -Before merging the Pull Request, at least one [team member](https://github.com/go-sql-driver?tab=members) must have commented with "LGTM". - -## Development Ideas - -If you are looking for ideas for code contributions, please check our [Development Ideas](https://github.com/go-sql-driver/mysql/wiki/Development-Ideas) Wiki page. diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE deleted file mode 100644 index 14e2f777f..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/README.md b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/README.md deleted file mode 100644 index 72b921eaf..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/README.md +++ /dev/null @@ -1,386 +0,0 @@ -# Go-MySQL-Driver - -A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) package - -![Go-MySQL-Driver logo](https://raw.github.com/wiki/go-sql-driver/mysql/gomysql_m.png "Golang Gopher holding the MySQL Dolphin") - -**Latest stable Release:** [Version 1.2 (June 03, 2014)](https://github.com/go-sql-driver/mysql/releases) - -[![Build Status](https://travis-ci.org/go-sql-driver/mysql.png?branch=master)](https://travis-ci.org/go-sql-driver/mysql) - ---------------------------------------- - * [Features](#features) - * [Requirements](#requirements) - * [Installation](#installation) - * [Usage](#usage) - * [DSN (Data Source Name)](#dsn-data-source-name) - * [Password](#password) - * [Protocol](#protocol) - * [Address](#address) - * [Parameters](#parameters) - * [Examples](#examples) - * [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support) - * [time.Time support](#timetime-support) - * [Unicode support](#unicode-support) - * [Testing / Development](#testing--development) - * [License](#license) - ---------------------------------------- - -## Features - * Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance") - * Native Go implementation. No C-bindings, just pure Go - * Connections over TCP/IPv4, TCP/IPv6, Unix domain sockets or [custom protocols](http://godoc.org/github.com/go-sql-driver/mysql#DialFunc) - * Automatic handling of broken connections - * Automatic Connection Pooling *(by database/sql package)* - * Supports queries larger than 16MB - * Full [`sql.RawBytes`](http://golang.org/pkg/database/sql/#RawBytes) support. - * Intelligent `LONG DATA` handling in prepared statements - * Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support - * Optional `time.Time` parsing - * Optional placeholder interpolation - -## Requirements - * Go 1.2 or higher - * MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+) - ---------------------------------------- - -## Installation -Simple install the package to your [$GOPATH](http://code.google.com/p/go-wiki/wiki/GOPATH "GOPATH") with the [go tool](http://golang.org/cmd/go/ "go command") from shell: -```bash -$ go get github.com/go-sql-driver/mysql -``` -Make sure [Git is installed](http://git-scm.com/downloads) on your machine and in your system's `PATH`. - -## Usage -_Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](http://golang.org/pkg/database/sql) API then. - -Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`: -```go -import "database/sql" -import _ "github.com/go-sql-driver/mysql" - -db, err := sql.Open("mysql", "user:password@/dbname") -``` - -[Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples"). - - -### DSN (Data Source Name) - -The Data Source Name has a common format, like e.g. [PEAR DB](http://pear.php.net/manual/en/package.database.db.intro-dsn.php) uses it, but without type-prefix (optional parts marked by squared brackets): -``` -[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN] -``` - -A DSN in its fullest form: -``` -username:password@protocol(address)/dbname?param=value -``` - -Except for the databasename, all values are optional. So the minimal DSN is: -``` -/dbname -``` - -If you do not want to preselect a database, leave `dbname` empty: -``` -/ -``` -This has the same effect as an empty DSN string: -``` - -``` - -#### Password -Passwords can consist of any character. Escaping is **not** necessary. - -#### Protocol -See [net.Dial](http://golang.org/pkg/net/#Dial) for more information which networks are available. -In general you should use an Unix domain socket if available and TCP otherwise for best performance. - -#### Address -For TCP and UDP networks, addresses have the form `host:port`. -If `host` is a literal IPv6 address, it must be enclosed in square brackets. -The functions [net.JoinHostPort](http://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](http://golang.org/pkg/net/#SplitHostPort) manipulate addresses in this form. - -For Unix domain sockets the address is the absolute path to the MySQL-Server-socket, e.g. `/var/run/mysqld/mysqld.sock` or `/tmp/mysql.sock`. - -#### Parameters -*Parameters are case-sensitive!* - -Notice that any of `true`, `TRUE`, `True` or `1` is accepted to stand for a true boolean value. Not surprisingly, false can be specified as any of: `false`, `FALSE`, `False` or `0`. - -##### `allowAllFiles` - -``` -Type: bool -Valid Values: true, false -Default: false -``` - -`allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files. -[*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html) - -##### `allowCleartextPasswords` - -``` -Type: bool -Valid Values: true, false -Default: false -``` - -`allowCleartextPasswords=true` allows using the [cleartext client side plugin](http://dev.mysql.com/doc/en/cleartext-authentication-plugin.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network. - -##### `allowOldPasswords` - -``` -Type: bool -Valid Values: true, false -Default: false -``` -`allowOldPasswords=true` allows the usage of the insecure old password method. This should be avoided, but is necessary in some cases. See also [the old_passwords wiki page](https://github.com/go-sql-driver/mysql/wiki/old_passwords). - -##### `charset` - -``` -Type: string -Valid Values: -Default: none -``` - -Sets the charset used for client-server interaction (`"SET NAMES "`). If multiple charsets are set (separated by a comma), the following charset is used if setting the charset failes. This enables for example support for `utf8mb4` ([introduced in MySQL 5.5.3](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)) with fallback to `utf8` for older servers (`charset=utf8mb4,utf8`). - -Usage of the `charset` parameter is discouraged because it issues additional queries to the server. -Unless you need the fallback behavior, please use `collation` instead. - -##### `collation` - -``` -Type: string -Valid Values: -Default: utf8_general_ci -``` - -Sets the collation used for client-server interaction on connection. In contrast to `charset`, `collation` does not issue additional queries. If the specified collation is unavailable on the target server, the connection will fail. - -A list of valid charsets for a server is retrievable with `SHOW COLLATION`. - -##### `clientFoundRows` - -``` -Type: bool -Valid Values: true, false -Default: false -``` - -`clientFoundRows=true` causes an UPDATE to return the number of matching rows instead of the number of rows changed. - -##### `columnsWithAlias` - -``` -Type: bool -Valid Values: true, false -Default: false -``` - -When `columnsWithAlias` is true, calls to `sql.Rows.Columns()` will return the table alias and the column name separated by a dot. For example: - -``` -SELECT u.id FROM users as u -``` - -will return `u.id` instead of just `id` if `columnsWithAlias=true`. - -##### `interpolateParams` - -``` -Type: bool -Valid Values: true, false -Default: false -``` - -If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`. - -*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are blacklisted as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!* - -##### `loc` - -``` -Type: string -Valid Values: -Default: UTC -``` - -Sets the location for time.Time values (when using `parseTime=true`). *"Local"* sets the system's location. See [time.LoadLocation](http://golang.org/pkg/time/#LoadLocation) for details. - -Note that this sets the location for time.Time values but does not change MySQL's [time_zone setting](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html). For that see the [time_zone system variable](#system-variables), which can also be set as a DSN parameter. - -Please keep in mind, that param values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`. - - -##### `parseTime` - -``` -Type: bool -Valid Values: true, false -Default: false -``` - -`parseTime=true` changes the output type of `DATE` and `DATETIME` values to `time.Time` instead of `[]byte` / `string` - - -##### `strict` - -``` -Type: bool -Valid Values: true, false -Default: false -``` - -`strict=true` enables the strict mode in which MySQL warnings are treated as errors. - -By default MySQL also treats notes as warnings. Use [`sql_notes=false`](http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_notes) to ignore notes. See the [examples](#examples) for an DSN example. - - -##### `timeout` - -``` -Type: decimal number -Default: OS default -``` - -*Driver* side connection timeout. The value must be a string of decimal numbers, each with optional fraction and a unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout). - - -##### `tls` - -``` -Type: bool / string -Valid Values: true, false, skip-verify, -Default: false -``` - -`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](http://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig). - - -##### System Variables - -All other parameters are interpreted as system variables: - * `autocommit`: `"SET autocommit="` - * [`time_zone`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `"SET time_zone="` - * [`tx_isolation`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `"SET tx_isolation="` - * `param`: `"SET ="` - -*The values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!* - -#### Examples -``` -user@unix(/path/to/socket)/dbname -``` - -``` -root:pw@unix(/tmp/mysql.sock)/myDatabase?loc=Local -``` - -``` -user:password@tcp(localhost:5555)/dbname?tls=skip-verify&autocommit=true -``` - -Use the [strict mode](#strict) but ignore notes: -``` -user:password@/dbname?strict=true&sql_notes=false -``` - -TCP via IPv6: -``` -user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname?timeout=90s&collation=utf8mb4_unicode_ci -``` - -TCP on a remote host, e.g. Amazon RDS: -``` -id:password@tcp(your-amazonaws-uri.com:3306)/dbname -``` - -Google Cloud SQL on App Engine: -``` -user@cloudsql(project-id:instance-name)/dbname -``` - -TCP using default port (3306) on localhost: -``` -user:password@tcp/dbname?charset=utf8mb4,utf8&sys_var=esc%40ped -``` - -Use the default protocol (tcp) and host (localhost:3306): -``` -user:password@/dbname -``` - -No Database preselected: -``` -user:password@/ -``` - -### `LOAD DATA LOCAL INFILE` support -For this feature you need direct access to the package. Therefore you must change the import path (no `_`): -```go -import "github.com/go-sql-driver/mysql" -``` - -Files must be whitelisted by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the Whitelist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)). - -To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::` then. - -See the [godoc of Go-MySQL-Driver](http://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") for details. - - -### `time.Time` support -The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your programm. - -However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` variables, which is the logical opposite in Go to `DATE` and `DATETIME` in MySQL. You can do that by changing the internal output type from `[]byte` to `time.Time` with the DSN parameter `parseTime=true`. You can set the default [`time.Time` location](http://golang.org/pkg/time/#Location) with the `loc` DSN parameter. - -**Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes). - -Alternatively you can use the [`NullTime`](http://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`. - - -### Unicode support -Since version 1.1 Go-MySQL-Driver automatically uses the collation `utf8_general_ci` by default. - -Other collations / charsets can be set using the [`collation`](#collation) DSN parameter. - -Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAMES utf8`) to the DSN to enable proper UTF-8 support. This is not necessary anymore. The [`collation`](#collation) parameter should be preferred to set another collation / charset than the default. - -See http://dev.mysql.com/doc/refman/5.7/en/charset-unicode.html for more details on MySQL's Unicode support. - - -## Testing / Development -To run the driver tests you may need to adjust the configuration. See the [Testing Wiki-Page](https://github.com/go-sql-driver/mysql/wiki/Testing "Testing") for details. - -Go-MySQL-Driver is not feature-complete yet. Your help is very appreciated. -If you want to contribute, you can work on an [open issue](https://github.com/go-sql-driver/mysql/issues?state=open) or review a [pull request](https://github.com/go-sql-driver/mysql/pulls). - -See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/CONTRIBUTING.md) for details. - ---------------------------------------- - -## License -Go-MySQL-Driver is licensed under the [Mozilla Public License Version 2.0](https://raw.github.com/go-sql-driver/mysql/master/LICENSE) - -Mozilla summarizes the license scope as follows: -> MPL: The copyleft applies to any files containing MPLed code. - - -That means: - * You can **use** the **unchanged** source code both in private and commercially - * When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0) - * You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged** - -Please read the [MPL 2.0 FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html) if you have further questions regarding the license. - -You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE) - -![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow") - diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/appengine.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/appengine.go deleted file mode 100644 index 565614eef..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/appengine.go +++ /dev/null @@ -1,19 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -// +build appengine - -package mysql - -import ( - "appengine/cloudsql" -) - -func init() { - RegisterDial("cloudsql", cloudsql.Dial) -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/benchmark_test.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/benchmark_test.go deleted file mode 100644 index fb8a2f5f3..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/benchmark_test.go +++ /dev/null @@ -1,246 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "bytes" - "database/sql" - "database/sql/driver" - "math" - "strings" - "sync" - "sync/atomic" - "testing" - "time" -) - -type TB testing.B - -func (tb *TB) check(err error) { - if err != nil { - tb.Fatal(err) - } -} - -func (tb *TB) checkDB(db *sql.DB, err error) *sql.DB { - tb.check(err) - return db -} - -func (tb *TB) checkRows(rows *sql.Rows, err error) *sql.Rows { - tb.check(err) - return rows -} - -func (tb *TB) checkStmt(stmt *sql.Stmt, err error) *sql.Stmt { - tb.check(err) - return stmt -} - -func initDB(b *testing.B, queries ...string) *sql.DB { - tb := (*TB)(b) - db := tb.checkDB(sql.Open("mysql", dsn)) - for _, query := range queries { - if _, err := db.Exec(query); err != nil { - if w, ok := err.(MySQLWarnings); ok { - b.Logf("Warning on %q: %v", query, w) - } else { - b.Fatalf("Error on %q: %v", query, err) - } - } - } - return db -} - -const concurrencyLevel = 10 - -func BenchmarkQuery(b *testing.B) { - tb := (*TB)(b) - b.StopTimer() - b.ReportAllocs() - db := initDB(b, - "DROP TABLE IF EXISTS foo", - "CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))", - `INSERT INTO foo VALUES (1, "one")`, - `INSERT INTO foo VALUES (2, "two")`, - ) - db.SetMaxIdleConns(concurrencyLevel) - defer db.Close() - - stmt := tb.checkStmt(db.Prepare("SELECT val FROM foo WHERE id=?")) - defer stmt.Close() - - remain := int64(b.N) - var wg sync.WaitGroup - wg.Add(concurrencyLevel) - defer wg.Wait() - b.StartTimer() - - for i := 0; i < concurrencyLevel; i++ { - go func() { - for { - if atomic.AddInt64(&remain, -1) < 0 { - wg.Done() - return - } - - var got string - tb.check(stmt.QueryRow(1).Scan(&got)) - if got != "one" { - b.Errorf("query = %q; want one", got) - wg.Done() - return - } - } - }() - } -} - -func BenchmarkExec(b *testing.B) { - tb := (*TB)(b) - b.StopTimer() - b.ReportAllocs() - db := tb.checkDB(sql.Open("mysql", dsn)) - db.SetMaxIdleConns(concurrencyLevel) - defer db.Close() - - stmt := tb.checkStmt(db.Prepare("DO 1")) - defer stmt.Close() - - remain := int64(b.N) - var wg sync.WaitGroup - wg.Add(concurrencyLevel) - defer wg.Wait() - b.StartTimer() - - for i := 0; i < concurrencyLevel; i++ { - go func() { - for { - if atomic.AddInt64(&remain, -1) < 0 { - wg.Done() - return - } - - if _, err := stmt.Exec(); err != nil { - b.Fatal(err.Error()) - } - } - }() - } -} - -// data, but no db writes -var roundtripSample []byte - -func initRoundtripBenchmarks() ([]byte, int, int) { - if roundtripSample == nil { - roundtripSample = []byte(strings.Repeat("0123456789abcdef", 1024*1024)) - } - return roundtripSample, 16, len(roundtripSample) -} - -func BenchmarkRoundtripTxt(b *testing.B) { - b.StopTimer() - sample, min, max := initRoundtripBenchmarks() - sampleString := string(sample) - b.ReportAllocs() - tb := (*TB)(b) - db := tb.checkDB(sql.Open("mysql", dsn)) - defer db.Close() - b.StartTimer() - var result string - for i := 0; i < b.N; i++ { - length := min + i - if length > max { - length = max - } - test := sampleString[0:length] - rows := tb.checkRows(db.Query(`SELECT "` + test + `"`)) - if !rows.Next() { - rows.Close() - b.Fatalf("crashed") - } - err := rows.Scan(&result) - if err != nil { - rows.Close() - b.Fatalf("crashed") - } - if result != test { - rows.Close() - b.Errorf("mismatch") - } - rows.Close() - } -} - -func BenchmarkRoundtripBin(b *testing.B) { - b.StopTimer() - sample, min, max := initRoundtripBenchmarks() - b.ReportAllocs() - tb := (*TB)(b) - db := tb.checkDB(sql.Open("mysql", dsn)) - defer db.Close() - stmt := tb.checkStmt(db.Prepare("SELECT ?")) - defer stmt.Close() - b.StartTimer() - var result sql.RawBytes - for i := 0; i < b.N; i++ { - length := min + i - if length > max { - length = max - } - test := sample[0:length] - rows := tb.checkRows(stmt.Query(test)) - if !rows.Next() { - rows.Close() - b.Fatalf("crashed") - } - err := rows.Scan(&result) - if err != nil { - rows.Close() - b.Fatalf("crashed") - } - if !bytes.Equal(result, test) { - rows.Close() - b.Errorf("mismatch") - } - rows.Close() - } -} - -func BenchmarkInterpolation(b *testing.B) { - mc := &mysqlConn{ - cfg: &config{ - interpolateParams: true, - loc: time.UTC, - }, - maxPacketAllowed: maxPacketSize, - maxWriteSize: maxPacketSize - 1, - buf: newBuffer(nil), - } - - args := []driver.Value{ - int64(42424242), - float64(math.Pi), - false, - time.Unix(1423411542, 807015000), - []byte("bytes containing special chars ' \" \a \x00"), - "string containing special chars ' \" \a \x00", - } - q := "SELECT ?, ?, ?, ?, ?, ?" - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := mc.interpolateParams(q, args) - if err != nil { - b.Fatal(err) - } - } -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go deleted file mode 100644 index 509ce89e4..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go +++ /dev/null @@ -1,136 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import "io" - -const defaultBufSize = 4096 - -// A buffer which is used for both reading and writing. -// This is possible since communication on each connection is synchronous. -// In other words, we can't write and read simultaneously on the same connection. -// The buffer is similar to bufio.Reader / Writer but zero-copy-ish -// Also highly optimized for this particular use case. -type buffer struct { - buf []byte - rd io.Reader - idx int - length int -} - -func newBuffer(rd io.Reader) buffer { - var b [defaultBufSize]byte - return buffer{ - buf: b[:], - rd: rd, - } -} - -// fill reads into the buffer until at least _need_ bytes are in it -func (b *buffer) fill(need int) error { - n := b.length - - // move existing data to the beginning - if n > 0 && b.idx > 0 { - copy(b.buf[0:n], b.buf[b.idx:]) - } - - // grow buffer if necessary - // TODO: let the buffer shrink again at some point - // Maybe keep the org buf slice and swap back? - if need > len(b.buf) { - // Round up to the next multiple of the default size - newBuf := make([]byte, ((need/defaultBufSize)+1)*defaultBufSize) - copy(newBuf, b.buf) - b.buf = newBuf - } - - b.idx = 0 - - for { - nn, err := b.rd.Read(b.buf[n:]) - n += nn - - switch err { - case nil: - if n < need { - continue - } - b.length = n - return nil - - case io.EOF: - if n >= need { - b.length = n - return nil - } - return io.ErrUnexpectedEOF - - default: - return err - } - } -} - -// returns next N bytes from buffer. -// The returned slice is only guaranteed to be valid until the next read -func (b *buffer) readNext(need int) ([]byte, error) { - if b.length < need { - // refill - if err := b.fill(need); err != nil { - return nil, err - } - } - - offset := b.idx - b.idx += need - b.length -= need - return b.buf[offset:b.idx], nil -} - -// returns a buffer with the requested size. -// If possible, a slice from the existing buffer is returned. -// Otherwise a bigger buffer is made. -// Only one buffer (total) can be used at a time. -func (b *buffer) takeBuffer(length int) []byte { - if b.length > 0 { - return nil - } - - // test (cheap) general case first - if length <= defaultBufSize || length <= cap(b.buf) { - return b.buf[:length] - } - - if length < maxPacketSize { - b.buf = make([]byte, length) - return b.buf - } - return make([]byte, length) -} - -// shortcut which can be used if the requested buffer is guaranteed to be -// smaller than defaultBufSize -// Only one buffer (total) can be used at a time. -func (b *buffer) takeSmallBuffer(length int) []byte { - if b.length == 0 { - return b.buf[:length] - } - return nil -} - -// takeCompleteBuffer returns the complete existing buffer. -// This can be used if the necessary buffer size is unknown. -// Only one buffer (total) can be used at a time. -func (b *buffer) takeCompleteBuffer() []byte { - if b.length == 0 { - return b.buf - } - return nil -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/collations.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/collations.go deleted file mode 100644 index 6c1d613d5..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/collations.go +++ /dev/null @@ -1,250 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2014 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -const defaultCollation byte = 33 // utf8_general_ci - -// A list of available collations mapped to the internal ID. -// To update this map use the following MySQL query: -// SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS -var collations = map[string]byte{ - "big5_chinese_ci": 1, - "latin2_czech_cs": 2, - "dec8_swedish_ci": 3, - "cp850_general_ci": 4, - "latin1_german1_ci": 5, - "hp8_english_ci": 6, - "koi8r_general_ci": 7, - "latin1_swedish_ci": 8, - "latin2_general_ci": 9, - "swe7_swedish_ci": 10, - "ascii_general_ci": 11, - "ujis_japanese_ci": 12, - "sjis_japanese_ci": 13, - "cp1251_bulgarian_ci": 14, - "latin1_danish_ci": 15, - "hebrew_general_ci": 16, - "tis620_thai_ci": 18, - "euckr_korean_ci": 19, - "latin7_estonian_cs": 20, - "latin2_hungarian_ci": 21, - "koi8u_general_ci": 22, - "cp1251_ukrainian_ci": 23, - "gb2312_chinese_ci": 24, - "greek_general_ci": 25, - "cp1250_general_ci": 26, - "latin2_croatian_ci": 27, - "gbk_chinese_ci": 28, - "cp1257_lithuanian_ci": 29, - "latin5_turkish_ci": 30, - "latin1_german2_ci": 31, - "armscii8_general_ci": 32, - "utf8_general_ci": 33, - "cp1250_czech_cs": 34, - "ucs2_general_ci": 35, - "cp866_general_ci": 36, - "keybcs2_general_ci": 37, - "macce_general_ci": 38, - "macroman_general_ci": 39, - "cp852_general_ci": 40, - "latin7_general_ci": 41, - "latin7_general_cs": 42, - "macce_bin": 43, - "cp1250_croatian_ci": 44, - "utf8mb4_general_ci": 45, - "utf8mb4_bin": 46, - "latin1_bin": 47, - "latin1_general_ci": 48, - "latin1_general_cs": 49, - "cp1251_bin": 50, - "cp1251_general_ci": 51, - "cp1251_general_cs": 52, - "macroman_bin": 53, - "utf16_general_ci": 54, - "utf16_bin": 55, - "utf16le_general_ci": 56, - "cp1256_general_ci": 57, - "cp1257_bin": 58, - "cp1257_general_ci": 59, - "utf32_general_ci": 60, - "utf32_bin": 61, - "utf16le_bin": 62, - "binary": 63, - "armscii8_bin": 64, - "ascii_bin": 65, - "cp1250_bin": 66, - "cp1256_bin": 67, - "cp866_bin": 68, - "dec8_bin": 69, - "greek_bin": 70, - "hebrew_bin": 71, - "hp8_bin": 72, - "keybcs2_bin": 73, - "koi8r_bin": 74, - "koi8u_bin": 75, - "latin2_bin": 77, - "latin5_bin": 78, - "latin7_bin": 79, - "cp850_bin": 80, - "cp852_bin": 81, - "swe7_bin": 82, - "utf8_bin": 83, - "big5_bin": 84, - "euckr_bin": 85, - "gb2312_bin": 86, - "gbk_bin": 87, - "sjis_bin": 88, - "tis620_bin": 89, - "ucs2_bin": 90, - "ujis_bin": 91, - "geostd8_general_ci": 92, - "geostd8_bin": 93, - "latin1_spanish_ci": 94, - "cp932_japanese_ci": 95, - "cp932_bin": 96, - "eucjpms_japanese_ci": 97, - "eucjpms_bin": 98, - "cp1250_polish_ci": 99, - "utf16_unicode_ci": 101, - "utf16_icelandic_ci": 102, - "utf16_latvian_ci": 103, - "utf16_romanian_ci": 104, - "utf16_slovenian_ci": 105, - "utf16_polish_ci": 106, - "utf16_estonian_ci": 107, - "utf16_spanish_ci": 108, - "utf16_swedish_ci": 109, - "utf16_turkish_ci": 110, - "utf16_czech_ci": 111, - "utf16_danish_ci": 112, - "utf16_lithuanian_ci": 113, - "utf16_slovak_ci": 114, - "utf16_spanish2_ci": 115, - "utf16_roman_ci": 116, - "utf16_persian_ci": 117, - "utf16_esperanto_ci": 118, - "utf16_hungarian_ci": 119, - "utf16_sinhala_ci": 120, - "utf16_german2_ci": 121, - "utf16_croatian_ci": 122, - "utf16_unicode_520_ci": 123, - "utf16_vietnamese_ci": 124, - "ucs2_unicode_ci": 128, - "ucs2_icelandic_ci": 129, - "ucs2_latvian_ci": 130, - "ucs2_romanian_ci": 131, - "ucs2_slovenian_ci": 132, - "ucs2_polish_ci": 133, - "ucs2_estonian_ci": 134, - "ucs2_spanish_ci": 135, - "ucs2_swedish_ci": 136, - "ucs2_turkish_ci": 137, - "ucs2_czech_ci": 138, - "ucs2_danish_ci": 139, - "ucs2_lithuanian_ci": 140, - "ucs2_slovak_ci": 141, - "ucs2_spanish2_ci": 142, - "ucs2_roman_ci": 143, - "ucs2_persian_ci": 144, - "ucs2_esperanto_ci": 145, - "ucs2_hungarian_ci": 146, - "ucs2_sinhala_ci": 147, - "ucs2_german2_ci": 148, - "ucs2_croatian_ci": 149, - "ucs2_unicode_520_ci": 150, - "ucs2_vietnamese_ci": 151, - "ucs2_general_mysql500_ci": 159, - "utf32_unicode_ci": 160, - "utf32_icelandic_ci": 161, - "utf32_latvian_ci": 162, - "utf32_romanian_ci": 163, - "utf32_slovenian_ci": 164, - "utf32_polish_ci": 165, - "utf32_estonian_ci": 166, - "utf32_spanish_ci": 167, - "utf32_swedish_ci": 168, - "utf32_turkish_ci": 169, - "utf32_czech_ci": 170, - "utf32_danish_ci": 171, - "utf32_lithuanian_ci": 172, - "utf32_slovak_ci": 173, - "utf32_spanish2_ci": 174, - "utf32_roman_ci": 175, - "utf32_persian_ci": 176, - "utf32_esperanto_ci": 177, - "utf32_hungarian_ci": 178, - "utf32_sinhala_ci": 179, - "utf32_german2_ci": 180, - "utf32_croatian_ci": 181, - "utf32_unicode_520_ci": 182, - "utf32_vietnamese_ci": 183, - "utf8_unicode_ci": 192, - "utf8_icelandic_ci": 193, - "utf8_latvian_ci": 194, - "utf8_romanian_ci": 195, - "utf8_slovenian_ci": 196, - "utf8_polish_ci": 197, - "utf8_estonian_ci": 198, - "utf8_spanish_ci": 199, - "utf8_swedish_ci": 200, - "utf8_turkish_ci": 201, - "utf8_czech_ci": 202, - "utf8_danish_ci": 203, - "utf8_lithuanian_ci": 204, - "utf8_slovak_ci": 205, - "utf8_spanish2_ci": 206, - "utf8_roman_ci": 207, - "utf8_persian_ci": 208, - "utf8_esperanto_ci": 209, - "utf8_hungarian_ci": 210, - "utf8_sinhala_ci": 211, - "utf8_german2_ci": 212, - "utf8_croatian_ci": 213, - "utf8_unicode_520_ci": 214, - "utf8_vietnamese_ci": 215, - "utf8_general_mysql500_ci": 223, - "utf8mb4_unicode_ci": 224, - "utf8mb4_icelandic_ci": 225, - "utf8mb4_latvian_ci": 226, - "utf8mb4_romanian_ci": 227, - "utf8mb4_slovenian_ci": 228, - "utf8mb4_polish_ci": 229, - "utf8mb4_estonian_ci": 230, - "utf8mb4_spanish_ci": 231, - "utf8mb4_swedish_ci": 232, - "utf8mb4_turkish_ci": 233, - "utf8mb4_czech_ci": 234, - "utf8mb4_danish_ci": 235, - "utf8mb4_lithuanian_ci": 236, - "utf8mb4_slovak_ci": 237, - "utf8mb4_spanish2_ci": 238, - "utf8mb4_roman_ci": 239, - "utf8mb4_persian_ci": 240, - "utf8mb4_esperanto_ci": 241, - "utf8mb4_hungarian_ci": 242, - "utf8mb4_sinhala_ci": 243, - "utf8mb4_german2_ci": 244, - "utf8mb4_croatian_ci": 245, - "utf8mb4_unicode_520_ci": 246, - "utf8mb4_vietnamese_ci": 247, -} - -// A blacklist of collations which is unsafe to interpolate parameters. -// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes. -var unsafeCollations = map[byte]bool{ - 1: true, // big5_chinese_ci - 13: true, // sjis_japanese_ci - 28: true, // gbk_chinese_ci - 84: true, // big5_bin - 86: true, // gb2312_bin - 87: true, // gbk_bin - 88: true, // sjis_bin - 95: true, // cp932_japanese_ci - 96: true, // cp932_bin -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go deleted file mode 100644 index caaae013f..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go +++ /dev/null @@ -1,403 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "crypto/tls" - "database/sql/driver" - "errors" - "net" - "strconv" - "strings" - "time" -) - -type mysqlConn struct { - buf buffer - netConn net.Conn - affectedRows uint64 - insertId uint64 - cfg *config - maxPacketAllowed int - maxWriteSize int - flags clientFlag - status statusFlag - sequence uint8 - parseTime bool - strict bool -} - -type config struct { - user string - passwd string - net string - addr string - dbname string - params map[string]string - loc *time.Location - tls *tls.Config - timeout time.Duration - collation uint8 - allowAllFiles bool - allowOldPasswords bool - allowCleartextPasswords bool - clientFoundRows bool - columnsWithAlias bool - interpolateParams bool -} - -// Handles parameters set in DSN after the connection is established -func (mc *mysqlConn) handleParams() (err error) { - for param, val := range mc.cfg.params { - switch param { - // Charset - case "charset": - charsets := strings.Split(val, ",") - for i := range charsets { - // ignore errors here - a charset may not exist - err = mc.exec("SET NAMES " + charsets[i]) - if err == nil { - break - } - } - if err != nil { - return - } - - // time.Time parsing - case "parseTime": - var isBool bool - mc.parseTime, isBool = readBool(val) - if !isBool { - return errors.New("Invalid Bool value: " + val) - } - - // Strict mode - case "strict": - var isBool bool - mc.strict, isBool = readBool(val) - if !isBool { - return errors.New("Invalid Bool value: " + val) - } - - // Compression - case "compress": - err = errors.New("Compression not implemented yet") - return - - // System Vars - default: - err = mc.exec("SET " + param + "=" + val + "") - if err != nil { - return - } - } - } - - return -} - -func (mc *mysqlConn) Begin() (driver.Tx, error) { - if mc.netConn == nil { - errLog.Print(ErrInvalidConn) - return nil, driver.ErrBadConn - } - err := mc.exec("START TRANSACTION") - if err == nil { - return &mysqlTx{mc}, err - } - - return nil, err -} - -func (mc *mysqlConn) Close() (err error) { - // Makes Close idempotent - if mc.netConn != nil { - err = mc.writeCommandPacket(comQuit) - if err == nil { - err = mc.netConn.Close() - } else { - mc.netConn.Close() - } - mc.netConn = nil - } - - mc.cfg = nil - mc.buf.rd = nil - - return -} - -func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) { - if mc.netConn == nil { - errLog.Print(ErrInvalidConn) - return nil, driver.ErrBadConn - } - // Send command - err := mc.writeCommandPacketStr(comStmtPrepare, query) - if err != nil { - return nil, err - } - - stmt := &mysqlStmt{ - mc: mc, - } - - // Read Result - columnCount, err := stmt.readPrepareResultPacket() - if err == nil { - if stmt.paramCount > 0 { - if err = mc.readUntilEOF(); err != nil { - return nil, err - } - } - - if columnCount > 0 { - err = mc.readUntilEOF() - } - } - - return stmt, err -} - -func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) { - buf := mc.buf.takeCompleteBuffer() - if buf == nil { - // can not take the buffer. Something must be wrong with the connection - errLog.Print(ErrBusyBuffer) - return "", driver.ErrBadConn - } - buf = buf[:0] - argPos := 0 - - for i := 0; i < len(query); i++ { - q := strings.IndexByte(query[i:], '?') - if q == -1 { - buf = append(buf, query[i:]...) - break - } - buf = append(buf, query[i:i+q]...) - i += q - - arg := args[argPos] - argPos++ - - if arg == nil { - buf = append(buf, "NULL"...) - continue - } - - switch v := arg.(type) { - case int64: - buf = strconv.AppendInt(buf, v, 10) - case float64: - buf = strconv.AppendFloat(buf, v, 'g', -1, 64) - case bool: - if v { - buf = append(buf, '1') - } else { - buf = append(buf, '0') - } - case time.Time: - if v.IsZero() { - buf = append(buf, "'0000-00-00'"...) - } else { - v := v.In(mc.cfg.loc) - v = v.Add(time.Nanosecond * 500) // To round under microsecond - year := v.Year() - year100 := year / 100 - year1 := year % 100 - month := v.Month() - day := v.Day() - hour := v.Hour() - minute := v.Minute() - second := v.Second() - micro := v.Nanosecond() / 1000 - - buf = append(buf, []byte{ - '\'', - digits10[year100], digits01[year100], - digits10[year1], digits01[year1], - '-', - digits10[month], digits01[month], - '-', - digits10[day], digits01[day], - ' ', - digits10[hour], digits01[hour], - ':', - digits10[minute], digits01[minute], - ':', - digits10[second], digits01[second], - }...) - - if micro != 0 { - micro10000 := micro / 10000 - micro100 := micro / 100 % 100 - micro1 := micro % 100 - buf = append(buf, []byte{ - '.', - digits10[micro10000], digits01[micro10000], - digits10[micro100], digits01[micro100], - digits10[micro1], digits01[micro1], - }...) - } - buf = append(buf, '\'') - } - case []byte: - if v == nil { - buf = append(buf, "NULL"...) - } else { - buf = append(buf, '\'') - if mc.status&statusNoBackslashEscapes == 0 { - buf = escapeBytesBackslash(buf, v) - } else { - buf = escapeBytesQuotes(buf, v) - } - buf = append(buf, '\'') - } - case string: - buf = append(buf, '\'') - if mc.status&statusNoBackslashEscapes == 0 { - buf = escapeStringBackslash(buf, v) - } else { - buf = escapeStringQuotes(buf, v) - } - buf = append(buf, '\'') - default: - return "", driver.ErrSkip - } - - if len(buf)+4 > mc.maxPacketAllowed { - return "", driver.ErrSkip - } - } - if argPos != len(args) { - return "", driver.ErrSkip - } - return string(buf), nil -} - -func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) { - if mc.netConn == nil { - errLog.Print(ErrInvalidConn) - return nil, driver.ErrBadConn - } - if len(args) != 0 { - if !mc.cfg.interpolateParams { - return nil, driver.ErrSkip - } - // try to interpolate the parameters to save extra roundtrips for preparing and closing a statement - prepared, err := mc.interpolateParams(query, args) - if err != nil { - return nil, err - } - query = prepared - args = nil - } - mc.affectedRows = 0 - mc.insertId = 0 - - err := mc.exec(query) - if err == nil { - return &mysqlResult{ - affectedRows: int64(mc.affectedRows), - insertId: int64(mc.insertId), - }, err - } - return nil, err -} - -// Internal function to execute commands -func (mc *mysqlConn) exec(query string) error { - // Send command - err := mc.writeCommandPacketStr(comQuery, query) - if err != nil { - return err - } - - // Read Result - resLen, err := mc.readResultSetHeaderPacket() - if err == nil && resLen > 0 { - if err = mc.readUntilEOF(); err != nil { - return err - } - - err = mc.readUntilEOF() - } - - return err -} - -func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) { - if mc.netConn == nil { - errLog.Print(ErrInvalidConn) - return nil, driver.ErrBadConn - } - if len(args) != 0 { - if !mc.cfg.interpolateParams { - return nil, driver.ErrSkip - } - // try client-side prepare to reduce roundtrip - prepared, err := mc.interpolateParams(query, args) - if err != nil { - return nil, err - } - query = prepared - args = nil - } - // Send command - err := mc.writeCommandPacketStr(comQuery, query) - if err == nil { - // Read Result - var resLen int - resLen, err = mc.readResultSetHeaderPacket() - if err == nil { - rows := new(textRows) - rows.mc = mc - - if resLen == 0 { - // no columns, no more data - return emptyRows{}, nil - } - // Columns - rows.columns, err = mc.readColumns(resLen) - return rows, err - } - } - return nil, err -} - -// Gets the value of the given MySQL System Variable -// The returned byte slice is only valid until the next read -func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) { - // Send command - if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil { - return nil, err - } - - // Read Result - resLen, err := mc.readResultSetHeaderPacket() - if err == nil { - rows := new(textRows) - rows.mc = mc - - if resLen > 0 { - // Columns - if err := mc.readUntilEOF(); err != nil { - return nil, err - } - } - - dest := make([]driver.Value, resLen) - if err = rows.readRow(dest); err == nil { - return dest[0].([]byte), mc.readUntilEOF() - } - } - return nil, err -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go deleted file mode 100644 index dddc12908..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go +++ /dev/null @@ -1,162 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -const ( - minProtocolVersion byte = 10 - maxPacketSize = 1<<24 - 1 - timeFormat = "2006-01-02 15:04:05.999999" -) - -// MySQL constants documentation: -// http://dev.mysql.com/doc/internals/en/client-server-protocol.html - -const ( - iOK byte = 0x00 - iLocalInFile byte = 0xfb - iEOF byte = 0xfe - iERR byte = 0xff -) - -// https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags -type clientFlag uint32 - -const ( - clientLongPassword clientFlag = 1 << iota - clientFoundRows - clientLongFlag - clientConnectWithDB - clientNoSchema - clientCompress - clientODBC - clientLocalFiles - clientIgnoreSpace - clientProtocol41 - clientInteractive - clientSSL - clientIgnoreSIGPIPE - clientTransactions - clientReserved - clientSecureConn - clientMultiStatements - clientMultiResults - clientPSMultiResults - clientPluginAuth - clientConnectAttrs - clientPluginAuthLenEncClientData - clientCanHandleExpiredPasswords - clientSessionTrack - clientDeprecateEOF -) - -const ( - comQuit byte = iota + 1 - comInitDB - comQuery - comFieldList - comCreateDB - comDropDB - comRefresh - comShutdown - comStatistics - comProcessInfo - comConnect - comProcessKill - comDebug - comPing - comTime - comDelayedInsert - comChangeUser - comBinlogDump - comTableDump - comConnectOut - comRegisterSlave - comStmtPrepare - comStmtExecute - comStmtSendLongData - comStmtClose - comStmtReset - comSetOption - comStmtFetch -) - -// https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType -const ( - fieldTypeDecimal byte = iota - fieldTypeTiny - fieldTypeShort - fieldTypeLong - fieldTypeFloat - fieldTypeDouble - fieldTypeNULL - fieldTypeTimestamp - fieldTypeLongLong - fieldTypeInt24 - fieldTypeDate - fieldTypeTime - fieldTypeDateTime - fieldTypeYear - fieldTypeNewDate - fieldTypeVarChar - fieldTypeBit -) -const ( - fieldTypeNewDecimal byte = iota + 0xf6 - fieldTypeEnum - fieldTypeSet - fieldTypeTinyBLOB - fieldTypeMediumBLOB - fieldTypeLongBLOB - fieldTypeBLOB - fieldTypeVarString - fieldTypeString - fieldTypeGeometry -) - -type fieldFlag uint16 - -const ( - flagNotNULL fieldFlag = 1 << iota - flagPriKey - flagUniqueKey - flagMultipleKey - flagBLOB - flagUnsigned - flagZeroFill - flagBinary - flagEnum - flagAutoIncrement - flagTimestamp - flagSet - flagUnknown1 - flagUnknown2 - flagUnknown3 - flagUnknown4 -) - -// http://dev.mysql.com/doc/internals/en/status-flags.html -type statusFlag uint16 - -const ( - statusInTrans statusFlag = 1 << iota - statusInAutocommit - statusReserved // Not in documentation - statusMoreResultsExists - statusNoGoodIndexUsed - statusNoIndexUsed - statusCursorExists - statusLastRowSent - statusDbDropped - statusNoBackslashEscapes - statusMetadataChanged - statusQueryWasSlow - statusPsOutParams - statusInTransReadonly - statusSessionStateChanged -) diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go deleted file mode 100644 index d310624ad..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// The driver should be used via the database/sql package: -// -// import "database/sql" -// import _ "github.com/go-sql-driver/mysql" -// -// db, err := sql.Open("mysql", "user:password@/dbname") -// -// See https://github.com/go-sql-driver/mysql#usage for details -package mysql - -import ( - "database/sql" - "database/sql/driver" - "net" -) - -// This struct is exported to make the driver directly accessible. -// In general the driver is used via the database/sql package. -type MySQLDriver struct{} - -// DialFunc is a function which can be used to establish the network connection. -// Custom dial functions must be registered with RegisterDial -type DialFunc func(addr string) (net.Conn, error) - -var dials map[string]DialFunc - -// RegisterDial registers a custom dial function. It can then be used by the -// network address mynet(addr), where mynet is the registered new network. -// addr is passed as a parameter to the dial function. -func RegisterDial(net string, dial DialFunc) { - if dials == nil { - dials = make(map[string]DialFunc) - } - dials[net] = dial -} - -// Open new Connection. -// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how -// the DSN string is formated -func (d MySQLDriver) Open(dsn string) (driver.Conn, error) { - var err error - - // New mysqlConn - mc := &mysqlConn{ - maxPacketAllowed: maxPacketSize, - maxWriteSize: maxPacketSize - 1, - } - mc.cfg, err = parseDSN(dsn) - if err != nil { - return nil, err - } - - // Connect to Server - if dial, ok := dials[mc.cfg.net]; ok { - mc.netConn, err = dial(mc.cfg.addr) - } else { - nd := net.Dialer{Timeout: mc.cfg.timeout} - mc.netConn, err = nd.Dial(mc.cfg.net, mc.cfg.addr) - } - if err != nil { - return nil, err - } - - // Enable TCP Keepalives on TCP connections - if tc, ok := mc.netConn.(*net.TCPConn); ok { - if err := tc.SetKeepAlive(true); err != nil { - // Don't send COM_QUIT before handshake. - mc.netConn.Close() - mc.netConn = nil - return nil, err - } - } - - mc.buf = newBuffer(mc.netConn) - - // Reading Handshake Initialization Packet - cipher, err := mc.readInitPacket() - if err != nil { - mc.Close() - return nil, err - } - - // Send Client Authentication Packet - if err = mc.writeAuthPacket(cipher); err != nil { - mc.Close() - return nil, err - } - - // Read Result Packet - err = mc.readResultOK() - if err != nil { - // Retry with old authentication method, if allowed - if mc.cfg != nil && mc.cfg.allowOldPasswords && err == ErrOldPassword { - if err = mc.writeOldAuthPacket(cipher); err != nil { - mc.Close() - return nil, err - } - if err = mc.readResultOK(); err != nil { - mc.Close() - return nil, err - } - } else if mc.cfg != nil && mc.cfg.allowCleartextPasswords && err == ErrCleartextPassword { - if err = mc.writeClearAuthPacket(); err != nil { - mc.Close() - return nil, err - } - if err = mc.readResultOK(); err != nil { - mc.Close() - return nil, err - } - } else { - mc.Close() - return nil, err - } - - } - - // Get max allowed packet size - maxap, err := mc.getSystemVar("max_allowed_packet") - if err != nil { - mc.Close() - return nil, err - } - mc.maxPacketAllowed = stringToInt(maxap) - 1 - if mc.maxPacketAllowed < maxPacketSize { - mc.maxWriteSize = mc.maxPacketAllowed - } - - // Handle DSN Params - err = mc.handleParams() - if err != nil { - mc.Close() - return nil, err - } - - return mc, nil -} - -func init() { - sql.Register("mysql", &MySQLDriver{}) -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go deleted file mode 100644 index f9da416ec..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go +++ /dev/null @@ -1,1681 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "crypto/tls" - "database/sql" - "database/sql/driver" - "fmt" - "io" - "io/ioutil" - "net" - "net/url" - "os" - "strings" - "sync" - "sync/atomic" - "testing" - "time" -) - -var ( - user string - pass string - prot string - addr string - dbname string - dsn string - netAddr string - available bool -) - -var ( - tDate = time.Date(2012, 6, 14, 0, 0, 0, 0, time.UTC) - sDate = "2012-06-14" - tDateTime = time.Date(2011, 11, 20, 21, 27, 37, 0, time.UTC) - sDateTime = "2011-11-20 21:27:37" - tDate0 = time.Time{} - sDate0 = "0000-00-00" - sDateTime0 = "0000-00-00 00:00:00" -) - -// See https://github.com/go-sql-driver/mysql/wiki/Testing -func init() { - // get environment variables - env := func(key, defaultValue string) string { - if value := os.Getenv(key); value != "" { - return value - } - return defaultValue - } - user = env("MYSQL_TEST_USER", "root") - pass = env("MYSQL_TEST_PASS", "") - prot = env("MYSQL_TEST_PROT", "tcp") - addr = env("MYSQL_TEST_ADDR", "localhost:3306") - dbname = env("MYSQL_TEST_DBNAME", "gotest") - netAddr = fmt.Sprintf("%s(%s)", prot, addr) - dsn = fmt.Sprintf("%s:%s@%s/%s?timeout=30s&strict=true", user, pass, netAddr, dbname) - c, err := net.Dial(prot, addr) - if err == nil { - available = true - c.Close() - } -} - -type DBTest struct { - *testing.T - db *sql.DB -} - -func runTests(t *testing.T, dsn string, tests ...func(dbt *DBTest)) { - if !available { - t.Skipf("MySQL-Server not running on %s", netAddr) - } - - db, err := sql.Open("mysql", dsn) - if err != nil { - t.Fatalf("Error connecting: %s", err.Error()) - } - defer db.Close() - - db.Exec("DROP TABLE IF EXISTS test") - - dsn2 := dsn + "&interpolateParams=true" - var db2 *sql.DB - if _, err := parseDSN(dsn2); err != errInvalidDSNUnsafeCollation { - db2, err = sql.Open("mysql", dsn2) - if err != nil { - t.Fatalf("Error connecting: %s", err.Error()) - } - defer db2.Close() - } - - dbt := &DBTest{t, db} - dbt2 := &DBTest{t, db2} - for _, test := range tests { - test(dbt) - dbt.db.Exec("DROP TABLE IF EXISTS test") - if db2 != nil { - test(dbt2) - dbt2.db.Exec("DROP TABLE IF EXISTS test") - } - } -} - -func (dbt *DBTest) fail(method, query string, err error) { - if len(query) > 300 { - query = "[query too large to print]" - } - dbt.Fatalf("Error on %s %s: %s", method, query, err.Error()) -} - -func (dbt *DBTest) mustExec(query string, args ...interface{}) (res sql.Result) { - res, err := dbt.db.Exec(query, args...) - if err != nil { - dbt.fail("Exec", query, err) - } - return res -} - -func (dbt *DBTest) mustQuery(query string, args ...interface{}) (rows *sql.Rows) { - rows, err := dbt.db.Query(query, args...) - if err != nil { - dbt.fail("Query", query, err) - } - return rows -} - -func TestEmptyQuery(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - // just a comment, no query - rows := dbt.mustQuery("--") - // will hang before #255 - if rows.Next() { - dbt.Errorf("Next on rows must be false") - } - }) -} - -func TestCRUD(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - // Create Table - dbt.mustExec("CREATE TABLE test (value BOOL)") - - // Test for unexpected data - var out bool - rows := dbt.mustQuery("SELECT * FROM test") - if rows.Next() { - dbt.Error("unexpected data in empty table") - } - - // Create Data - res := dbt.mustExec("INSERT INTO test VALUES (1)") - count, err := res.RowsAffected() - if err != nil { - dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) - } - if count != 1 { - dbt.Fatalf("Expected 1 affected row, got %d", count) - } - - id, err := res.LastInsertId() - if err != nil { - dbt.Fatalf("res.LastInsertId() returned error: %s", err.Error()) - } - if id != 0 { - dbt.Fatalf("Expected InsertID 0, got %d", id) - } - - // Read - rows = dbt.mustQuery("SELECT value FROM test") - if rows.Next() { - rows.Scan(&out) - if true != out { - dbt.Errorf("true != %t", out) - } - - if rows.Next() { - dbt.Error("unexpected data") - } - } else { - dbt.Error("no data") - } - - // Update - res = dbt.mustExec("UPDATE test SET value = ? WHERE value = ?", false, true) - count, err = res.RowsAffected() - if err != nil { - dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) - } - if count != 1 { - dbt.Fatalf("Expected 1 affected row, got %d", count) - } - - // Check Update - rows = dbt.mustQuery("SELECT value FROM test") - if rows.Next() { - rows.Scan(&out) - if false != out { - dbt.Errorf("false != %t", out) - } - - if rows.Next() { - dbt.Error("unexpected data") - } - } else { - dbt.Error("no data") - } - - // Delete - res = dbt.mustExec("DELETE FROM test WHERE value = ?", false) - count, err = res.RowsAffected() - if err != nil { - dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) - } - if count != 1 { - dbt.Fatalf("Expected 1 affected row, got %d", count) - } - - // Check for unexpected rows - res = dbt.mustExec("DELETE FROM test") - count, err = res.RowsAffected() - if err != nil { - dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) - } - if count != 0 { - dbt.Fatalf("Expected 0 affected row, got %d", count) - } - }) -} - -func TestInt(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - types := [5]string{"TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT"} - in := int64(42) - var out int64 - var rows *sql.Rows - - // SIGNED - for _, v := range types { - dbt.mustExec("CREATE TABLE test (value " + v + ")") - - dbt.mustExec("INSERT INTO test VALUES (?)", in) - - rows = dbt.mustQuery("SELECT value FROM test") - if rows.Next() { - rows.Scan(&out) - if in != out { - dbt.Errorf("%s: %d != %d", v, in, out) - } - } else { - dbt.Errorf("%s: no data", v) - } - - dbt.mustExec("DROP TABLE IF EXISTS test") - } - - // UNSIGNED ZEROFILL - for _, v := range types { - dbt.mustExec("CREATE TABLE test (value " + v + " ZEROFILL)") - - dbt.mustExec("INSERT INTO test VALUES (?)", in) - - rows = dbt.mustQuery("SELECT value FROM test") - if rows.Next() { - rows.Scan(&out) - if in != out { - dbt.Errorf("%s ZEROFILL: %d != %d", v, in, out) - } - } else { - dbt.Errorf("%s ZEROFILL: no data", v) - } - - dbt.mustExec("DROP TABLE IF EXISTS test") - } - }) -} - -func TestFloat(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - types := [2]string{"FLOAT", "DOUBLE"} - in := float32(42.23) - var out float32 - var rows *sql.Rows - for _, v := range types { - dbt.mustExec("CREATE TABLE test (value " + v + ")") - dbt.mustExec("INSERT INTO test VALUES (?)", in) - rows = dbt.mustQuery("SELECT value FROM test") - if rows.Next() { - rows.Scan(&out) - if in != out { - dbt.Errorf("%s: %g != %g", v, in, out) - } - } else { - dbt.Errorf("%s: no data", v) - } - dbt.mustExec("DROP TABLE IF EXISTS test") - } - }) -} - -func TestString(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - types := [6]string{"CHAR(255)", "VARCHAR(255)", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT"} - in := "κόσμε üöäßñóùéàâÿœ'îë Árvíztűrő いろはにほへとちりぬるを イロハニホヘト דג סקרן чащах น่าฟังเอย" - var out string - var rows *sql.Rows - - for _, v := range types { - dbt.mustExec("CREATE TABLE test (value " + v + ") CHARACTER SET utf8") - - dbt.mustExec("INSERT INTO test VALUES (?)", in) - - rows = dbt.mustQuery("SELECT value FROM test") - if rows.Next() { - rows.Scan(&out) - if in != out { - dbt.Errorf("%s: %s != %s", v, in, out) - } - } else { - dbt.Errorf("%s: no data", v) - } - - dbt.mustExec("DROP TABLE IF EXISTS test") - } - - // BLOB - dbt.mustExec("CREATE TABLE test (id int, value BLOB) CHARACTER SET utf8") - - id := 2 - in = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + - "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " + - "sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. " + - "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. " + - "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + - "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " + - "sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. " + - "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." - dbt.mustExec("INSERT INTO test VALUES (?, ?)", id, in) - - err := dbt.db.QueryRow("SELECT value FROM test WHERE id = ?", id).Scan(&out) - if err != nil { - dbt.Fatalf("Error on BLOB-Query: %s", err.Error()) - } else if out != in { - dbt.Errorf("BLOB: %s != %s", in, out) - } - }) -} - -type timeTests struct { - dbtype string - tlayout string - tests []timeTest -} - -type timeTest struct { - s string // leading "!": do not use t as value in queries - t time.Time -} - -type timeMode byte - -func (t timeMode) String() string { - switch t { - case binaryString: - return "binary:string" - case binaryTime: - return "binary:time.Time" - case textString: - return "text:string" - } - panic("unsupported timeMode") -} - -func (t timeMode) Binary() bool { - switch t { - case binaryString, binaryTime: - return true - } - return false -} - -const ( - binaryString timeMode = iota - binaryTime - textString -) - -func (t timeTest) genQuery(dbtype string, mode timeMode) string { - var inner string - if mode.Binary() { - inner = "?" - } else { - inner = `"%s"` - } - return `SELECT cast(` + inner + ` as ` + dbtype + `)` -} - -func (t timeTest) run(dbt *DBTest, dbtype, tlayout string, mode timeMode) { - var rows *sql.Rows - query := t.genQuery(dbtype, mode) - switch mode { - case binaryString: - rows = dbt.mustQuery(query, t.s) - case binaryTime: - rows = dbt.mustQuery(query, t.t) - case textString: - query = fmt.Sprintf(query, t.s) - rows = dbt.mustQuery(query) - default: - panic("unsupported mode") - } - defer rows.Close() - var err error - if !rows.Next() { - err = rows.Err() - if err == nil { - err = fmt.Errorf("no data") - } - dbt.Errorf("%s [%s]: %s", dbtype, mode, err) - return - } - var dst interface{} - err = rows.Scan(&dst) - if err != nil { - dbt.Errorf("%s [%s]: %s", dbtype, mode, err) - return - } - switch val := dst.(type) { - case []uint8: - str := string(val) - if str == t.s { - return - } - if mode.Binary() && dbtype == "DATETIME" && len(str) == 26 && str[:19] == t.s { - // a fix mainly for TravisCI: - // accept full microsecond resolution in result for DATETIME columns - // where the binary protocol was used - return - } - dbt.Errorf("%s [%s] to string: expected %q, got %q", - dbtype, mode, - t.s, str, - ) - case time.Time: - if val == t.t { - return - } - dbt.Errorf("%s [%s] to string: expected %q, got %q", - dbtype, mode, - t.s, val.Format(tlayout), - ) - default: - fmt.Printf("%#v\n", []interface{}{dbtype, tlayout, mode, t.s, t.t}) - dbt.Errorf("%s [%s]: unhandled type %T (is '%v')", - dbtype, mode, - val, val, - ) - } -} - -func TestDateTime(t *testing.T) { - afterTime := func(t time.Time, d string) time.Time { - dur, err := time.ParseDuration(d) - if err != nil { - panic(err) - } - return t.Add(dur) - } - // NOTE: MySQL rounds DATETIME(x) up - but that's not included in the tests - format := "2006-01-02 15:04:05.999999" - t0 := time.Time{} - tstr0 := "0000-00-00 00:00:00.000000" - testcases := []timeTests{ - {"DATE", format[:10], []timeTest{ - {t: time.Date(2011, 11, 20, 0, 0, 0, 0, time.UTC)}, - {t: t0, s: tstr0[:10]}, - }}, - {"DATETIME", format[:19], []timeTest{ - {t: time.Date(2011, 11, 20, 21, 27, 37, 0, time.UTC)}, - {t: t0, s: tstr0[:19]}, - }}, - {"DATETIME(0)", format[:21], []timeTest{ - {t: time.Date(2011, 11, 20, 21, 27, 37, 0, time.UTC)}, - {t: t0, s: tstr0[:19]}, - }}, - {"DATETIME(1)", format[:21], []timeTest{ - {t: time.Date(2011, 11, 20, 21, 27, 37, 100000000, time.UTC)}, - {t: t0, s: tstr0[:21]}, - }}, - {"DATETIME(6)", format, []timeTest{ - {t: time.Date(2011, 11, 20, 21, 27, 37, 123456000, time.UTC)}, - {t: t0, s: tstr0}, - }}, - {"TIME", format[11:19], []timeTest{ - {t: afterTime(t0, "12345s")}, - {s: "!-12:34:56"}, - {s: "!-838:59:59"}, - {s: "!838:59:59"}, - {t: t0, s: tstr0[11:19]}, - }}, - {"TIME(0)", format[11:19], []timeTest{ - {t: afterTime(t0, "12345s")}, - {s: "!-12:34:56"}, - {s: "!-838:59:59"}, - {s: "!838:59:59"}, - {t: t0, s: tstr0[11:19]}, - }}, - {"TIME(1)", format[11:21], []timeTest{ - {t: afterTime(t0, "12345600ms")}, - {s: "!-12:34:56.7"}, - {s: "!-838:59:58.9"}, - {s: "!838:59:58.9"}, - {t: t0, s: tstr0[11:21]}, - }}, - {"TIME(6)", format[11:], []timeTest{ - {t: afterTime(t0, "1234567890123000ns")}, - {s: "!-12:34:56.789012"}, - {s: "!-838:59:58.999999"}, - {s: "!838:59:58.999999"}, - {t: t0, s: tstr0[11:]}, - }}, - } - dsns := []string{ - dsn + "&parseTime=true", - dsn + "&parseTime=false", - } - for _, testdsn := range dsns { - runTests(t, testdsn, func(dbt *DBTest) { - microsecsSupported := false - zeroDateSupported := false - var rows *sql.Rows - var err error - rows, err = dbt.db.Query(`SELECT cast("00:00:00.1" as TIME(1)) = "00:00:00.1"`) - if err == nil { - rows.Scan(µsecsSupported) - rows.Close() - } - rows, err = dbt.db.Query(`SELECT cast("0000-00-00" as DATE) = "0000-00-00"`) - if err == nil { - rows.Scan(&zeroDateSupported) - rows.Close() - } - for _, setups := range testcases { - if t := setups.dbtype; !microsecsSupported && t[len(t)-1:] == ")" { - // skip fractional second tests if unsupported by server - continue - } - for _, setup := range setups.tests { - allowBinTime := true - if setup.s == "" { - // fill time string whereever Go can reliable produce it - setup.s = setup.t.Format(setups.tlayout) - } else if setup.s[0] == '!' { - // skip tests using setup.t as source in queries - allowBinTime = false - // fix setup.s - remove the "!" - setup.s = setup.s[1:] - } - if !zeroDateSupported && setup.s == tstr0[:len(setup.s)] { - // skip disallowed 0000-00-00 date - continue - } - setup.run(dbt, setups.dbtype, setups.tlayout, textString) - setup.run(dbt, setups.dbtype, setups.tlayout, binaryString) - if allowBinTime { - setup.run(dbt, setups.dbtype, setups.tlayout, binaryTime) - } - } - } - }) - } -} - -func TestTimestampMicros(t *testing.T) { - format := "2006-01-02 15:04:05.999999" - f0 := format[:19] - f1 := format[:21] - f6 := format[:26] - runTests(t, dsn, func(dbt *DBTest) { - // check if microseconds are supported. - // Do not use timestamp(x) for that check - before 5.5.6, x would mean display width - // and not precision. - // Se last paragraph at http://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html - microsecsSupported := false - if rows, err := dbt.db.Query(`SELECT cast("00:00:00.1" as TIME(1)) = "00:00:00.1"`); err == nil { - rows.Scan(µsecsSupported) - rows.Close() - } - if !microsecsSupported { - // skip test - return - } - _, err := dbt.db.Exec(` - CREATE TABLE test ( - value0 TIMESTAMP NOT NULL DEFAULT '` + f0 + `', - value1 TIMESTAMP(1) NOT NULL DEFAULT '` + f1 + `', - value6 TIMESTAMP(6) NOT NULL DEFAULT '` + f6 + `' - )`, - ) - if err != nil { - dbt.Error(err) - } - defer dbt.mustExec("DROP TABLE IF EXISTS test") - dbt.mustExec("INSERT INTO test SET value0=?, value1=?, value6=?", f0, f1, f6) - var res0, res1, res6 string - rows := dbt.mustQuery("SELECT * FROM test") - if !rows.Next() { - dbt.Errorf("test contained no selectable values") - } - err = rows.Scan(&res0, &res1, &res6) - if err != nil { - dbt.Error(err) - } - if res0 != f0 { - dbt.Errorf("expected %q, got %q", f0, res0) - } - if res1 != f1 { - dbt.Errorf("expected %q, got %q", f1, res1) - } - if res6 != f6 { - dbt.Errorf("expected %q, got %q", f6, res6) - } - }) -} - -func TestNULL(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - nullStmt, err := dbt.db.Prepare("SELECT NULL") - if err != nil { - dbt.Fatal(err) - } - defer nullStmt.Close() - - nonNullStmt, err := dbt.db.Prepare("SELECT 1") - if err != nil { - dbt.Fatal(err) - } - defer nonNullStmt.Close() - - // NullBool - var nb sql.NullBool - // Invalid - if err = nullStmt.QueryRow().Scan(&nb); err != nil { - dbt.Fatal(err) - } - if nb.Valid { - dbt.Error("Valid NullBool which should be invalid") - } - // Valid - if err = nonNullStmt.QueryRow().Scan(&nb); err != nil { - dbt.Fatal(err) - } - if !nb.Valid { - dbt.Error("Invalid NullBool which should be valid") - } else if nb.Bool != true { - dbt.Errorf("Unexpected NullBool value: %t (should be true)", nb.Bool) - } - - // NullFloat64 - var nf sql.NullFloat64 - // Invalid - if err = nullStmt.QueryRow().Scan(&nf); err != nil { - dbt.Fatal(err) - } - if nf.Valid { - dbt.Error("Valid NullFloat64 which should be invalid") - } - // Valid - if err = nonNullStmt.QueryRow().Scan(&nf); err != nil { - dbt.Fatal(err) - } - if !nf.Valid { - dbt.Error("Invalid NullFloat64 which should be valid") - } else if nf.Float64 != float64(1) { - dbt.Errorf("Unexpected NullFloat64 value: %f (should be 1.0)", nf.Float64) - } - - // NullInt64 - var ni sql.NullInt64 - // Invalid - if err = nullStmt.QueryRow().Scan(&ni); err != nil { - dbt.Fatal(err) - } - if ni.Valid { - dbt.Error("Valid NullInt64 which should be invalid") - } - // Valid - if err = nonNullStmt.QueryRow().Scan(&ni); err != nil { - dbt.Fatal(err) - } - if !ni.Valid { - dbt.Error("Invalid NullInt64 which should be valid") - } else if ni.Int64 != int64(1) { - dbt.Errorf("Unexpected NullInt64 value: %d (should be 1)", ni.Int64) - } - - // NullString - var ns sql.NullString - // Invalid - if err = nullStmt.QueryRow().Scan(&ns); err != nil { - dbt.Fatal(err) - } - if ns.Valid { - dbt.Error("Valid NullString which should be invalid") - } - // Valid - if err = nonNullStmt.QueryRow().Scan(&ns); err != nil { - dbt.Fatal(err) - } - if !ns.Valid { - dbt.Error("Invalid NullString which should be valid") - } else if ns.String != `1` { - dbt.Error("Unexpected NullString value:" + ns.String + " (should be `1`)") - } - - // nil-bytes - var b []byte - // Read nil - if err = nullStmt.QueryRow().Scan(&b); err != nil { - dbt.Fatal(err) - } - if b != nil { - dbt.Error("Non-nil []byte wich should be nil") - } - // Read non-nil - if err = nonNullStmt.QueryRow().Scan(&b); err != nil { - dbt.Fatal(err) - } - if b == nil { - dbt.Error("Nil []byte wich should be non-nil") - } - // Insert nil - b = nil - success := false - if err = dbt.db.QueryRow("SELECT ? IS NULL", b).Scan(&success); err != nil { - dbt.Fatal(err) - } - if !success { - dbt.Error("Inserting []byte(nil) as NULL failed") - } - // Check input==output with input==nil - b = nil - if err = dbt.db.QueryRow("SELECT ?", b).Scan(&b); err != nil { - dbt.Fatal(err) - } - if b != nil { - dbt.Error("Non-nil echo from nil input") - } - // Check input==output with input!=nil - b = []byte("") - if err = dbt.db.QueryRow("SELECT ?", b).Scan(&b); err != nil { - dbt.Fatal(err) - } - if b == nil { - dbt.Error("nil echo from non-nil input") - } - - // Insert NULL - dbt.mustExec("CREATE TABLE test (dummmy1 int, value int, dummy2 int)") - - dbt.mustExec("INSERT INTO test VALUES (?, ?, ?)", 1, nil, 2) - - var out interface{} - rows := dbt.mustQuery("SELECT * FROM test") - if rows.Next() { - rows.Scan(&out) - if out != nil { - dbt.Errorf("%v != nil", out) - } - } else { - dbt.Error("no data") - } - }) -} - -func TestUint64(t *testing.T) { - const ( - u0 = uint64(0) - uall = ^u0 - uhigh = uall >> 1 - utop = ^uhigh - s0 = int64(0) - sall = ^s0 - shigh = int64(uhigh) - stop = ^shigh - ) - runTests(t, dsn, func(dbt *DBTest) { - stmt, err := dbt.db.Prepare(`SELECT ?, ?, ? ,?, ?, ?, ?, ?`) - if err != nil { - dbt.Fatal(err) - } - defer stmt.Close() - row := stmt.QueryRow( - u0, uhigh, utop, uall, - s0, shigh, stop, sall, - ) - - var ua, ub, uc, ud uint64 - var sa, sb, sc, sd int64 - - err = row.Scan(&ua, &ub, &uc, &ud, &sa, &sb, &sc, &sd) - if err != nil { - dbt.Fatal(err) - } - switch { - case ua != u0, - ub != uhigh, - uc != utop, - ud != uall, - sa != s0, - sb != shigh, - sc != stop, - sd != sall: - dbt.Fatal("Unexpected result value") - } - }) -} - -func TestLongData(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - var maxAllowedPacketSize int - err := dbt.db.QueryRow("select @@max_allowed_packet").Scan(&maxAllowedPacketSize) - if err != nil { - dbt.Fatal(err) - } - maxAllowedPacketSize-- - - // don't get too ambitious - if maxAllowedPacketSize > 1<<25 { - maxAllowedPacketSize = 1 << 25 - } - - dbt.mustExec("CREATE TABLE test (value LONGBLOB)") - - in := strings.Repeat(`a`, maxAllowedPacketSize+1) - var out string - var rows *sql.Rows - - // Long text data - const nonDataQueryLen = 28 // length query w/o value - inS := in[:maxAllowedPacketSize-nonDataQueryLen] - dbt.mustExec("INSERT INTO test VALUES('" + inS + "')") - rows = dbt.mustQuery("SELECT value FROM test") - if rows.Next() { - rows.Scan(&out) - if inS != out { - dbt.Fatalf("LONGBLOB: length in: %d, length out: %d", len(inS), len(out)) - } - if rows.Next() { - dbt.Error("LONGBLOB: unexpexted row") - } - } else { - dbt.Fatalf("LONGBLOB: no data") - } - - // Empty table - dbt.mustExec("TRUNCATE TABLE test") - - // Long binary data - dbt.mustExec("INSERT INTO test VALUES(?)", in) - rows = dbt.mustQuery("SELECT value FROM test WHERE 1=?", 1) - if rows.Next() { - rows.Scan(&out) - if in != out { - dbt.Fatalf("LONGBLOB: length in: %d, length out: %d", len(in), len(out)) - } - if rows.Next() { - dbt.Error("LONGBLOB: unexpexted row") - } - } else { - if err = rows.Err(); err != nil { - dbt.Fatalf("LONGBLOB: no data (err: %s)", err.Error()) - } else { - dbt.Fatal("LONGBLOB: no data (err: )") - } - } - }) -} - -func TestLoadData(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - verifyLoadDataResult := func() { - rows, err := dbt.db.Query("SELECT * FROM test") - if err != nil { - dbt.Fatal(err.Error()) - } - - i := 0 - values := [4]string{ - "a string", - "a string containing a \t", - "a string containing a \n", - "a string containing both \t\n", - } - - var id int - var value string - - for rows.Next() { - i++ - err = rows.Scan(&id, &value) - if err != nil { - dbt.Fatal(err.Error()) - } - if i != id { - dbt.Fatalf("%d != %d", i, id) - } - if values[i-1] != value { - dbt.Fatalf("%q != %q", values[i-1], value) - } - } - err = rows.Err() - if err != nil { - dbt.Fatal(err.Error()) - } - - if i != 4 { - dbt.Fatalf("Rows count mismatch. Got %d, want 4", i) - } - } - file, err := ioutil.TempFile("", "gotest") - defer os.Remove(file.Name()) - if err != nil { - dbt.Fatal(err) - } - file.WriteString("1\ta string\n2\ta string containing a \\t\n3\ta string containing a \\n\n4\ta string containing both \\t\\n\n") - file.Close() - - dbt.db.Exec("DROP TABLE IF EXISTS test") - dbt.mustExec("CREATE TABLE test (id INT NOT NULL PRIMARY KEY, value TEXT NOT NULL) CHARACTER SET utf8") - - // Local File - RegisterLocalFile(file.Name()) - dbt.mustExec(fmt.Sprintf("LOAD DATA LOCAL INFILE %q INTO TABLE test", file.Name())) - verifyLoadDataResult() - // negative test - _, err = dbt.db.Exec("LOAD DATA LOCAL INFILE 'doesnotexist' INTO TABLE test") - if err == nil { - dbt.Fatal("Load non-existent file didn't fail") - } else if err.Error() != "Local File 'doesnotexist' is not registered. Use the DSN parameter 'allowAllFiles=true' to allow all files" { - dbt.Fatal(err.Error()) - } - - // Empty table - dbt.mustExec("TRUNCATE TABLE test") - - // Reader - RegisterReaderHandler("test", func() io.Reader { - file, err = os.Open(file.Name()) - if err != nil { - dbt.Fatal(err) - } - return file - }) - dbt.mustExec("LOAD DATA LOCAL INFILE 'Reader::test' INTO TABLE test") - verifyLoadDataResult() - // negative test - _, err = dbt.db.Exec("LOAD DATA LOCAL INFILE 'Reader::doesnotexist' INTO TABLE test") - if err == nil { - dbt.Fatal("Load non-existent Reader didn't fail") - } else if err.Error() != "Reader 'doesnotexist' is not registered" { - dbt.Fatal(err.Error()) - } - }) -} - -func TestFoundRows(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - dbt.mustExec("CREATE TABLE test (id INT NOT NULL ,data INT NOT NULL)") - dbt.mustExec("INSERT INTO test (id, data) VALUES (0, 0),(0, 0),(1, 0),(1, 0),(1, 1)") - - res := dbt.mustExec("UPDATE test SET data = 1 WHERE id = 0") - count, err := res.RowsAffected() - if err != nil { - dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) - } - if count != 2 { - dbt.Fatalf("Expected 2 affected rows, got %d", count) - } - res = dbt.mustExec("UPDATE test SET data = 1 WHERE id = 1") - count, err = res.RowsAffected() - if err != nil { - dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) - } - if count != 2 { - dbt.Fatalf("Expected 2 affected rows, got %d", count) - } - }) - runTests(t, dsn+"&clientFoundRows=true", func(dbt *DBTest) { - dbt.mustExec("CREATE TABLE test (id INT NOT NULL ,data INT NOT NULL)") - dbt.mustExec("INSERT INTO test (id, data) VALUES (0, 0),(0, 0),(1, 0),(1, 0),(1, 1)") - - res := dbt.mustExec("UPDATE test SET data = 1 WHERE id = 0") - count, err := res.RowsAffected() - if err != nil { - dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) - } - if count != 2 { - dbt.Fatalf("Expected 2 matched rows, got %d", count) - } - res = dbt.mustExec("UPDATE test SET data = 1 WHERE id = 1") - count, err = res.RowsAffected() - if err != nil { - dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) - } - if count != 3 { - dbt.Fatalf("Expected 3 matched rows, got %d", count) - } - }) -} - -func TestStrict(t *testing.T) { - // ALLOW_INVALID_DATES to get rid of stricter modes - we want to test for warnings, not errors - relaxedDsn := dsn + "&sql_mode=ALLOW_INVALID_DATES" - // make sure the MySQL version is recent enough with a separate connection - // before running the test - conn, err := MySQLDriver{}.Open(relaxedDsn) - if conn != nil { - conn.Close() - } - if me, ok := err.(*MySQLError); ok && me.Number == 1231 { - // Error 1231: Variable 'sql_mode' can't be set to the value of 'ALLOW_INVALID_DATES' - // => skip test, MySQL server version is too old - return - } - runTests(t, relaxedDsn, func(dbt *DBTest) { - dbt.mustExec("CREATE TABLE test (a TINYINT NOT NULL, b CHAR(4))") - - var queries = [...]struct { - in string - codes []string - }{ - {"DROP TABLE IF EXISTS no_such_table", []string{"1051"}}, - {"INSERT INTO test VALUES(10,'mysql'),(NULL,'test'),(300,'Open Source')", []string{"1265", "1048", "1264", "1265"}}, - } - var err error - - var checkWarnings = func(err error, mode string, idx int) { - if err == nil { - dbt.Errorf("Expected STRICT error on query [%s] %s", mode, queries[idx].in) - } - - if warnings, ok := err.(MySQLWarnings); ok { - var codes = make([]string, len(warnings)) - for i := range warnings { - codes[i] = warnings[i].Code - } - if len(codes) != len(queries[idx].codes) { - dbt.Errorf("Unexpected STRICT error count on query [%s] %s: Wanted %v, Got %v", mode, queries[idx].in, queries[idx].codes, codes) - } - - for i := range warnings { - if codes[i] != queries[idx].codes[i] { - dbt.Errorf("Unexpected STRICT error codes on query [%s] %s: Wanted %v, Got %v", mode, queries[idx].in, queries[idx].codes, codes) - return - } - } - - } else { - dbt.Errorf("Unexpected error on query [%s] %s: %s", mode, queries[idx].in, err.Error()) - } - } - - // text protocol - for i := range queries { - _, err = dbt.db.Exec(queries[i].in) - checkWarnings(err, "text", i) - } - - var stmt *sql.Stmt - - // binary protocol - for i := range queries { - stmt, err = dbt.db.Prepare(queries[i].in) - if err != nil { - dbt.Errorf("Error on preparing query %s: %s", queries[i].in, err.Error()) - } - - _, err = stmt.Exec() - checkWarnings(err, "binary", i) - - err = stmt.Close() - if err != nil { - dbt.Errorf("Error on closing stmt for query %s: %s", queries[i].in, err.Error()) - } - } - }) -} - -func TestTLS(t *testing.T) { - tlsTest := func(dbt *DBTest) { - if err := dbt.db.Ping(); err != nil { - if err == ErrNoTLS { - dbt.Skip("Server does not support TLS") - } else { - dbt.Fatalf("Error on Ping: %s", err.Error()) - } - } - - rows := dbt.mustQuery("SHOW STATUS LIKE 'Ssl_cipher'") - - var variable, value *sql.RawBytes - for rows.Next() { - if err := rows.Scan(&variable, &value); err != nil { - dbt.Fatal(err.Error()) - } - - if value == nil { - dbt.Fatal("No Cipher") - } - } - } - - runTests(t, dsn+"&tls=skip-verify", tlsTest) - - // Verify that registering / using a custom cfg works - RegisterTLSConfig("custom-skip-verify", &tls.Config{ - InsecureSkipVerify: true, - }) - runTests(t, dsn+"&tls=custom-skip-verify", tlsTest) -} - -func TestReuseClosedConnection(t *testing.T) { - // this test does not use sql.database, it uses the driver directly - if !available { - t.Skipf("MySQL-Server not running on %s", netAddr) - } - - md := &MySQLDriver{} - conn, err := md.Open(dsn) - if err != nil { - t.Fatalf("Error connecting: %s", err.Error()) - } - stmt, err := conn.Prepare("DO 1") - if err != nil { - t.Fatalf("Error preparing statement: %s", err.Error()) - } - _, err = stmt.Exec(nil) - if err != nil { - t.Fatalf("Error executing statement: %s", err.Error()) - } - err = conn.Close() - if err != nil { - t.Fatalf("Error closing connection: %s", err.Error()) - } - - defer func() { - if err := recover(); err != nil { - t.Errorf("Panic after reusing a closed connection: %v", err) - } - }() - _, err = stmt.Exec(nil) - if err != nil && err != driver.ErrBadConn { - t.Errorf("Unexpected error '%s', expected '%s'", - err.Error(), driver.ErrBadConn.Error()) - } -} - -func TestCharset(t *testing.T) { - if !available { - t.Skipf("MySQL-Server not running on %s", netAddr) - } - - mustSetCharset := func(charsetParam, expected string) { - runTests(t, dsn+"&"+charsetParam, func(dbt *DBTest) { - rows := dbt.mustQuery("SELECT @@character_set_connection") - defer rows.Close() - - if !rows.Next() { - dbt.Fatalf("Error getting connection charset: %s", rows.Err()) - } - - var got string - rows.Scan(&got) - - if got != expected { - dbt.Fatalf("Expected connection charset %s but got %s", expected, got) - } - }) - } - - // non utf8 test - mustSetCharset("charset=ascii", "ascii") - - // when the first charset is invalid, use the second - mustSetCharset("charset=none,utf8", "utf8") - - // when the first charset is valid, use it - mustSetCharset("charset=ascii,utf8", "ascii") - mustSetCharset("charset=utf8,ascii", "utf8") -} - -func TestFailingCharset(t *testing.T) { - runTests(t, dsn+"&charset=none", func(dbt *DBTest) { - // run query to really establish connection... - _, err := dbt.db.Exec("SELECT 1") - if err == nil { - dbt.db.Close() - t.Fatalf("Connection must not succeed without a valid charset") - } - }) -} - -func TestCollation(t *testing.T) { - if !available { - t.Skipf("MySQL-Server not running on %s", netAddr) - } - - defaultCollation := "utf8_general_ci" - testCollations := []string{ - "", // do not set - defaultCollation, // driver default - "latin1_general_ci", - "binary", - "utf8_unicode_ci", - "cp1257_bin", - } - - for _, collation := range testCollations { - var expected, tdsn string - if collation != "" { - tdsn = dsn + "&collation=" + collation - expected = collation - } else { - tdsn = dsn - expected = defaultCollation - } - - runTests(t, tdsn, func(dbt *DBTest) { - var got string - if err := dbt.db.QueryRow("SELECT @@collation_connection").Scan(&got); err != nil { - dbt.Fatal(err) - } - - if got != expected { - dbt.Fatalf("Expected connection collation %s but got %s", expected, got) - } - }) - } -} - -func TestColumnsWithAlias(t *testing.T) { - runTests(t, dsn+"&columnsWithAlias=true", func(dbt *DBTest) { - rows := dbt.mustQuery("SELECT 1 AS A") - defer rows.Close() - cols, _ := rows.Columns() - if len(cols) != 1 { - t.Fatalf("expected 1 column, got %d", len(cols)) - } - if cols[0] != "A" { - t.Fatalf("expected column name \"A\", got \"%s\"", cols[0]) - } - rows.Close() - - rows = dbt.mustQuery("SELECT * FROM (SELECT 1 AS one) AS A") - cols, _ = rows.Columns() - if len(cols) != 1 { - t.Fatalf("expected 1 column, got %d", len(cols)) - } - if cols[0] != "A.one" { - t.Fatalf("expected column name \"A.one\", got \"%s\"", cols[0]) - } - }) -} - -func TestRawBytesResultExceedsBuffer(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - // defaultBufSize from buffer.go - expected := strings.Repeat("abc", defaultBufSize) - - rows := dbt.mustQuery("SELECT '" + expected + "'") - defer rows.Close() - if !rows.Next() { - dbt.Error("expected result, got none") - } - var result sql.RawBytes - rows.Scan(&result) - if expected != string(result) { - dbt.Error("result did not match expected value") - } - }) -} - -func TestTimezoneConversion(t *testing.T) { - zones := []string{"UTC", "US/Central", "US/Pacific", "Local"} - - // Regression test for timezone handling - tzTest := func(dbt *DBTest) { - - // Create table - dbt.mustExec("CREATE TABLE test (ts TIMESTAMP)") - - // Insert local time into database (should be converted) - usCentral, _ := time.LoadLocation("US/Central") - reftime := time.Date(2014, 05, 30, 18, 03, 17, 0, time.UTC).In(usCentral) - dbt.mustExec("INSERT INTO test VALUE (?)", reftime) - - // Retrieve time from DB - rows := dbt.mustQuery("SELECT ts FROM test") - if !rows.Next() { - dbt.Fatal("Didn't get any rows out") - } - - var dbTime time.Time - err := rows.Scan(&dbTime) - if err != nil { - dbt.Fatal("Err", err) - } - - // Check that dates match - if reftime.Unix() != dbTime.Unix() { - dbt.Errorf("Times don't match.\n") - dbt.Errorf(" Now(%v)=%v\n", usCentral, reftime) - dbt.Errorf(" Now(UTC)=%v\n", dbTime) - } - } - - for _, tz := range zones { - runTests(t, dsn+"&parseTime=true&loc="+url.QueryEscape(tz), tzTest) - } -} - -// Special cases - -func TestRowsClose(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - rows, err := dbt.db.Query("SELECT 1") - if err != nil { - dbt.Fatal(err) - } - - err = rows.Close() - if err != nil { - dbt.Fatal(err) - } - - if rows.Next() { - dbt.Fatal("Unexpected row after rows.Close()") - } - - err = rows.Err() - if err != nil { - dbt.Fatal(err) - } - }) -} - -// dangling statements -// http://code.google.com/p/go/issues/detail?id=3865 -func TestCloseStmtBeforeRows(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - stmt, err := dbt.db.Prepare("SELECT 1") - if err != nil { - dbt.Fatal(err) - } - - rows, err := stmt.Query() - if err != nil { - stmt.Close() - dbt.Fatal(err) - } - defer rows.Close() - - err = stmt.Close() - if err != nil { - dbt.Fatal(err) - } - - if !rows.Next() { - dbt.Fatal("Getting row failed") - } else { - err = rows.Err() - if err != nil { - dbt.Fatal(err) - } - - var out bool - err = rows.Scan(&out) - if err != nil { - dbt.Fatalf("Error on rows.Scan(): %s", err.Error()) - } - if out != true { - dbt.Errorf("true != %t", out) - } - } - }) -} - -// It is valid to have multiple Rows for the same Stmt -// http://code.google.com/p/go/issues/detail?id=3734 -func TestStmtMultiRows(t *testing.T) { - runTests(t, dsn, func(dbt *DBTest) { - stmt, err := dbt.db.Prepare("SELECT 1 UNION SELECT 0") - if err != nil { - dbt.Fatal(err) - } - - rows1, err := stmt.Query() - if err != nil { - stmt.Close() - dbt.Fatal(err) - } - defer rows1.Close() - - rows2, err := stmt.Query() - if err != nil { - stmt.Close() - dbt.Fatal(err) - } - defer rows2.Close() - - var out bool - - // 1 - if !rows1.Next() { - dbt.Fatal("1st rows1.Next failed") - } else { - err = rows1.Err() - if err != nil { - dbt.Fatal(err) - } - - err = rows1.Scan(&out) - if err != nil { - dbt.Fatalf("Error on rows.Scan(): %s", err.Error()) - } - if out != true { - dbt.Errorf("true != %t", out) - } - } - - if !rows2.Next() { - dbt.Fatal("1st rows2.Next failed") - } else { - err = rows2.Err() - if err != nil { - dbt.Fatal(err) - } - - err = rows2.Scan(&out) - if err != nil { - dbt.Fatalf("Error on rows.Scan(): %s", err.Error()) - } - if out != true { - dbt.Errorf("true != %t", out) - } - } - - // 2 - if !rows1.Next() { - dbt.Fatal("2nd rows1.Next failed") - } else { - err = rows1.Err() - if err != nil { - dbt.Fatal(err) - } - - err = rows1.Scan(&out) - if err != nil { - dbt.Fatalf("Error on rows.Scan(): %s", err.Error()) - } - if out != false { - dbt.Errorf("false != %t", out) - } - - if rows1.Next() { - dbt.Fatal("Unexpected row on rows1") - } - err = rows1.Close() - if err != nil { - dbt.Fatal(err) - } - } - - if !rows2.Next() { - dbt.Fatal("2nd rows2.Next failed") - } else { - err = rows2.Err() - if err != nil { - dbt.Fatal(err) - } - - err = rows2.Scan(&out) - if err != nil { - dbt.Fatalf("Error on rows.Scan(): %s", err.Error()) - } - if out != false { - dbt.Errorf("false != %t", out) - } - - if rows2.Next() { - dbt.Fatal("Unexpected row on rows2") - } - err = rows2.Close() - if err != nil { - dbt.Fatal(err) - } - } - }) -} - -// Regression test for -// * more than 32 NULL parameters (issue 209) -// * more parameters than fit into the buffer (issue 201) -func TestPreparedManyCols(t *testing.T) { - const numParams = defaultBufSize - runTests(t, dsn, func(dbt *DBTest) { - query := "SELECT ?" + strings.Repeat(",?", numParams-1) - stmt, err := dbt.db.Prepare(query) - if err != nil { - dbt.Fatal(err) - } - defer stmt.Close() - // create more parameters than fit into the buffer - // which will take nil-values - params := make([]interface{}, numParams) - rows, err := stmt.Query(params...) - if err != nil { - stmt.Close() - dbt.Fatal(err) - } - defer rows.Close() - }) -} - -func TestConcurrent(t *testing.T) { - if enabled, _ := readBool(os.Getenv("MYSQL_TEST_CONCURRENT")); !enabled { - t.Skip("MYSQL_TEST_CONCURRENT env var not set") - } - - runTests(t, dsn, func(dbt *DBTest) { - var max int - err := dbt.db.QueryRow("SELECT @@max_connections").Scan(&max) - if err != nil { - dbt.Fatalf("%s", err.Error()) - } - dbt.Logf("Testing up to %d concurrent connections \r\n", max) - - var remaining, succeeded int32 = int32(max), 0 - - var wg sync.WaitGroup - wg.Add(max) - - var fatalError string - var once sync.Once - fatalf := func(s string, vals ...interface{}) { - once.Do(func() { - fatalError = fmt.Sprintf(s, vals...) - }) - } - - for i := 0; i < max; i++ { - go func(id int) { - defer wg.Done() - - tx, err := dbt.db.Begin() - atomic.AddInt32(&remaining, -1) - - if err != nil { - if err.Error() != "Error 1040: Too many connections" { - fatalf("Error on Conn %d: %s", id, err.Error()) - } - return - } - - // keep the connection busy until all connections are open - for remaining > 0 { - if _, err = tx.Exec("DO 1"); err != nil { - fatalf("Error on Conn %d: %s", id, err.Error()) - return - } - } - - if err = tx.Commit(); err != nil { - fatalf("Error on Conn %d: %s", id, err.Error()) - return - } - - // everything went fine with this connection - atomic.AddInt32(&succeeded, 1) - }(i) - } - - // wait until all conections are open - wg.Wait() - - if fatalError != "" { - dbt.Fatal(fatalError) - } - - dbt.Logf("Reached %d concurrent connections\r\n", succeeded) - }) -} - -// Tests custom dial functions -func TestCustomDial(t *testing.T) { - if !available { - t.Skipf("MySQL-Server not running on %s", netAddr) - } - - // our custom dial function which justs wraps net.Dial here - RegisterDial("mydial", func(addr string) (net.Conn, error) { - return net.Dial(prot, addr) - }) - - db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@mydial(%s)/%s?timeout=30s&strict=true", user, pass, addr, dbname)) - if err != nil { - t.Fatalf("Error connecting: %s", err.Error()) - } - defer db.Close() - - if _, err = db.Exec("DO 1"); err != nil { - t.Fatalf("Connection failed: %s", err.Error()) - } -} - -func TestSqlInjection(t *testing.T) { - createTest := func(arg string) func(dbt *DBTest) { - return func(dbt *DBTest) { - dbt.mustExec("CREATE TABLE test (v INTEGER)") - dbt.mustExec("INSERT INTO test VALUES (?)", 1) - - var v int - // NULL can't be equal to anything, the idea here is to inject query so it returns row - // This test verifies that escapeQuotes and escapeBackslash are working properly - err := dbt.db.QueryRow("SELECT v FROM test WHERE NULL = ?", arg).Scan(&v) - if err == sql.ErrNoRows { - return // success, sql injection failed - } else if err == nil { - dbt.Errorf("Sql injection successful with arg: %s", arg) - } else { - dbt.Errorf("Error running query with arg: %s; err: %s", arg, err.Error()) - } - } - } - - dsns := []string{ - dsn, - dsn + "&sql_mode=NO_BACKSLASH_ESCAPES", - } - for _, testdsn := range dsns { - runTests(t, testdsn, createTest("1 OR 1=1")) - runTests(t, testdsn, createTest("' OR '1'='1")) - } -} - -// Test if inserted data is correctly retrieved after being escaped -func TestInsertRetrieveEscapedData(t *testing.T) { - testData := func(dbt *DBTest) { - dbt.mustExec("CREATE TABLE test (v VARCHAR(255))") - - // All sequences that are escaped by escapeQuotes and escapeBackslash - v := "foo \x00\n\r\x1a\"'\\" - dbt.mustExec("INSERT INTO test VALUES (?)", v) - - var out string - err := dbt.db.QueryRow("SELECT v FROM test").Scan(&out) - if err != nil { - dbt.Fatalf("%s", err.Error()) - } - - if out != v { - dbt.Errorf("%q != %q", out, v) - } - } - - dsns := []string{ - dsn, - dsn + "&sql_mode=NO_BACKSLASH_ESCAPES", - } - for _, testdsn := range dsns { - runTests(t, testdsn, testData) - } -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go deleted file mode 100644 index 44cf30db6..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go +++ /dev/null @@ -1,131 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "database/sql/driver" - "errors" - "fmt" - "io" - "log" - "os" -) - -// Various errors the driver might return. Can change between driver versions. -var ( - ErrInvalidConn = errors.New("Invalid Connection") - ErrMalformPkt = errors.New("Malformed Packet") - ErrNoTLS = errors.New("TLS encryption requested but server does not support TLS") - ErrOldPassword = errors.New("This user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords") - ErrCleartextPassword = errors.New("This user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN.") - ErrUnknownPlugin = errors.New("The authentication plugin is not supported.") - ErrOldProtocol = errors.New("MySQL-Server does not support required Protocol 41+") - ErrPktSync = errors.New("Commands out of sync. You can't run this command now") - ErrPktSyncMul = errors.New("Commands out of sync. Did you run multiple statements at once?") - ErrPktTooLarge = errors.New("Packet for query is too large. You can change this value on the server by adjusting the 'max_allowed_packet' variable.") - ErrBusyBuffer = errors.New("Busy buffer") -) - -var errLog Logger = log.New(os.Stderr, "[MySQL] ", log.Ldate|log.Ltime|log.Lshortfile) - -// Logger is used to log critical error messages. -type Logger interface { - Print(v ...interface{}) -} - -// SetLogger is used to set the logger for critical errors. -// The initial logger is os.Stderr. -func SetLogger(logger Logger) error { - if logger == nil { - return errors.New("logger is nil") - } - errLog = logger - return nil -} - -// MySQLError is an error type which represents a single MySQL error -type MySQLError struct { - Number uint16 - Message string -} - -func (me *MySQLError) Error() string { - return fmt.Sprintf("Error %d: %s", me.Number, me.Message) -} - -// MySQLWarnings is an error type which represents a group of one or more MySQL -// warnings -type MySQLWarnings []MySQLWarning - -func (mws MySQLWarnings) Error() string { - var msg string - for i, warning := range mws { - if i > 0 { - msg += "\r\n" - } - msg += fmt.Sprintf( - "%s %s: %s", - warning.Level, - warning.Code, - warning.Message, - ) - } - return msg -} - -// MySQLWarning is an error type which represents a single MySQL warning. -// Warnings are returned in groups only. See MySQLWarnings -type MySQLWarning struct { - Level string - Code string - Message string -} - -func (mc *mysqlConn) getWarnings() (err error) { - rows, err := mc.Query("SHOW WARNINGS", nil) - if err != nil { - return - } - - var warnings = MySQLWarnings{} - var values = make([]driver.Value, 3) - - for { - err = rows.Next(values) - switch err { - case nil: - warning := MySQLWarning{} - - if raw, ok := values[0].([]byte); ok { - warning.Level = string(raw) - } else { - warning.Level = fmt.Sprintf("%s", values[0]) - } - if raw, ok := values[1].([]byte); ok { - warning.Code = string(raw) - } else { - warning.Code = fmt.Sprintf("%s", values[1]) - } - if raw, ok := values[2].([]byte); ok { - warning.Message = string(raw) - } else { - warning.Message = fmt.Sprintf("%s", values[0]) - } - - warnings = append(warnings, warning) - - case io.EOF: - return warnings - - default: - rows.Close() - return - } - } -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors_test.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors_test.go deleted file mode 100644 index 96f9126d6..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "bytes" - "log" - "testing" -) - -func TestErrorsSetLogger(t *testing.T) { - previous := errLog - defer func() { - errLog = previous - }() - - // set up logger - const expected = "prefix: test\n" - buffer := bytes.NewBuffer(make([]byte, 0, 64)) - logger := log.New(buffer, "prefix: ", 0) - - // print - SetLogger(logger) - errLog.Print("test") - - // check result - if actual := buffer.String(); actual != expected { - t.Errorf("expected %q, got %q", expected, actual) - } -} - -func TestErrorsStrictIgnoreNotes(t *testing.T) { - runTests(t, dsn+"&sql_notes=false", func(dbt *DBTest) { - dbt.mustExec("DROP TABLE IF EXISTS does_not_exist") - }) -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go deleted file mode 100644 index a2dedb3c0..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go +++ /dev/null @@ -1,164 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "fmt" - "io" - "os" - "strings" -) - -var ( - fileRegister map[string]bool - readerRegister map[string]func() io.Reader -) - -// RegisterLocalFile adds the given file to the file whitelist, -// so that it can be used by "LOAD DATA LOCAL INFILE ". -// Alternatively you can allow the use of all local files with -// the DSN parameter 'allowAllFiles=true' -// -// filePath := "/home/gopher/data.csv" -// mysql.RegisterLocalFile(filePath) -// err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo") -// if err != nil { -// ... -// -func RegisterLocalFile(filePath string) { - // lazy map init - if fileRegister == nil { - fileRegister = make(map[string]bool) - } - - fileRegister[strings.Trim(filePath, `"`)] = true -} - -// DeregisterLocalFile removes the given filepath from the whitelist. -func DeregisterLocalFile(filePath string) { - delete(fileRegister, strings.Trim(filePath, `"`)) -} - -// RegisterReaderHandler registers a handler function which is used -// to receive a io.Reader. -// The Reader can be used by "LOAD DATA LOCAL INFILE Reader::". -// If the handler returns a io.ReadCloser Close() is called when the -// request is finished. -// -// mysql.RegisterReaderHandler("data", func() io.Reader { -// var csvReader io.Reader // Some Reader that returns CSV data -// ... // Open Reader here -// return csvReader -// }) -// err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo") -// if err != nil { -// ... -// -func RegisterReaderHandler(name string, handler func() io.Reader) { - // lazy map init - if readerRegister == nil { - readerRegister = make(map[string]func() io.Reader) - } - - readerRegister[name] = handler -} - -// DeregisterReaderHandler removes the ReaderHandler function with -// the given name from the registry. -func DeregisterReaderHandler(name string) { - delete(readerRegister, name) -} - -func deferredClose(err *error, closer io.Closer) { - closeErr := closer.Close() - if *err == nil { - *err = closeErr - } -} - -func (mc *mysqlConn) handleInFileRequest(name string) (err error) { - var rdr io.Reader - var data []byte - - if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader - // The server might return an an absolute path. See issue #355. - name = name[idx+8:] - - if handler, inMap := readerRegister[name]; inMap { - rdr = handler() - if rdr != nil { - data = make([]byte, 4+mc.maxWriteSize) - - if cl, ok := rdr.(io.Closer); ok { - defer deferredClose(&err, cl) - } - } else { - err = fmt.Errorf("Reader '%s' is ", name) - } - } else { - err = fmt.Errorf("Reader '%s' is not registered", name) - } - } else { // File - name = strings.Trim(name, `"`) - if mc.cfg.allowAllFiles || fileRegister[name] { - var file *os.File - var fi os.FileInfo - - if file, err = os.Open(name); err == nil { - defer deferredClose(&err, file) - - // get file size - if fi, err = file.Stat(); err == nil { - rdr = file - if fileSize := int(fi.Size()); fileSize <= mc.maxWriteSize { - data = make([]byte, 4+fileSize) - } else if fileSize <= mc.maxPacketAllowed { - data = make([]byte, 4+mc.maxWriteSize) - } else { - err = fmt.Errorf("Local File '%s' too large: Size: %d, Max: %d", name, fileSize, mc.maxPacketAllowed) - } - } - } - } else { - err = fmt.Errorf("Local File '%s' is not registered. Use the DSN parameter 'allowAllFiles=true' to allow all files", name) - } - } - - // send content packets - if err == nil { - var n int - for err == nil { - n, err = rdr.Read(data[4:]) - if n > 0 { - if ioErr := mc.writePacket(data[:4+n]); ioErr != nil { - return ioErr - } - } - } - if err == io.EOF { - err = nil - } - } - - // send empty packet (termination) - if data == nil { - data = make([]byte, 4) - } - if ioErr := mc.writePacket(data[:4]); ioErr != nil { - return ioErr - } - - // read OK packet - if err == nil { - return mc.readResultOK() - } else { - mc.readPacket() - } - return err -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go deleted file mode 100644 index 14395bf9a..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go +++ /dev/null @@ -1,1179 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "bytes" - "crypto/tls" - "database/sql/driver" - "encoding/binary" - "fmt" - "io" - "math" - "time" -) - -// Packets documentation: -// http://dev.mysql.com/doc/internals/en/client-server-protocol.html - -// Read packet to buffer 'data' -func (mc *mysqlConn) readPacket() ([]byte, error) { - var payload []byte - for { - // Read packet header - data, err := mc.buf.readNext(4) - if err != nil { - errLog.Print(err) - mc.Close() - return nil, driver.ErrBadConn - } - - // Packet Length [24 bit] - pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16) - - if pktLen < 1 { - errLog.Print(ErrMalformPkt) - mc.Close() - return nil, driver.ErrBadConn - } - - // Check Packet Sync [8 bit] - if data[3] != mc.sequence { - if data[3] > mc.sequence { - return nil, ErrPktSyncMul - } else { - return nil, ErrPktSync - } - } - mc.sequence++ - - // Read packet body [pktLen bytes] - data, err = mc.buf.readNext(pktLen) - if err != nil { - errLog.Print(err) - mc.Close() - return nil, driver.ErrBadConn - } - - isLastPacket := (pktLen < maxPacketSize) - - // Zero allocations for non-splitting packets - if isLastPacket && payload == nil { - return data, nil - } - - payload = append(payload, data...) - - if isLastPacket { - return payload, nil - } - } -} - -// Write packet buffer 'data' -func (mc *mysqlConn) writePacket(data []byte) error { - pktLen := len(data) - 4 - - if pktLen > mc.maxPacketAllowed { - return ErrPktTooLarge - } - - for { - var size int - if pktLen >= maxPacketSize { - data[0] = 0xff - data[1] = 0xff - data[2] = 0xff - size = maxPacketSize - } else { - data[0] = byte(pktLen) - data[1] = byte(pktLen >> 8) - data[2] = byte(pktLen >> 16) - size = pktLen - } - data[3] = mc.sequence - - // Write packet - n, err := mc.netConn.Write(data[:4+size]) - if err == nil && n == 4+size { - mc.sequence++ - if size != maxPacketSize { - return nil - } - pktLen -= size - data = data[size:] - continue - } - - // Handle error - if err == nil { // n != len(data) - errLog.Print(ErrMalformPkt) - } else { - errLog.Print(err) - } - return driver.ErrBadConn - } -} - -/****************************************************************************** -* Initialisation Process * -******************************************************************************/ - -// Handshake Initialization Packet -// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake -func (mc *mysqlConn) readInitPacket() ([]byte, error) { - data, err := mc.readPacket() - if err != nil { - return nil, err - } - - if data[0] == iERR { - return nil, mc.handleErrorPacket(data) - } - - // protocol version [1 byte] - if data[0] < minProtocolVersion { - return nil, fmt.Errorf( - "Unsupported MySQL Protocol Version %d. Protocol Version %d or higher is required", - data[0], - minProtocolVersion, - ) - } - - // server version [null terminated string] - // connection id [4 bytes] - pos := 1 + bytes.IndexByte(data[1:], 0x00) + 1 + 4 - - // first part of the password cipher [8 bytes] - cipher := data[pos : pos+8] - - // (filler) always 0x00 [1 byte] - pos += 8 + 1 - - // capability flags (lower 2 bytes) [2 bytes] - mc.flags = clientFlag(binary.LittleEndian.Uint16(data[pos : pos+2])) - if mc.flags&clientProtocol41 == 0 { - return nil, ErrOldProtocol - } - if mc.flags&clientSSL == 0 && mc.cfg.tls != nil { - return nil, ErrNoTLS - } - pos += 2 - - if len(data) > pos { - // character set [1 byte] - // status flags [2 bytes] - // capability flags (upper 2 bytes) [2 bytes] - // length of auth-plugin-data [1 byte] - // reserved (all [00]) [10 bytes] - pos += 1 + 2 + 2 + 1 + 10 - - // second part of the password cipher [mininum 13 bytes], - // where len=MAX(13, length of auth-plugin-data - 8) - // - // The web documentation is ambiguous about the length. However, - // according to mysql-5.7/sql/auth/sql_authentication.cc line 538, - // the 13th byte is "\0 byte, terminating the second part of - // a scramble". So the second part of the password cipher is - // a NULL terminated string that's at least 13 bytes with the - // last byte being NULL. - // - // The official Python library uses the fixed length 12 - // which seems to work but technically could have a hidden bug. - cipher = append(cipher, data[pos:pos+12]...) - - // TODO: Verify string termination - // EOF if version (>= 5.5.7 and < 5.5.10) or (>= 5.6.0 and < 5.6.2) - // \NUL otherwise - // - //if data[len(data)-1] == 0 { - // return - //} - //return ErrMalformPkt - - // make a memory safe copy of the cipher slice - var b [20]byte - copy(b[:], cipher) - return b[:], nil - } - - // make a memory safe copy of the cipher slice - var b [8]byte - copy(b[:], cipher) - return b[:], nil -} - -// Client Authentication Packet -// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse -func (mc *mysqlConn) writeAuthPacket(cipher []byte) error { - // Adjust client flags based on server support - clientFlags := clientProtocol41 | - clientSecureConn | - clientLongPassword | - clientTransactions | - clientLocalFiles | - clientPluginAuth | - mc.flags&clientLongFlag - - if mc.cfg.clientFoundRows { - clientFlags |= clientFoundRows - } - - // To enable TLS / SSL - if mc.cfg.tls != nil { - clientFlags |= clientSSL - } - - // User Password - scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.passwd)) - - pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.user) + 1 + 1 + len(scrambleBuff) + 21 + 1 - - // To specify a db name - if n := len(mc.cfg.dbname); n > 0 { - clientFlags |= clientConnectWithDB - pktLen += n + 1 - } - - // Calculate packet length and get buffer with that size - data := mc.buf.takeSmallBuffer(pktLen + 4) - if data == nil { - // can not take the buffer. Something must be wrong with the connection - errLog.Print(ErrBusyBuffer) - return driver.ErrBadConn - } - - // ClientFlags [32 bit] - data[4] = byte(clientFlags) - data[5] = byte(clientFlags >> 8) - data[6] = byte(clientFlags >> 16) - data[7] = byte(clientFlags >> 24) - - // MaxPacketSize [32 bit] (none) - data[8] = 0x00 - data[9] = 0x00 - data[10] = 0x00 - data[11] = 0x00 - - // Charset [1 byte] - data[12] = mc.cfg.collation - - // SSL Connection Request Packet - // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest - if mc.cfg.tls != nil { - // Send TLS / SSL request packet - if err := mc.writePacket(data[:(4+4+1+23)+4]); err != nil { - return err - } - - // Switch to TLS - tlsConn := tls.Client(mc.netConn, mc.cfg.tls) - if err := tlsConn.Handshake(); err != nil { - return err - } - mc.netConn = tlsConn - mc.buf.rd = tlsConn - } - - // Filler [23 bytes] (all 0x00) - pos := 13 + 23 - - // User [null terminated string] - if len(mc.cfg.user) > 0 { - pos += copy(data[pos:], mc.cfg.user) - } - data[pos] = 0x00 - pos++ - - // ScrambleBuffer [length encoded integer] - data[pos] = byte(len(scrambleBuff)) - pos += 1 + copy(data[pos+1:], scrambleBuff) - - // Databasename [null terminated string] - if len(mc.cfg.dbname) > 0 { - pos += copy(data[pos:], mc.cfg.dbname) - data[pos] = 0x00 - pos++ - } - - // Assume native client during response - pos += copy(data[pos:], "mysql_native_password") - data[pos] = 0x00 - - // Send Auth packet - return mc.writePacket(data) -} - -// Client old authentication packet -// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse -func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error { - // User password - scrambleBuff := scrambleOldPassword(cipher, []byte(mc.cfg.passwd)) - - // Calculate the packet length and add a tailing 0 - pktLen := len(scrambleBuff) + 1 - data := mc.buf.takeSmallBuffer(4 + pktLen) - if data == nil { - // can not take the buffer. Something must be wrong with the connection - errLog.Print(ErrBusyBuffer) - return driver.ErrBadConn - } - - // Add the scrambled password [null terminated string] - copy(data[4:], scrambleBuff) - data[4+pktLen-1] = 0x00 - - return mc.writePacket(data) -} - -// Client clear text authentication packet -// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse -func (mc *mysqlConn) writeClearAuthPacket() error { - // Calculate the packet length and add a tailing 0 - pktLen := len(mc.cfg.passwd) + 1 - data := mc.buf.takeSmallBuffer(4 + pktLen) - if data == nil { - // can not take the buffer. Something must be wrong with the connection - errLog.Print(ErrBusyBuffer) - return driver.ErrBadConn - } - - // Add the clear password [null terminated string] - copy(data[4:], mc.cfg.passwd) - data[4+pktLen-1] = 0x00 - - return mc.writePacket(data) -} - -/****************************************************************************** -* Command Packets * -******************************************************************************/ - -func (mc *mysqlConn) writeCommandPacket(command byte) error { - // Reset Packet Sequence - mc.sequence = 0 - - data := mc.buf.takeSmallBuffer(4 + 1) - if data == nil { - // can not take the buffer. Something must be wrong with the connection - errLog.Print(ErrBusyBuffer) - return driver.ErrBadConn - } - - // Add command byte - data[4] = command - - // Send CMD packet - return mc.writePacket(data) -} - -func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error { - // Reset Packet Sequence - mc.sequence = 0 - - pktLen := 1 + len(arg) - data := mc.buf.takeBuffer(pktLen + 4) - if data == nil { - // can not take the buffer. Something must be wrong with the connection - errLog.Print(ErrBusyBuffer) - return driver.ErrBadConn - } - - // Add command byte - data[4] = command - - // Add arg - copy(data[5:], arg) - - // Send CMD packet - return mc.writePacket(data) -} - -func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error { - // Reset Packet Sequence - mc.sequence = 0 - - data := mc.buf.takeSmallBuffer(4 + 1 + 4) - if data == nil { - // can not take the buffer. Something must be wrong with the connection - errLog.Print(ErrBusyBuffer) - return driver.ErrBadConn - } - - // Add command byte - data[4] = command - - // Add arg [32 bit] - data[5] = byte(arg) - data[6] = byte(arg >> 8) - data[7] = byte(arg >> 16) - data[8] = byte(arg >> 24) - - // Send CMD packet - return mc.writePacket(data) -} - -/****************************************************************************** -* Result Packets * -******************************************************************************/ - -// Returns error if Packet is not an 'Result OK'-Packet -func (mc *mysqlConn) readResultOK() error { - data, err := mc.readPacket() - if err == nil { - // packet indicator - switch data[0] { - - case iOK: - return mc.handleOkPacket(data) - - case iEOF: - if len(data) > 1 { - plugin := string(data[1:bytes.IndexByte(data, 0x00)]) - if plugin == "mysql_old_password" { - // using old_passwords - return ErrOldPassword - } else if plugin == "mysql_clear_password" { - // using clear text password - return ErrCleartextPassword - } else { - return ErrUnknownPlugin - } - } else { - return ErrOldPassword - } - - default: // Error otherwise - return mc.handleErrorPacket(data) - } - } - return err -} - -// Result Set Header Packet -// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::Resultset -func (mc *mysqlConn) readResultSetHeaderPacket() (int, error) { - data, err := mc.readPacket() - if err == nil { - switch data[0] { - - case iOK: - return 0, mc.handleOkPacket(data) - - case iERR: - return 0, mc.handleErrorPacket(data) - - case iLocalInFile: - return 0, mc.handleInFileRequest(string(data[1:])) - } - - // column count - num, _, n := readLengthEncodedInteger(data) - if n-len(data) == 0 { - return int(num), nil - } - - return 0, ErrMalformPkt - } - return 0, err -} - -// Error Packet -// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-ERR_Packet -func (mc *mysqlConn) handleErrorPacket(data []byte) error { - if data[0] != iERR { - return ErrMalformPkt - } - - // 0xff [1 byte] - - // Error Number [16 bit uint] - errno := binary.LittleEndian.Uint16(data[1:3]) - - pos := 3 - - // SQL State [optional: # + 5bytes string] - if data[3] == 0x23 { - //sqlstate := string(data[4 : 4+5]) - pos = 9 - } - - // Error Message [string] - return &MySQLError{ - Number: errno, - Message: string(data[pos:]), - } -} - -// Ok Packet -// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-OK_Packet -func (mc *mysqlConn) handleOkPacket(data []byte) error { - var n, m int - - // 0x00 [1 byte] - - // Affected rows [Length Coded Binary] - mc.affectedRows, _, n = readLengthEncodedInteger(data[1:]) - - // Insert id [Length Coded Binary] - mc.insertId, _, m = readLengthEncodedInteger(data[1+n:]) - - // server_status [2 bytes] - mc.status = statusFlag(data[1+n+m]) | statusFlag(data[1+n+m+1])<<8 - - // warning count [2 bytes] - if !mc.strict { - return nil - } else { - pos := 1 + n + m + 2 - if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 { - return mc.getWarnings() - } - return nil - } -} - -// Read Packets as Field Packets until EOF-Packet or an Error appears -// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnDefinition41 -func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) { - columns := make([]mysqlField, count) - - for i := 0; ; i++ { - data, err := mc.readPacket() - if err != nil { - return nil, err - } - - // EOF Packet - if data[0] == iEOF && (len(data) == 5 || len(data) == 1) { - if i == count { - return columns, nil - } - return nil, fmt.Errorf("ColumnsCount mismatch n:%d len:%d", count, len(columns)) - } - - // Catalog - pos, err := skipLengthEncodedString(data) - if err != nil { - return nil, err - } - - // Database [len coded string] - n, err := skipLengthEncodedString(data[pos:]) - if err != nil { - return nil, err - } - pos += n - - // Table [len coded string] - if mc.cfg.columnsWithAlias { - tableName, _, n, err := readLengthEncodedString(data[pos:]) - if err != nil { - return nil, err - } - pos += n - columns[i].tableName = string(tableName) - } else { - n, err = skipLengthEncodedString(data[pos:]) - if err != nil { - return nil, err - } - pos += n - } - - // Original table [len coded string] - n, err = skipLengthEncodedString(data[pos:]) - if err != nil { - return nil, err - } - pos += n - - // Name [len coded string] - name, _, n, err := readLengthEncodedString(data[pos:]) - if err != nil { - return nil, err - } - columns[i].name = string(name) - pos += n - - // Original name [len coded string] - n, err = skipLengthEncodedString(data[pos:]) - if err != nil { - return nil, err - } - - // Filler [uint8] - // Charset [charset, collation uint8] - // Length [uint32] - pos += n + 1 + 2 + 4 - - // Field type [uint8] - columns[i].fieldType = data[pos] - pos++ - - // Flags [uint16] - columns[i].flags = fieldFlag(binary.LittleEndian.Uint16(data[pos : pos+2])) - pos += 2 - - // Decimals [uint8] - columns[i].decimals = data[pos] - //pos++ - - // Default value [len coded binary] - //if pos < len(data) { - // defaultVal, _, err = bytesToLengthCodedBinary(data[pos:]) - //} - } -} - -// Read Packets as Field Packets until EOF-Packet or an Error appears -// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::ResultsetRow -func (rows *textRows) readRow(dest []driver.Value) error { - mc := rows.mc - - data, err := mc.readPacket() - if err != nil { - return err - } - - // EOF Packet - if data[0] == iEOF && len(data) == 5 { - rows.mc = nil - return io.EOF - } - if data[0] == iERR { - rows.mc = nil - return mc.handleErrorPacket(data) - } - - // RowSet Packet - var n int - var isNull bool - pos := 0 - - for i := range dest { - // Read bytes and convert to string - dest[i], isNull, n, err = readLengthEncodedString(data[pos:]) - pos += n - if err == nil { - if !isNull { - if !mc.parseTime { - continue - } else { - switch rows.columns[i].fieldType { - case fieldTypeTimestamp, fieldTypeDateTime, - fieldTypeDate, fieldTypeNewDate: - dest[i], err = parseDateTime( - string(dest[i].([]byte)), - mc.cfg.loc, - ) - if err == nil { - continue - } - default: - continue - } - } - - } else { - dest[i] = nil - continue - } - } - return err // err != nil - } - - return nil -} - -// Reads Packets until EOF-Packet or an Error appears. Returns count of Packets read -func (mc *mysqlConn) readUntilEOF() error { - for { - data, err := mc.readPacket() - - // No Err and no EOF Packet - if err == nil && data[0] != iEOF { - continue - } - return err // Err or EOF - } -} - -/****************************************************************************** -* Prepared Statements * -******************************************************************************/ - -// Prepare Result Packets -// http://dev.mysql.com/doc/internals/en/com-stmt-prepare-response.html -func (stmt *mysqlStmt) readPrepareResultPacket() (uint16, error) { - data, err := stmt.mc.readPacket() - if err == nil { - // packet indicator [1 byte] - if data[0] != iOK { - return 0, stmt.mc.handleErrorPacket(data) - } - - // statement id [4 bytes] - stmt.id = binary.LittleEndian.Uint32(data[1:5]) - - // Column count [16 bit uint] - columnCount := binary.LittleEndian.Uint16(data[5:7]) - - // Param count [16 bit uint] - stmt.paramCount = int(binary.LittleEndian.Uint16(data[7:9])) - - // Reserved [8 bit] - - // Warning count [16 bit uint] - if !stmt.mc.strict { - return columnCount, nil - } else { - // Check for warnings count > 0, only available in MySQL > 4.1 - if len(data) >= 12 && binary.LittleEndian.Uint16(data[10:12]) > 0 { - return columnCount, stmt.mc.getWarnings() - } - return columnCount, nil - } - } - return 0, err -} - -// http://dev.mysql.com/doc/internals/en/com-stmt-send-long-data.html -func (stmt *mysqlStmt) writeCommandLongData(paramID int, arg []byte) error { - maxLen := stmt.mc.maxPacketAllowed - 1 - pktLen := maxLen - - // After the header (bytes 0-3) follows before the data: - // 1 byte command - // 4 bytes stmtID - // 2 bytes paramID - const dataOffset = 1 + 4 + 2 - - // Can not use the write buffer since - // a) the buffer is too small - // b) it is in use - data := make([]byte, 4+1+4+2+len(arg)) - - copy(data[4+dataOffset:], arg) - - for argLen := len(arg); argLen > 0; argLen -= pktLen - dataOffset { - if dataOffset+argLen < maxLen { - pktLen = dataOffset + argLen - } - - stmt.mc.sequence = 0 - // Add command byte [1 byte] - data[4] = comStmtSendLongData - - // Add stmtID [32 bit] - data[5] = byte(stmt.id) - data[6] = byte(stmt.id >> 8) - data[7] = byte(stmt.id >> 16) - data[8] = byte(stmt.id >> 24) - - // Add paramID [16 bit] - data[9] = byte(paramID) - data[10] = byte(paramID >> 8) - - // Send CMD packet - err := stmt.mc.writePacket(data[:4+pktLen]) - if err == nil { - data = data[pktLen-dataOffset:] - continue - } - return err - - } - - // Reset Packet Sequence - stmt.mc.sequence = 0 - return nil -} - -// Execute Prepared Statement -// http://dev.mysql.com/doc/internals/en/com-stmt-execute.html -func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { - if len(args) != stmt.paramCount { - return fmt.Errorf( - "Arguments count mismatch (Got: %d Has: %d)", - len(args), - stmt.paramCount, - ) - } - - const minPktLen = 4 + 1 + 4 + 1 + 4 - mc := stmt.mc - - // Reset packet-sequence - mc.sequence = 0 - - var data []byte - - if len(args) == 0 { - data = mc.buf.takeBuffer(minPktLen) - } else { - data = mc.buf.takeCompleteBuffer() - } - if data == nil { - // can not take the buffer. Something must be wrong with the connection - errLog.Print(ErrBusyBuffer) - return driver.ErrBadConn - } - - // command [1 byte] - data[4] = comStmtExecute - - // statement_id [4 bytes] - data[5] = byte(stmt.id) - data[6] = byte(stmt.id >> 8) - data[7] = byte(stmt.id >> 16) - data[8] = byte(stmt.id >> 24) - - // flags (0: CURSOR_TYPE_NO_CURSOR) [1 byte] - data[9] = 0x00 - - // iteration_count (uint32(1)) [4 bytes] - data[10] = 0x01 - data[11] = 0x00 - data[12] = 0x00 - data[13] = 0x00 - - if len(args) > 0 { - pos := minPktLen - - var nullMask []byte - if maskLen, typesLen := (len(args)+7)/8, 1+2*len(args); pos+maskLen+typesLen >= len(data) { - // buffer has to be extended but we don't know by how much so - // we depend on append after all data with known sizes fit. - // We stop at that because we deal with a lot of columns here - // which makes the required allocation size hard to guess. - tmp := make([]byte, pos+maskLen+typesLen) - copy(tmp[:pos], data[:pos]) - data = tmp - nullMask = data[pos : pos+maskLen] - pos += maskLen - } else { - nullMask = data[pos : pos+maskLen] - for i := 0; i < maskLen; i++ { - nullMask[i] = 0 - } - pos += maskLen - } - - // newParameterBoundFlag 1 [1 byte] - data[pos] = 0x01 - pos++ - - // type of each parameter [len(args)*2 bytes] - paramTypes := data[pos:] - pos += len(args) * 2 - - // value of each parameter [n bytes] - paramValues := data[pos:pos] - valuesCap := cap(paramValues) - - for i, arg := range args { - // build NULL-bitmap - if arg == nil { - nullMask[i/8] |= 1 << (uint(i) & 7) - paramTypes[i+i] = fieldTypeNULL - paramTypes[i+i+1] = 0x00 - continue - } - - // cache types and values - switch v := arg.(type) { - case int64: - paramTypes[i+i] = fieldTypeLongLong - paramTypes[i+i+1] = 0x00 - - if cap(paramValues)-len(paramValues)-8 >= 0 { - paramValues = paramValues[:len(paramValues)+8] - binary.LittleEndian.PutUint64( - paramValues[len(paramValues)-8:], - uint64(v), - ) - } else { - paramValues = append(paramValues, - uint64ToBytes(uint64(v))..., - ) - } - - case float64: - paramTypes[i+i] = fieldTypeDouble - paramTypes[i+i+1] = 0x00 - - if cap(paramValues)-len(paramValues)-8 >= 0 { - paramValues = paramValues[:len(paramValues)+8] - binary.LittleEndian.PutUint64( - paramValues[len(paramValues)-8:], - math.Float64bits(v), - ) - } else { - paramValues = append(paramValues, - uint64ToBytes(math.Float64bits(v))..., - ) - } - - case bool: - paramTypes[i+i] = fieldTypeTiny - paramTypes[i+i+1] = 0x00 - - if v { - paramValues = append(paramValues, 0x01) - } else { - paramValues = append(paramValues, 0x00) - } - - case []byte: - // Common case (non-nil value) first - if v != nil { - paramTypes[i+i] = fieldTypeString - paramTypes[i+i+1] = 0x00 - - if len(v) < mc.maxPacketAllowed-pos-len(paramValues)-(len(args)-(i+1))*64 { - paramValues = appendLengthEncodedInteger(paramValues, - uint64(len(v)), - ) - paramValues = append(paramValues, v...) - } else { - if err := stmt.writeCommandLongData(i, v); err != nil { - return err - } - } - continue - } - - // Handle []byte(nil) as a NULL value - nullMask[i/8] |= 1 << (uint(i) & 7) - paramTypes[i+i] = fieldTypeNULL - paramTypes[i+i+1] = 0x00 - - case string: - paramTypes[i+i] = fieldTypeString - paramTypes[i+i+1] = 0x00 - - if len(v) < mc.maxPacketAllowed-pos-len(paramValues)-(len(args)-(i+1))*64 { - paramValues = appendLengthEncodedInteger(paramValues, - uint64(len(v)), - ) - paramValues = append(paramValues, v...) - } else { - if err := stmt.writeCommandLongData(i, []byte(v)); err != nil { - return err - } - } - - case time.Time: - paramTypes[i+i] = fieldTypeString - paramTypes[i+i+1] = 0x00 - - var val []byte - if v.IsZero() { - val = []byte("0000-00-00") - } else { - val = []byte(v.In(mc.cfg.loc).Format(timeFormat)) - } - - paramValues = appendLengthEncodedInteger(paramValues, - uint64(len(val)), - ) - paramValues = append(paramValues, val...) - - default: - return fmt.Errorf("Can't convert type: %T", arg) - } - } - - // Check if param values exceeded the available buffer - // In that case we must build the data packet with the new values buffer - if valuesCap != cap(paramValues) { - data = append(data[:pos], paramValues...) - mc.buf.buf = data - } - - pos += len(paramValues) - data = data[:pos] - } - - return mc.writePacket(data) -} - -// http://dev.mysql.com/doc/internals/en/binary-protocol-resultset-row.html -func (rows *binaryRows) readRow(dest []driver.Value) error { - data, err := rows.mc.readPacket() - if err != nil { - return err - } - - // packet indicator [1 byte] - if data[0] != iOK { - rows.mc = nil - // EOF Packet - if data[0] == iEOF && len(data) == 5 { - return io.EOF - } - - // Error otherwise - return rows.mc.handleErrorPacket(data) - } - - // NULL-bitmap, [(column-count + 7 + 2) / 8 bytes] - pos := 1 + (len(dest)+7+2)>>3 - nullMask := data[1:pos] - - for i := range dest { - // Field is NULL - // (byte >> bit-pos) % 2 == 1 - if ((nullMask[(i+2)>>3] >> uint((i+2)&7)) & 1) == 1 { - dest[i] = nil - continue - } - - // Convert to byte-coded string - switch rows.columns[i].fieldType { - case fieldTypeNULL: - dest[i] = nil - continue - - // Numeric Types - case fieldTypeTiny: - if rows.columns[i].flags&flagUnsigned != 0 { - dest[i] = int64(data[pos]) - } else { - dest[i] = int64(int8(data[pos])) - } - pos++ - continue - - case fieldTypeShort, fieldTypeYear: - if rows.columns[i].flags&flagUnsigned != 0 { - dest[i] = int64(binary.LittleEndian.Uint16(data[pos : pos+2])) - } else { - dest[i] = int64(int16(binary.LittleEndian.Uint16(data[pos : pos+2]))) - } - pos += 2 - continue - - case fieldTypeInt24, fieldTypeLong: - if rows.columns[i].flags&flagUnsigned != 0 { - dest[i] = int64(binary.LittleEndian.Uint32(data[pos : pos+4])) - } else { - dest[i] = int64(int32(binary.LittleEndian.Uint32(data[pos : pos+4]))) - } - pos += 4 - continue - - case fieldTypeLongLong: - if rows.columns[i].flags&flagUnsigned != 0 { - val := binary.LittleEndian.Uint64(data[pos : pos+8]) - if val > math.MaxInt64 { - dest[i] = uint64ToString(val) - } else { - dest[i] = int64(val) - } - } else { - dest[i] = int64(binary.LittleEndian.Uint64(data[pos : pos+8])) - } - pos += 8 - continue - - case fieldTypeFloat: - dest[i] = float64(math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4]))) - pos += 4 - continue - - case fieldTypeDouble: - dest[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[pos : pos+8])) - pos += 8 - continue - - // Length coded Binary Strings - case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar, - fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB, - fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB, - fieldTypeVarString, fieldTypeString, fieldTypeGeometry: - var isNull bool - var n int - dest[i], isNull, n, err = readLengthEncodedString(data[pos:]) - pos += n - if err == nil { - if !isNull { - continue - } else { - dest[i] = nil - continue - } - } - return err - - case - fieldTypeDate, fieldTypeNewDate, // Date YYYY-MM-DD - fieldTypeTime, // Time [-][H]HH:MM:SS[.fractal] - fieldTypeTimestamp, fieldTypeDateTime: // Timestamp YYYY-MM-DD HH:MM:SS[.fractal] - - num, isNull, n := readLengthEncodedInteger(data[pos:]) - pos += n - - switch { - case isNull: - dest[i] = nil - continue - case rows.columns[i].fieldType == fieldTypeTime: - // database/sql does not support an equivalent to TIME, return a string - var dstlen uint8 - switch decimals := rows.columns[i].decimals; decimals { - case 0x00, 0x1f: - dstlen = 8 - case 1, 2, 3, 4, 5, 6: - dstlen = 8 + 1 + decimals - default: - return fmt.Errorf( - "MySQL protocol error, illegal decimals value %d", - rows.columns[i].decimals, - ) - } - dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, true) - case rows.mc.parseTime: - dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.loc) - default: - var dstlen uint8 - if rows.columns[i].fieldType == fieldTypeDate { - dstlen = 10 - } else { - switch decimals := rows.columns[i].decimals; decimals { - case 0x00, 0x1f: - dstlen = 19 - case 1, 2, 3, 4, 5, 6: - dstlen = 19 + 1 + decimals - default: - return fmt.Errorf( - "MySQL protocol error, illegal decimals value %d", - rows.columns[i].decimals, - ) - } - } - dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, false) - } - - if err == nil { - pos += int(num) - continue - } else { - return err - } - - // Please report if this happens! - default: - return fmt.Errorf("Unknown FieldType %d", rows.columns[i].fieldType) - } - } - - return nil -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go deleted file mode 100644 index c6438d034..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go +++ /dev/null @@ -1,22 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -type mysqlResult struct { - affectedRows int64 - insertId int64 -} - -func (res *mysqlResult) LastInsertId() (int64, error) { - return res.insertId, nil -} - -func (res *mysqlResult) RowsAffected() (int64, error) { - return res.affectedRows, nil -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go deleted file mode 100644 index ba606e146..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go +++ /dev/null @@ -1,106 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "database/sql/driver" - "io" -) - -type mysqlField struct { - tableName string - name string - flags fieldFlag - fieldType byte - decimals byte -} - -type mysqlRows struct { - mc *mysqlConn - columns []mysqlField -} - -type binaryRows struct { - mysqlRows -} - -type textRows struct { - mysqlRows -} - -type emptyRows struct{} - -func (rows *mysqlRows) Columns() []string { - columns := make([]string, len(rows.columns)) - if rows.mc.cfg.columnsWithAlias { - for i := range columns { - if tableName := rows.columns[i].tableName; len(tableName) > 0 { - columns[i] = tableName + "." + rows.columns[i].name - } else { - columns[i] = rows.columns[i].name - } - } - } else { - for i := range columns { - columns[i] = rows.columns[i].name - } - } - return columns -} - -func (rows *mysqlRows) Close() error { - mc := rows.mc - if mc == nil { - return nil - } - if mc.netConn == nil { - return ErrInvalidConn - } - - // Remove unread packets from stream - err := mc.readUntilEOF() - rows.mc = nil - return err -} - -func (rows *binaryRows) Next(dest []driver.Value) error { - if mc := rows.mc; mc != nil { - if mc.netConn == nil { - return ErrInvalidConn - } - - // Fetch next row from stream - return rows.readRow(dest) - } - return io.EOF -} - -func (rows *textRows) Next(dest []driver.Value) error { - if mc := rows.mc; mc != nil { - if mc.netConn == nil { - return ErrInvalidConn - } - - // Fetch next row from stream - return rows.readRow(dest) - } - return io.EOF -} - -func (rows emptyRows) Columns() []string { - return nil -} - -func (rows emptyRows) Close() error { - return nil -} - -func (rows emptyRows) Next(dest []driver.Value) error { - return io.EOF -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go deleted file mode 100644 index 6e869b340..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go +++ /dev/null @@ -1,150 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "database/sql/driver" - "fmt" - "reflect" - "strconv" -) - -type mysqlStmt struct { - mc *mysqlConn - id uint32 - paramCount int - columns []mysqlField // cached from the first query -} - -func (stmt *mysqlStmt) Close() error { - if stmt.mc == nil || stmt.mc.netConn == nil { - errLog.Print(ErrInvalidConn) - return driver.ErrBadConn - } - - err := stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id) - stmt.mc = nil - return err -} - -func (stmt *mysqlStmt) NumInput() int { - return stmt.paramCount -} - -func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter { - return converter{} -} - -func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { - if stmt.mc.netConn == nil { - errLog.Print(ErrInvalidConn) - return nil, driver.ErrBadConn - } - // Send command - err := stmt.writeExecutePacket(args) - if err != nil { - return nil, err - } - - mc := stmt.mc - - mc.affectedRows = 0 - mc.insertId = 0 - - // Read Result - resLen, err := mc.readResultSetHeaderPacket() - if err == nil { - if resLen > 0 { - // Columns - err = mc.readUntilEOF() - if err != nil { - return nil, err - } - - // Rows - err = mc.readUntilEOF() - } - if err == nil { - return &mysqlResult{ - affectedRows: int64(mc.affectedRows), - insertId: int64(mc.insertId), - }, nil - } - } - - return nil, err -} - -func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) { - if stmt.mc.netConn == nil { - errLog.Print(ErrInvalidConn) - return nil, driver.ErrBadConn - } - // Send command - err := stmt.writeExecutePacket(args) - if err != nil { - return nil, err - } - - mc := stmt.mc - - // Read Result - resLen, err := mc.readResultSetHeaderPacket() - if err != nil { - return nil, err - } - - rows := new(binaryRows) - rows.mc = mc - - if resLen > 0 { - // Columns - // If not cached, read them and cache them - if stmt.columns == nil { - rows.columns, err = mc.readColumns(resLen) - stmt.columns = rows.columns - } else { - rows.columns = stmt.columns - err = mc.readUntilEOF() - } - } - - return rows, err -} - -type converter struct{} - -func (c converter) ConvertValue(v interface{}) (driver.Value, error) { - if driver.IsValue(v) { - return v, nil - } - - rv := reflect.ValueOf(v) - switch rv.Kind() { - case reflect.Ptr: - // indirect pointers - if rv.IsNil() { - return nil, nil - } - return c.ConvertValue(rv.Elem().Interface()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return rv.Int(), nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: - return int64(rv.Uint()), nil - case reflect.Uint64: - u64 := rv.Uint() - if u64 >= 1<<63 { - return strconv.FormatUint(u64, 10), nil - } - return int64(u64), nil - case reflect.Float32, reflect.Float64: - return rv.Float(), nil - } - return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind()) -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go deleted file mode 100644 index 33c749b35..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go +++ /dev/null @@ -1,31 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -type mysqlTx struct { - mc *mysqlConn -} - -func (tx *mysqlTx) Commit() (err error) { - if tx.mc == nil || tx.mc.netConn == nil { - return ErrInvalidConn - } - err = tx.mc.exec("COMMIT") - tx.mc = nil - return -} - -func (tx *mysqlTx) Rollback() (err error) { - if tx.mc == nil || tx.mc.netConn == nil { - return ErrInvalidConn - } - err = tx.mc.exec("ROLLBACK") - tx.mc = nil - return -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go deleted file mode 100644 index 6a26ad129..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go +++ /dev/null @@ -1,973 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "crypto/sha1" - "crypto/tls" - "database/sql/driver" - "encoding/binary" - "errors" - "fmt" - "io" - "net" - "net/url" - "strings" - "time" -) - -var ( - tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs - - errInvalidDSNUnescaped = errors.New("Invalid DSN: Did you forget to escape a param value?") - errInvalidDSNAddr = errors.New("Invalid DSN: Network Address not terminated (missing closing brace)") - errInvalidDSNNoSlash = errors.New("Invalid DSN: Missing the slash separating the database name") - errInvalidDSNUnsafeCollation = errors.New("Invalid DSN: interpolateParams can be used with ascii, latin1, utf8 and utf8mb4 charset") -) - -func init() { - tlsConfigRegister = make(map[string]*tls.Config) -} - -// RegisterTLSConfig registers a custom tls.Config to be used with sql.Open. -// Use the key as a value in the DSN where tls=value. -// -// rootCertPool := x509.NewCertPool() -// pem, err := ioutil.ReadFile("/path/ca-cert.pem") -// if err != nil { -// log.Fatal(err) -// } -// if ok := rootCertPool.AppendCertsFromPEM(pem); !ok { -// log.Fatal("Failed to append PEM.") -// } -// clientCert := make([]tls.Certificate, 0, 1) -// certs, err := tls.LoadX509KeyPair("/path/client-cert.pem", "/path/client-key.pem") -// if err != nil { -// log.Fatal(err) -// } -// clientCert = append(clientCert, certs) -// mysql.RegisterTLSConfig("custom", &tls.Config{ -// RootCAs: rootCertPool, -// Certificates: clientCert, -// }) -// db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom") -// -func RegisterTLSConfig(key string, config *tls.Config) error { - if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" { - return fmt.Errorf("Key '%s' is reserved", key) - } - - tlsConfigRegister[key] = config - return nil -} - -// DeregisterTLSConfig removes the tls.Config associated with key. -func DeregisterTLSConfig(key string) { - delete(tlsConfigRegister, key) -} - -// parseDSN parses the DSN string to a config -func parseDSN(dsn string) (cfg *config, err error) { - // New config with some default values - cfg = &config{ - loc: time.UTC, - collation: defaultCollation, - } - - // [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN] - // Find the last '/' (since the password or the net addr might contain a '/') - foundSlash := false - for i := len(dsn) - 1; i >= 0; i-- { - if dsn[i] == '/' { - foundSlash = true - var j, k int - - // left part is empty if i <= 0 - if i > 0 { - // [username[:password]@][protocol[(address)]] - // Find the last '@' in dsn[:i] - for j = i; j >= 0; j-- { - if dsn[j] == '@' { - // username[:password] - // Find the first ':' in dsn[:j] - for k = 0; k < j; k++ { - if dsn[k] == ':' { - cfg.passwd = dsn[k+1 : j] - break - } - } - cfg.user = dsn[:k] - - break - } - } - - // [protocol[(address)]] - // Find the first '(' in dsn[j+1:i] - for k = j + 1; k < i; k++ { - if dsn[k] == '(' { - // dsn[i-1] must be == ')' if an address is specified - if dsn[i-1] != ')' { - if strings.ContainsRune(dsn[k+1:i], ')') { - return nil, errInvalidDSNUnescaped - } - return nil, errInvalidDSNAddr - } - cfg.addr = dsn[k+1 : i-1] - break - } - } - cfg.net = dsn[j+1 : k] - } - - // dbname[?param1=value1&...¶mN=valueN] - // Find the first '?' in dsn[i+1:] - for j = i + 1; j < len(dsn); j++ { - if dsn[j] == '?' { - if err = parseDSNParams(cfg, dsn[j+1:]); err != nil { - return - } - break - } - } - cfg.dbname = dsn[i+1 : j] - - break - } - } - - if !foundSlash && len(dsn) > 0 { - return nil, errInvalidDSNNoSlash - } - - if cfg.interpolateParams && unsafeCollations[cfg.collation] { - return nil, errInvalidDSNUnsafeCollation - } - - // Set default network if empty - if cfg.net == "" { - cfg.net = "tcp" - } - - // Set default address if empty - if cfg.addr == "" { - switch cfg.net { - case "tcp": - cfg.addr = "127.0.0.1:3306" - case "unix": - cfg.addr = "/tmp/mysql.sock" - default: - return nil, errors.New("Default addr for network '" + cfg.net + "' unknown") - } - - } - - return -} - -// parseDSNParams parses the DSN "query string" -// Values must be url.QueryEscape'ed -func parseDSNParams(cfg *config, params string) (err error) { - for _, v := range strings.Split(params, "&") { - param := strings.SplitN(v, "=", 2) - if len(param) != 2 { - continue - } - - // cfg params - switch value := param[1]; param[0] { - - // Enable client side placeholder substitution - case "interpolateParams": - var isBool bool - cfg.interpolateParams, isBool = readBool(value) - if !isBool { - return fmt.Errorf("Invalid Bool value: %s", value) - } - - // Disable INFILE whitelist / enable all files - case "allowAllFiles": - var isBool bool - cfg.allowAllFiles, isBool = readBool(value) - if !isBool { - return fmt.Errorf("Invalid Bool value: %s", value) - } - - // Use cleartext authentication mode (MySQL 5.5.10+) - case "allowCleartextPasswords": - var isBool bool - cfg.allowCleartextPasswords, isBool = readBool(value) - if !isBool { - return fmt.Errorf("Invalid Bool value: %s", value) - } - - // Use old authentication mode (pre MySQL 4.1) - case "allowOldPasswords": - var isBool bool - cfg.allowOldPasswords, isBool = readBool(value) - if !isBool { - return fmt.Errorf("Invalid Bool value: %s", value) - } - - // Switch "rowsAffected" mode - case "clientFoundRows": - var isBool bool - cfg.clientFoundRows, isBool = readBool(value) - if !isBool { - return fmt.Errorf("Invalid Bool value: %s", value) - } - - // Collation - case "collation": - collation, ok := collations[value] - if !ok { - // Note possibility for false negatives: - // could be triggered although the collation is valid if the - // collations map does not contain entries the server supports. - err = errors.New("unknown collation") - return - } - cfg.collation = collation - break - - case "columnsWithAlias": - var isBool bool - cfg.columnsWithAlias, isBool = readBool(value) - if !isBool { - return fmt.Errorf("Invalid Bool value: %s", value) - } - - // Time Location - case "loc": - if value, err = url.QueryUnescape(value); err != nil { - return - } - cfg.loc, err = time.LoadLocation(value) - if err != nil { - return - } - - // Dial Timeout - case "timeout": - cfg.timeout, err = time.ParseDuration(value) - if err != nil { - return - } - - // TLS-Encryption - case "tls": - boolValue, isBool := readBool(value) - if isBool { - if boolValue { - cfg.tls = &tls.Config{} - } - } else { - if strings.ToLower(value) == "skip-verify" { - cfg.tls = &tls.Config{InsecureSkipVerify: true} - } else if tlsConfig, ok := tlsConfigRegister[value]; ok { - if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify { - host, _, err := net.SplitHostPort(cfg.addr) - if err == nil { - tlsConfig.ServerName = host - } - } - - cfg.tls = tlsConfig - } else { - return fmt.Errorf("Invalid value / unknown config name: %s", value) - } - } - - default: - // lazy init - if cfg.params == nil { - cfg.params = make(map[string]string) - } - - if cfg.params[param[0]], err = url.QueryUnescape(value); err != nil { - return - } - } - } - - return -} - -// Returns the bool value of the input. -// The 2nd return value indicates if the input was a valid bool value -func readBool(input string) (value bool, valid bool) { - switch input { - case "1", "true", "TRUE", "True": - return true, true - case "0", "false", "FALSE", "False": - return false, true - } - - // Not a valid bool value - return -} - -/****************************************************************************** -* Authentication * -******************************************************************************/ - -// Encrypt password using 4.1+ method -func scramblePassword(scramble, password []byte) []byte { - if len(password) == 0 { - return nil - } - - // stage1Hash = SHA1(password) - crypt := sha1.New() - crypt.Write(password) - stage1 := crypt.Sum(nil) - - // scrambleHash = SHA1(scramble + SHA1(stage1Hash)) - // inner Hash - crypt.Reset() - crypt.Write(stage1) - hash := crypt.Sum(nil) - - // outer Hash - crypt.Reset() - crypt.Write(scramble) - crypt.Write(hash) - scramble = crypt.Sum(nil) - - // token = scrambleHash XOR stage1Hash - for i := range scramble { - scramble[i] ^= stage1[i] - } - return scramble -} - -// Encrypt password using pre 4.1 (old password) method -// https://github.com/atcurtis/mariadb/blob/master/mysys/my_rnd.c -type myRnd struct { - seed1, seed2 uint32 -} - -const myRndMaxVal = 0x3FFFFFFF - -// Pseudo random number generator -func newMyRnd(seed1, seed2 uint32) *myRnd { - return &myRnd{ - seed1: seed1 % myRndMaxVal, - seed2: seed2 % myRndMaxVal, - } -} - -// Tested to be equivalent to MariaDB's floating point variant -// http://play.golang.org/p/QHvhd4qved -// http://play.golang.org/p/RG0q4ElWDx -func (r *myRnd) NextByte() byte { - r.seed1 = (r.seed1*3 + r.seed2) % myRndMaxVal - r.seed2 = (r.seed1 + r.seed2 + 33) % myRndMaxVal - - return byte(uint64(r.seed1) * 31 / myRndMaxVal) -} - -// Generate binary hash from byte string using insecure pre 4.1 method -func pwHash(password []byte) (result [2]uint32) { - var add uint32 = 7 - var tmp uint32 - - result[0] = 1345345333 - result[1] = 0x12345671 - - for _, c := range password { - // skip spaces and tabs in password - if c == ' ' || c == '\t' { - continue - } - - tmp = uint32(c) - result[0] ^= (((result[0] & 63) + add) * tmp) + (result[0] << 8) - result[1] += (result[1] << 8) ^ result[0] - add += tmp - } - - // Remove sign bit (1<<31)-1) - result[0] &= 0x7FFFFFFF - result[1] &= 0x7FFFFFFF - - return -} - -// Encrypt password using insecure pre 4.1 method -func scrambleOldPassword(scramble, password []byte) []byte { - if len(password) == 0 { - return nil - } - - scramble = scramble[:8] - - hashPw := pwHash(password) - hashSc := pwHash(scramble) - - r := newMyRnd(hashPw[0]^hashSc[0], hashPw[1]^hashSc[1]) - - var out [8]byte - for i := range out { - out[i] = r.NextByte() + 64 - } - - mask := r.NextByte() - for i := range out { - out[i] ^= mask - } - - return out[:] -} - -/****************************************************************************** -* Time related utils * -******************************************************************************/ - -// NullTime represents a time.Time that may be NULL. -// NullTime implements the Scanner interface so -// it can be used as a scan destination: -// -// var nt NullTime -// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt) -// ... -// if nt.Valid { -// // use nt.Time -// } else { -// // NULL value -// } -// -// This NullTime implementation is not driver-specific -type NullTime struct { - Time time.Time - Valid bool // Valid is true if Time is not NULL -} - -// Scan implements the Scanner interface. -// The value type must be time.Time or string / []byte (formatted time-string), -// otherwise Scan fails. -func (nt *NullTime) Scan(value interface{}) (err error) { - if value == nil { - nt.Time, nt.Valid = time.Time{}, false - return - } - - switch v := value.(type) { - case time.Time: - nt.Time, nt.Valid = v, true - return - case []byte: - nt.Time, err = parseDateTime(string(v), time.UTC) - nt.Valid = (err == nil) - return - case string: - nt.Time, err = parseDateTime(v, time.UTC) - nt.Valid = (err == nil) - return - } - - nt.Valid = false - return fmt.Errorf("Can't convert %T to time.Time", value) -} - -// Value implements the driver Valuer interface. -func (nt NullTime) Value() (driver.Value, error) { - if !nt.Valid { - return nil, nil - } - return nt.Time, nil -} - -func parseDateTime(str string, loc *time.Location) (t time.Time, err error) { - base := "0000-00-00 00:00:00.0000000" - switch len(str) { - case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" - if str == base[:len(str)] { - return - } - t, err = time.Parse(timeFormat[:len(str)], str) - default: - err = fmt.Errorf("Invalid Time-String: %s", str) - return - } - - // Adjust location - if err == nil && loc != time.UTC { - y, mo, d := t.Date() - h, mi, s := t.Clock() - t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil - } - - return -} - -func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) { - switch num { - case 0: - return time.Time{}, nil - case 4: - return time.Date( - int(binary.LittleEndian.Uint16(data[:2])), // year - time.Month(data[2]), // month - int(data[3]), // day - 0, 0, 0, 0, - loc, - ), nil - case 7: - return time.Date( - int(binary.LittleEndian.Uint16(data[:2])), // year - time.Month(data[2]), // month - int(data[3]), // day - int(data[4]), // hour - int(data[5]), // minutes - int(data[6]), // seconds - 0, - loc, - ), nil - case 11: - return time.Date( - int(binary.LittleEndian.Uint16(data[:2])), // year - time.Month(data[2]), // month - int(data[3]), // day - int(data[4]), // hour - int(data[5]), // minutes - int(data[6]), // seconds - int(binary.LittleEndian.Uint32(data[7:11]))*1000, // nanoseconds - loc, - ), nil - } - return nil, fmt.Errorf("Invalid DATETIME-packet length %d", num) -} - -// zeroDateTime is used in formatBinaryDateTime to avoid an allocation -// if the DATE or DATETIME has the zero value. -// It must never be changed. -// The current behavior depends on database/sql copying the result. -var zeroDateTime = []byte("0000-00-00 00:00:00.000000") - -const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" -const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999" - -func formatBinaryDateTime(src []byte, length uint8, justTime bool) (driver.Value, error) { - // length expects the deterministic length of the zero value, - // negative time and 100+ hours are automatically added if needed - if len(src) == 0 { - if justTime { - return zeroDateTime[11 : 11+length], nil - } - return zeroDateTime[:length], nil - } - var dst []byte // return value - var pt, p1, p2, p3 byte // current digit pair - var zOffs byte // offset of value in zeroDateTime - if justTime { - switch length { - case - 8, // time (can be up to 10 when negative and 100+ hours) - 10, 11, 12, 13, 14, 15: // time with fractional seconds - default: - return nil, fmt.Errorf("illegal TIME length %d", length) - } - switch len(src) { - case 8, 12: - default: - return nil, fmt.Errorf("Invalid TIME-packet length %d", len(src)) - } - // +2 to enable negative time and 100+ hours - dst = make([]byte, 0, length+2) - if src[0] == 1 { - dst = append(dst, '-') - } - if src[1] != 0 { - hour := uint16(src[1])*24 + uint16(src[5]) - pt = byte(hour / 100) - p1 = byte(hour - 100*uint16(pt)) - dst = append(dst, digits01[pt]) - } else { - p1 = src[5] - } - zOffs = 11 - src = src[6:] - } else { - switch length { - case 10, 19, 21, 22, 23, 24, 25, 26: - default: - t := "DATE" - if length > 10 { - t += "TIME" - } - return nil, fmt.Errorf("illegal %s length %d", t, length) - } - switch len(src) { - case 4, 7, 11: - default: - t := "DATE" - if length > 10 { - t += "TIME" - } - return nil, fmt.Errorf("illegal %s-packet length %d", t, len(src)) - } - dst = make([]byte, 0, length) - // start with the date - year := binary.LittleEndian.Uint16(src[:2]) - pt = byte(year / 100) - p1 = byte(year - 100*uint16(pt)) - p2, p3 = src[2], src[3] - dst = append(dst, - digits10[pt], digits01[pt], - digits10[p1], digits01[p1], '-', - digits10[p2], digits01[p2], '-', - digits10[p3], digits01[p3], - ) - if length == 10 { - return dst, nil - } - if len(src) == 4 { - return append(dst, zeroDateTime[10:length]...), nil - } - dst = append(dst, ' ') - p1 = src[4] // hour - src = src[5:] - } - // p1 is 2-digit hour, src is after hour - p2, p3 = src[0], src[1] - dst = append(dst, - digits10[p1], digits01[p1], ':', - digits10[p2], digits01[p2], ':', - digits10[p3], digits01[p3], - ) - if length <= byte(len(dst)) { - return dst, nil - } - src = src[2:] - if len(src) == 0 { - return append(dst, zeroDateTime[19:zOffs+length]...), nil - } - microsecs := binary.LittleEndian.Uint32(src[:4]) - p1 = byte(microsecs / 10000) - microsecs -= 10000 * uint32(p1) - p2 = byte(microsecs / 100) - microsecs -= 100 * uint32(p2) - p3 = byte(microsecs) - switch decimals := zOffs + length - 20; decimals { - default: - return append(dst, '.', - digits10[p1], digits01[p1], - digits10[p2], digits01[p2], - digits10[p3], digits01[p3], - ), nil - case 1: - return append(dst, '.', - digits10[p1], - ), nil - case 2: - return append(dst, '.', - digits10[p1], digits01[p1], - ), nil - case 3: - return append(dst, '.', - digits10[p1], digits01[p1], - digits10[p2], - ), nil - case 4: - return append(dst, '.', - digits10[p1], digits01[p1], - digits10[p2], digits01[p2], - ), nil - case 5: - return append(dst, '.', - digits10[p1], digits01[p1], - digits10[p2], digits01[p2], - digits10[p3], - ), nil - } -} - -/****************************************************************************** -* Convert from and to bytes * -******************************************************************************/ - -func uint64ToBytes(n uint64) []byte { - return []byte{ - byte(n), - byte(n >> 8), - byte(n >> 16), - byte(n >> 24), - byte(n >> 32), - byte(n >> 40), - byte(n >> 48), - byte(n >> 56), - } -} - -func uint64ToString(n uint64) []byte { - var a [20]byte - i := 20 - - // U+0030 = 0 - // ... - // U+0039 = 9 - - var q uint64 - for n >= 10 { - i-- - q = n / 10 - a[i] = uint8(n-q*10) + 0x30 - n = q - } - - i-- - a[i] = uint8(n) + 0x30 - - return a[i:] -} - -// treats string value as unsigned integer representation -func stringToInt(b []byte) int { - val := 0 - for i := range b { - val *= 10 - val += int(b[i] - 0x30) - } - return val -} - -// returns the string read as a bytes slice, wheter the value is NULL, -// the number of bytes read and an error, in case the string is longer than -// the input slice -func readLengthEncodedString(b []byte) ([]byte, bool, int, error) { - // Get length - num, isNull, n := readLengthEncodedInteger(b) - if num < 1 { - return b[n:n], isNull, n, nil - } - - n += int(num) - - // Check data length - if len(b) >= n { - return b[n-int(num) : n], false, n, nil - } - return nil, false, n, io.EOF -} - -// returns the number of bytes skipped and an error, in case the string is -// longer than the input slice -func skipLengthEncodedString(b []byte) (int, error) { - // Get length - num, _, n := readLengthEncodedInteger(b) - if num < 1 { - return n, nil - } - - n += int(num) - - // Check data length - if len(b) >= n { - return n, nil - } - return n, io.EOF -} - -// returns the number read, whether the value is NULL and the number of bytes read -func readLengthEncodedInteger(b []byte) (uint64, bool, int) { - // See issue #349 - if len(b) == 0 { - return 0, true, 1 - } - switch b[0] { - - // 251: NULL - case 0xfb: - return 0, true, 1 - - // 252: value of following 2 - case 0xfc: - return uint64(b[1]) | uint64(b[2])<<8, false, 3 - - // 253: value of following 3 - case 0xfd: - return uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16, false, 4 - - // 254: value of following 8 - case 0xfe: - return uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 | - uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 | - uint64(b[7])<<48 | uint64(b[8])<<56, - false, 9 - } - - // 0-250: value of first byte - return uint64(b[0]), false, 1 -} - -// encodes a uint64 value and appends it to the given bytes slice -func appendLengthEncodedInteger(b []byte, n uint64) []byte { - switch { - case n <= 250: - return append(b, byte(n)) - - case n <= 0xffff: - return append(b, 0xfc, byte(n), byte(n>>8)) - - case n <= 0xffffff: - return append(b, 0xfd, byte(n), byte(n>>8), byte(n>>16)) - } - return append(b, 0xfe, byte(n), byte(n>>8), byte(n>>16), byte(n>>24), - byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56)) -} - -// reserveBuffer checks cap(buf) and expand buffer to len(buf) + appendSize. -// If cap(buf) is not enough, reallocate new buffer. -func reserveBuffer(buf []byte, appendSize int) []byte { - newSize := len(buf) + appendSize - if cap(buf) < newSize { - // Grow buffer exponentially - newBuf := make([]byte, len(buf)*2+appendSize) - copy(newBuf, buf) - buf = newBuf - } - return buf[:newSize] -} - -// escapeBytesBackslash escapes []byte with backslashes (\) -// This escapes the contents of a string (provided as []byte) by adding backslashes before special -// characters, and turning others into specific escape sequences, such as -// turning newlines into \n and null bytes into \0. -// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L823-L932 -func escapeBytesBackslash(buf, v []byte) []byte { - pos := len(buf) - buf = reserveBuffer(buf, len(v)*2) - - for _, c := range v { - switch c { - case '\x00': - buf[pos] = '\\' - buf[pos+1] = '0' - pos += 2 - case '\n': - buf[pos] = '\\' - buf[pos+1] = 'n' - pos += 2 - case '\r': - buf[pos] = '\\' - buf[pos+1] = 'r' - pos += 2 - case '\x1a': - buf[pos] = '\\' - buf[pos+1] = 'Z' - pos += 2 - case '\'': - buf[pos] = '\\' - buf[pos+1] = '\'' - pos += 2 - case '"': - buf[pos] = '\\' - buf[pos+1] = '"' - pos += 2 - case '\\': - buf[pos] = '\\' - buf[pos+1] = '\\' - pos += 2 - default: - buf[pos] = c - pos += 1 - } - } - - return buf[:pos] -} - -// escapeStringBackslash is similar to escapeBytesBackslash but for string. -func escapeStringBackslash(buf []byte, v string) []byte { - pos := len(buf) - buf = reserveBuffer(buf, len(v)*2) - - for i := 0; i < len(v); i++ { - c := v[i] - switch c { - case '\x00': - buf[pos] = '\\' - buf[pos+1] = '0' - pos += 2 - case '\n': - buf[pos] = '\\' - buf[pos+1] = 'n' - pos += 2 - case '\r': - buf[pos] = '\\' - buf[pos+1] = 'r' - pos += 2 - case '\x1a': - buf[pos] = '\\' - buf[pos+1] = 'Z' - pos += 2 - case '\'': - buf[pos] = '\\' - buf[pos+1] = '\'' - pos += 2 - case '"': - buf[pos] = '\\' - buf[pos+1] = '"' - pos += 2 - case '\\': - buf[pos] = '\\' - buf[pos+1] = '\\' - pos += 2 - default: - buf[pos] = c - pos += 1 - } - } - - return buf[:pos] -} - -// escapeBytesQuotes escapes apostrophes in []byte by doubling them up. -// This escapes the contents of a string by doubling up any apostrophes that -// it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in -// effect on the server. -// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L963-L1038 -func escapeBytesQuotes(buf, v []byte) []byte { - pos := len(buf) - buf = reserveBuffer(buf, len(v)*2) - - for _, c := range v { - if c == '\'' { - buf[pos] = '\'' - buf[pos+1] = '\'' - pos += 2 - } else { - buf[pos] = c - pos++ - } - } - - return buf[:pos] -} - -// escapeStringQuotes is similar to escapeBytesQuotes but for string. -func escapeStringQuotes(buf []byte, v string) []byte { - pos := len(buf) - buf = reserveBuffer(buf, len(v)*2) - - for i := 0; i < len(v); i++ { - c := v[i] - if c == '\'' { - buf[pos] = '\'' - buf[pos+1] = '\'' - pos += 2 - } else { - buf[pos] = c - pos++ - } - } - - return buf[:pos] -} diff --git a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go b/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go deleted file mode 100644 index 79fbdd1eb..000000000 --- a/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go +++ /dev/null @@ -1,346 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "bytes" - "crypto/tls" - "encoding/binary" - "fmt" - "testing" - "time" -) - -var testDSNs = []struct { - in string - out string - loc *time.Location -}{ - {"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, - {"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:true interpolateParams:false}", time.UTC}, - {"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, - {"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, - {"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8mb4,utf8] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, - {"user:password@/dbname?loc=UTC&timeout=30s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{user:user passwd:password net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls: timeout:30000000000 collation:224 allowAllFiles:true allowOldPasswords:true allowCleartextPasswords:false clientFoundRows:true columnsWithAlias:false interpolateParams:false}", time.UTC}, - {"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{user:user passwd:p@ss(word) net:tcp addr:[de:ad:be:ef::ca:fe]:80 dbname:dbname params:map[] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.Local}, - {"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, - {"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, - {"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, - {"", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, - {"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, - {"unix/?arg=%2Fsome%2Fpath.ext", "&{user: passwd: net:unix addr:/tmp/mysql.sock dbname: params:map[arg:/some/path.ext] loc:%p tls: timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, -} - -func TestDSNParser(t *testing.T) { - var cfg *config - var err error - var res string - - for i, tst := range testDSNs { - cfg, err = parseDSN(tst.in) - if err != nil { - t.Error(err.Error()) - } - - // pointer not static - cfg.tls = nil - - res = fmt.Sprintf("%+v", cfg) - if res != fmt.Sprintf(tst.out, tst.loc) { - t.Errorf("%d. parseDSN(%q) => %q, want %q", i, tst.in, res, fmt.Sprintf(tst.out, tst.loc)) - } - } -} - -func TestDSNParserInvalid(t *testing.T) { - var invalidDSNs = []string{ - "@net(addr/", // no closing brace - "@tcp(/", // no closing brace - "tcp(/", // no closing brace - "(/", // no closing brace - "net(addr)//", // unescaped - "user:pass@tcp(1.2.3.4:3306)", // no trailing slash - //"/dbname?arg=/some/unescaped/path", - } - - for i, tst := range invalidDSNs { - if _, err := parseDSN(tst); err == nil { - t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst) - } - } -} - -func TestDSNWithCustomTLS(t *testing.T) { - baseDSN := "user:password@tcp(localhost:5555)/dbname?tls=" - tlsCfg := tls.Config{} - - RegisterTLSConfig("utils_test", &tlsCfg) - - // Custom TLS is missing - tst := baseDSN + "invalid_tls" - cfg, err := parseDSN(tst) - if err == nil { - t.Errorf("Invalid custom TLS in DSN (%s) but did not error. Got config: %#v", tst, cfg) - } - - tst = baseDSN + "utils_test" - - // Custom TLS with a server name - name := "foohost" - tlsCfg.ServerName = name - cfg, err = parseDSN(tst) - - if err != nil { - t.Error(err.Error()) - } else if cfg.tls.ServerName != name { - t.Errorf("Did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst) - } - - // Custom TLS without a server name - name = "localhost" - tlsCfg.ServerName = "" - cfg, err = parseDSN(tst) - - if err != nil { - t.Error(err.Error()) - } else if cfg.tls.ServerName != name { - t.Errorf("Did not get the correct ServerName (%s) parsing DSN (%s).", name, tst) - } - - DeregisterTLSConfig("utils_test") -} - -func TestDSNUnsafeCollation(t *testing.T) { - _, err := parseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true") - if err != errInvalidDSNUnsafeCollation { - t.Error("Expected %v, Got %v", errInvalidDSNUnsafeCollation, err) - } - - _, err = parseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=false") - if err != nil { - t.Error("Expected %v, Got %v", nil, err) - } - - _, err = parseDSN("/dbname?collation=gbk_chinese_ci") - if err != nil { - t.Error("Expected %v, Got %v", nil, err) - } - - _, err = parseDSN("/dbname?collation=ascii_bin&interpolateParams=true") - if err != nil { - t.Error("Expected %v, Got %v", nil, err) - } - - _, err = parseDSN("/dbname?collation=latin1_german1_ci&interpolateParams=true") - if err != nil { - t.Error("Expected %v, Got %v", nil, err) - } - - _, err = parseDSN("/dbname?collation=utf8_general_ci&interpolateParams=true") - if err != nil { - t.Error("Expected %v, Got %v", nil, err) - } - - _, err = parseDSN("/dbname?collation=utf8mb4_general_ci&interpolateParams=true") - if err != nil { - t.Error("Expected %v, Got %v", nil, err) - } -} - -func BenchmarkParseDSN(b *testing.B) { - b.ReportAllocs() - - for i := 0; i < b.N; i++ { - for _, tst := range testDSNs { - if _, err := parseDSN(tst.in); err != nil { - b.Error(err.Error()) - } - } - } -} - -func TestScanNullTime(t *testing.T) { - var scanTests = []struct { - in interface{} - error bool - valid bool - time time.Time - }{ - {tDate, false, true, tDate}, - {sDate, false, true, tDate}, - {[]byte(sDate), false, true, tDate}, - {tDateTime, false, true, tDateTime}, - {sDateTime, false, true, tDateTime}, - {[]byte(sDateTime), false, true, tDateTime}, - {tDate0, false, true, tDate0}, - {sDate0, false, true, tDate0}, - {[]byte(sDate0), false, true, tDate0}, - {sDateTime0, false, true, tDate0}, - {[]byte(sDateTime0), false, true, tDate0}, - {"", true, false, tDate0}, - {"1234", true, false, tDate0}, - {0, true, false, tDate0}, - } - - var nt = NullTime{} - var err error - - for _, tst := range scanTests { - err = nt.Scan(tst.in) - if (err != nil) != tst.error { - t.Errorf("%v: expected error status %t, got %t", tst.in, tst.error, (err != nil)) - } - if nt.Valid != tst.valid { - t.Errorf("%v: expected valid status %t, got %t", tst.in, tst.valid, nt.Valid) - } - if nt.Time != tst.time { - t.Errorf("%v: expected time %v, got %v", tst.in, tst.time, nt.Time) - } - } -} - -func TestLengthEncodedInteger(t *testing.T) { - var integerTests = []struct { - num uint64 - encoded []byte - }{ - {0x0000000000000000, []byte{0x00}}, - {0x0000000000000012, []byte{0x12}}, - {0x00000000000000fa, []byte{0xfa}}, - {0x0000000000000100, []byte{0xfc, 0x00, 0x01}}, - {0x0000000000001234, []byte{0xfc, 0x34, 0x12}}, - {0x000000000000ffff, []byte{0xfc, 0xff, 0xff}}, - {0x0000000000010000, []byte{0xfd, 0x00, 0x00, 0x01}}, - {0x0000000000123456, []byte{0xfd, 0x56, 0x34, 0x12}}, - {0x0000000000ffffff, []byte{0xfd, 0xff, 0xff, 0xff}}, - {0x0000000001000000, []byte{0xfe, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}}, - {0x123456789abcdef0, []byte{0xfe, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12}}, - {0xffffffffffffffff, []byte{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, - } - - for _, tst := range integerTests { - num, isNull, numLen := readLengthEncodedInteger(tst.encoded) - if isNull { - t.Errorf("%x: expected %d, got NULL", tst.encoded, tst.num) - } - if num != tst.num { - t.Errorf("%x: expected %d, got %d", tst.encoded, tst.num, num) - } - if numLen != len(tst.encoded) { - t.Errorf("%x: expected size %d, got %d", tst.encoded, len(tst.encoded), numLen) - } - encoded := appendLengthEncodedInteger(nil, num) - if !bytes.Equal(encoded, tst.encoded) { - t.Errorf("%v: expected %x, got %x", num, tst.encoded, encoded) - } - } -} - -func TestOldPass(t *testing.T) { - scramble := []byte{9, 8, 7, 6, 5, 4, 3, 2} - vectors := []struct { - pass string - out string - }{ - {" pass", "47575c5a435b4251"}, - {"pass ", "47575c5a435b4251"}, - {"123\t456", "575c47505b5b5559"}, - {"C0mpl!ca ted#PASS123", "5d5d554849584a45"}, - } - for _, tuple := range vectors { - ours := scrambleOldPassword(scramble, []byte(tuple.pass)) - if tuple.out != fmt.Sprintf("%x", ours) { - t.Errorf("Failed old password %q", tuple.pass) - } - } -} - -func TestFormatBinaryDateTime(t *testing.T) { - rawDate := [11]byte{} - binary.LittleEndian.PutUint16(rawDate[:2], 1978) // years - rawDate[2] = 12 // months - rawDate[3] = 30 // days - rawDate[4] = 15 // hours - rawDate[5] = 46 // minutes - rawDate[6] = 23 // seconds - binary.LittleEndian.PutUint32(rawDate[7:], 987654) // microseconds - expect := func(expected string, inlen, outlen uint8) { - actual, _ := formatBinaryDateTime(rawDate[:inlen], outlen, false) - bytes, ok := actual.([]byte) - if !ok { - t.Errorf("formatBinaryDateTime must return []byte, was %T", actual) - } - if string(bytes) != expected { - t.Errorf( - "expected %q, got %q for length in %d, out %d", - bytes, actual, inlen, outlen, - ) - } - } - expect("0000-00-00", 0, 10) - expect("0000-00-00 00:00:00", 0, 19) - expect("1978-12-30", 4, 10) - expect("1978-12-30 15:46:23", 7, 19) - expect("1978-12-30 15:46:23.987654", 11, 26) -} - -func TestEscapeBackslash(t *testing.T) { - expect := func(expected, value string) { - actual := string(escapeBytesBackslash([]byte{}, []byte(value))) - if actual != expected { - t.Errorf( - "expected %s, got %s", - expected, actual, - ) - } - - actual = string(escapeStringBackslash([]byte{}, value)) - if actual != expected { - t.Errorf( - "expected %s, got %s", - expected, actual, - ) - } - } - - expect("foo\\0bar", "foo\x00bar") - expect("foo\\nbar", "foo\nbar") - expect("foo\\rbar", "foo\rbar") - expect("foo\\Zbar", "foo\x1abar") - expect("foo\\\"bar", "foo\"bar") - expect("foo\\\\bar", "foo\\bar") - expect("foo\\'bar", "foo'bar") -} - -func TestEscapeQuotes(t *testing.T) { - expect := func(expected, value string) { - actual := string(escapeBytesQuotes([]byte{}, []byte(value))) - if actual != expected { - t.Errorf( - "expected %s, got %s", - expected, actual, - ) - } - - actual = string(escapeStringQuotes([]byte{}, value)) - if actual != expected { - t.Errorf( - "expected %s, got %s", - expected, actual, - ) - } - } - - expect("foo\x00bar", "foo\x00bar") // not affected - expect("foo\nbar", "foo\nbar") // not affected - expect("foo\rbar", "foo\rbar") // not affected - expect("foo\x1abar", "foo\x1abar") // not affected - expect("foo''bar", "foo'bar") // affected - expect("foo\"bar", "foo\"bar") // not affected -} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore b/vendor/github.com/Sirupsen/logrus/.gitignore similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore rename to vendor/github.com/Sirupsen/logrus/.gitignore diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml b/vendor/github.com/Sirupsen/logrus/.travis.yml similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml rename to vendor/github.com/Sirupsen/logrus/.travis.yml diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/CHANGELOG.md b/vendor/github.com/Sirupsen/logrus/CHANGELOG.md similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/CHANGELOG.md rename to vendor/github.com/Sirupsen/logrus/CHANGELOG.md diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE b/vendor/github.com/Sirupsen/logrus/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE rename to vendor/github.com/Sirupsen/logrus/LICENSE diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md b/vendor/github.com/Sirupsen/logrus/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md rename to vendor/github.com/Sirupsen/logrus/README.md diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/doc.go b/vendor/github.com/Sirupsen/logrus/doc.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/doc.go rename to vendor/github.com/Sirupsen/logrus/doc.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go b/vendor/github.com/Sirupsen/logrus/entry.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go rename to vendor/github.com/Sirupsen/logrus/entry.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go b/vendor/github.com/Sirupsen/logrus/entry_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go rename to vendor/github.com/Sirupsen/logrus/entry_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go b/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go rename to vendor/github.com/Sirupsen/logrus/examples/basic/basic.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go b/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go rename to vendor/github.com/Sirupsen/logrus/examples/hook/hook.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go b/vendor/github.com/Sirupsen/logrus/exported.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go rename to vendor/github.com/Sirupsen/logrus/exported.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go b/vendor/github.com/Sirupsen/logrus/formatter.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go rename to vendor/github.com/Sirupsen/logrus/formatter.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go b/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go rename to vendor/github.com/Sirupsen/logrus/formatter_bench_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go b/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go rename to vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go b/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go rename to vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go b/vendor/github.com/Sirupsen/logrus/hook_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go rename to vendor/github.com/Sirupsen/logrus/hook_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go b/vendor/github.com/Sirupsen/logrus/hooks.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go rename to vendor/github.com/Sirupsen/logrus/hooks.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go b/vendor/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go rename to vendor/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go b/vendor/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go rename to vendor/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md b/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md rename to vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go b/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go rename to vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go b/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go rename to vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go b/vendor/github.com/Sirupsen/logrus/json_formatter.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go rename to vendor/github.com/Sirupsen/logrus/json_formatter.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter_test.go b/vendor/github.com/Sirupsen/logrus/json_formatter_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter_test.go rename to vendor/github.com/Sirupsen/logrus/json_formatter_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go b/vendor/github.com/Sirupsen/logrus/logger.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go rename to vendor/github.com/Sirupsen/logrus/logger.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go b/vendor/github.com/Sirupsen/logrus/logrus.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go rename to vendor/github.com/Sirupsen/logrus/logrus.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go b/vendor/github.com/Sirupsen/logrus/logrus_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go rename to vendor/github.com/Sirupsen/logrus/logrus_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_bsd.go b/vendor/github.com/Sirupsen/logrus/terminal_bsd.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_bsd.go rename to vendor/github.com/Sirupsen/logrus/terminal_bsd.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go b/vendor/github.com/Sirupsen/logrus/terminal_linux.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go rename to vendor/github.com/Sirupsen/logrus/terminal_linux.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go b/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go rename to vendor/github.com/Sirupsen/logrus/terminal_notwindows.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go b/vendor/github.com/Sirupsen/logrus/terminal_windows.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go rename to vendor/github.com/Sirupsen/logrus/terminal_windows.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go b/vendor/github.com/Sirupsen/logrus/text_formatter.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go rename to vendor/github.com/Sirupsen/logrus/text_formatter.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter_test.go b/vendor/github.com/Sirupsen/logrus/text_formatter_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter_test.go rename to vendor/github.com/Sirupsen/logrus/text_formatter_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/writer.go b/vendor/github.com/Sirupsen/logrus/writer.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/writer.go rename to vendor/github.com/Sirupsen/logrus/writer.go diff --git a/Godeps/_workspace/src/github.com/Unknwon/goconfig/.gitignore b/vendor/github.com/Unknwon/goconfig/.gitignore similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/goconfig/.gitignore rename to vendor/github.com/Unknwon/goconfig/.gitignore diff --git a/Godeps/_workspace/src/github.com/Unknwon/goconfig/LICENSE b/vendor/github.com/Unknwon/goconfig/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/goconfig/LICENSE rename to vendor/github.com/Unknwon/goconfig/LICENSE diff --git a/Godeps/_workspace/src/github.com/Unknwon/goconfig/README.md b/vendor/github.com/Unknwon/goconfig/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/goconfig/README.md rename to vendor/github.com/Unknwon/goconfig/README.md diff --git a/Godeps/_workspace/src/github.com/Unknwon/goconfig/README_ZH.md b/vendor/github.com/Unknwon/goconfig/README_ZH.md similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/goconfig/README_ZH.md rename to vendor/github.com/Unknwon/goconfig/README_ZH.md diff --git a/Godeps/_workspace/src/github.com/Unknwon/goconfig/conf.go b/vendor/github.com/Unknwon/goconfig/conf.go similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/goconfig/conf.go rename to vendor/github.com/Unknwon/goconfig/conf.go diff --git a/Godeps/_workspace/src/github.com/Unknwon/goconfig/goconfig_test.go b/vendor/github.com/Unknwon/goconfig/goconfig_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/goconfig/goconfig_test.go rename to vendor/github.com/Unknwon/goconfig/goconfig_test.go diff --git a/Godeps/_workspace/src/github.com/Unknwon/goconfig/read.go b/vendor/github.com/Unknwon/goconfig/read.go similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/goconfig/read.go rename to vendor/github.com/Unknwon/goconfig/read.go diff --git a/Godeps/_workspace/src/github.com/Unknwon/goconfig/testdata/conf.ini b/vendor/github.com/Unknwon/goconfig/testdata/conf.ini similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/goconfig/testdata/conf.ini rename to vendor/github.com/Unknwon/goconfig/testdata/conf.ini diff --git a/Godeps/_workspace/src/github.com/Unknwon/goconfig/testdata/conf2.ini b/vendor/github.com/Unknwon/goconfig/testdata/conf2.ini similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/goconfig/testdata/conf2.ini rename to vendor/github.com/Unknwon/goconfig/testdata/conf2.ini diff --git a/Godeps/_workspace/src/github.com/Unknwon/goconfig/testdata/conf_test.ini b/vendor/github.com/Unknwon/goconfig/testdata/conf_test.ini similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/goconfig/testdata/conf_test.ini rename to vendor/github.com/Unknwon/goconfig/testdata/conf_test.ini diff --git a/Godeps/_workspace/src/github.com/Unknwon/goconfig/write.go b/vendor/github.com/Unknwon/goconfig/write.go similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/goconfig/write.go rename to vendor/github.com/Unknwon/goconfig/write.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/.gitignore b/vendor/github.com/astaxie/beego/.gitignore similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/.gitignore rename to vendor/github.com/astaxie/beego/.gitignore diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/.travis.yml b/vendor/github.com/astaxie/beego/.travis.yml similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/.travis.yml rename to vendor/github.com/astaxie/beego/.travis.yml diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/CONTRIBUTING.md b/vendor/github.com/astaxie/beego/CONTRIBUTING.md similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/CONTRIBUTING.md rename to vendor/github.com/astaxie/beego/CONTRIBUTING.md diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/LICENSE b/vendor/github.com/astaxie/beego/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/LICENSE rename to vendor/github.com/astaxie/beego/LICENSE diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/README.md b/vendor/github.com/astaxie/beego/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/README.md rename to vendor/github.com/astaxie/beego/README.md diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/admin.go b/vendor/github.com/astaxie/beego/admin.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/admin.go rename to vendor/github.com/astaxie/beego/admin.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/adminui.go b/vendor/github.com/astaxie/beego/adminui.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/adminui.go rename to vendor/github.com/astaxie/beego/adminui.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/app.go b/vendor/github.com/astaxie/beego/app.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/app.go rename to vendor/github.com/astaxie/beego/app.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/beego.go b/vendor/github.com/astaxie/beego/beego.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/beego.go rename to vendor/github.com/astaxie/beego/beego.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/cache/README.md b/vendor/github.com/astaxie/beego/cache/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/cache/README.md rename to vendor/github.com/astaxie/beego/cache/README.md diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/cache/cache.go b/vendor/github.com/astaxie/beego/cache/cache.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/cache/cache.go rename to vendor/github.com/astaxie/beego/cache/cache.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/cache/conv.go b/vendor/github.com/astaxie/beego/cache/conv.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/cache/conv.go rename to vendor/github.com/astaxie/beego/cache/conv.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/cache/file.go b/vendor/github.com/astaxie/beego/cache/file.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/cache/file.go rename to vendor/github.com/astaxie/beego/cache/file.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/cache/memcache/memcache.go b/vendor/github.com/astaxie/beego/cache/memcache/memcache.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/cache/memcache/memcache.go rename to vendor/github.com/astaxie/beego/cache/memcache/memcache.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/cache/memory.go b/vendor/github.com/astaxie/beego/cache/memory.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/cache/memory.go rename to vendor/github.com/astaxie/beego/cache/memory.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/cache/redis/redis.go b/vendor/github.com/astaxie/beego/cache/redis/redis.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/cache/redis/redis.go rename to vendor/github.com/astaxie/beego/cache/redis/redis.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/config.go b/vendor/github.com/astaxie/beego/config.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/config.go rename to vendor/github.com/astaxie/beego/config.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/config/config.go b/vendor/github.com/astaxie/beego/config/config.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/config/config.go rename to vendor/github.com/astaxie/beego/config/config.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/config/fake.go b/vendor/github.com/astaxie/beego/config/fake.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/config/fake.go rename to vendor/github.com/astaxie/beego/config/fake.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/config/ini.go b/vendor/github.com/astaxie/beego/config/ini.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/config/ini.go rename to vendor/github.com/astaxie/beego/config/ini.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/config/json.go b/vendor/github.com/astaxie/beego/config/json.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/config/json.go rename to vendor/github.com/astaxie/beego/config/json.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/config/xml/xml.go b/vendor/github.com/astaxie/beego/config/xml/xml.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/config/xml/xml.go rename to vendor/github.com/astaxie/beego/config/xml/xml.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/config/yaml/yaml.go b/vendor/github.com/astaxie/beego/config/yaml/yaml.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/config/yaml/yaml.go rename to vendor/github.com/astaxie/beego/config/yaml/yaml.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/context/acceptencoder.go b/vendor/github.com/astaxie/beego/context/acceptencoder.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/context/acceptencoder.go rename to vendor/github.com/astaxie/beego/context/acceptencoder.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/context/context.go b/vendor/github.com/astaxie/beego/context/context.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/context/context.go rename to vendor/github.com/astaxie/beego/context/context.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/context/input.go b/vendor/github.com/astaxie/beego/context/input.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/context/input.go rename to vendor/github.com/astaxie/beego/context/input.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/context/output.go b/vendor/github.com/astaxie/beego/context/output.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/context/output.go rename to vendor/github.com/astaxie/beego/context/output.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/controller.go b/vendor/github.com/astaxie/beego/controller.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/controller.go rename to vendor/github.com/astaxie/beego/controller.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/doc.go b/vendor/github.com/astaxie/beego/doc.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/doc.go rename to vendor/github.com/astaxie/beego/doc.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/docs.go b/vendor/github.com/astaxie/beego/docs.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/docs.go rename to vendor/github.com/astaxie/beego/docs.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/error.go b/vendor/github.com/astaxie/beego/error.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/error.go rename to vendor/github.com/astaxie/beego/error.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/filter.go b/vendor/github.com/astaxie/beego/filter.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/filter.go rename to vendor/github.com/astaxie/beego/filter.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/flash.go b/vendor/github.com/astaxie/beego/flash.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/flash.go rename to vendor/github.com/astaxie/beego/flash.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/grace/conn.go b/vendor/github.com/astaxie/beego/grace/conn.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/grace/conn.go rename to vendor/github.com/astaxie/beego/grace/conn.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/grace/grace.go b/vendor/github.com/astaxie/beego/grace/grace.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/grace/grace.go rename to vendor/github.com/astaxie/beego/grace/grace.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/grace/listener.go b/vendor/github.com/astaxie/beego/grace/listener.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/grace/listener.go rename to vendor/github.com/astaxie/beego/grace/listener.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/grace/server.go b/vendor/github.com/astaxie/beego/grace/server.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/grace/server.go rename to vendor/github.com/astaxie/beego/grace/server.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/hooks.go b/vendor/github.com/astaxie/beego/hooks.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/hooks.go rename to vendor/github.com/astaxie/beego/hooks.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/httplib/README.md b/vendor/github.com/astaxie/beego/httplib/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/httplib/README.md rename to vendor/github.com/astaxie/beego/httplib/README.md diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/httplib/httplib.go b/vendor/github.com/astaxie/beego/httplib/httplib.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/httplib/httplib.go rename to vendor/github.com/astaxie/beego/httplib/httplib.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/log.go b/vendor/github.com/astaxie/beego/log.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/log.go rename to vendor/github.com/astaxie/beego/log.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/logs/README.md b/vendor/github.com/astaxie/beego/logs/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/logs/README.md rename to vendor/github.com/astaxie/beego/logs/README.md diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/logs/conn.go b/vendor/github.com/astaxie/beego/logs/conn.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/logs/conn.go rename to vendor/github.com/astaxie/beego/logs/conn.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/logs/console.go b/vendor/github.com/astaxie/beego/logs/console.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/logs/console.go rename to vendor/github.com/astaxie/beego/logs/console.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/logs/es/es.go b/vendor/github.com/astaxie/beego/logs/es/es.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/logs/es/es.go rename to vendor/github.com/astaxie/beego/logs/es/es.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/logs/file.go b/vendor/github.com/astaxie/beego/logs/file.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/logs/file.go rename to vendor/github.com/astaxie/beego/logs/file.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/logs/log.go b/vendor/github.com/astaxie/beego/logs/log.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/logs/log.go rename to vendor/github.com/astaxie/beego/logs/log.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/logs/smtp.go b/vendor/github.com/astaxie/beego/logs/smtp.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/logs/smtp.go rename to vendor/github.com/astaxie/beego/logs/smtp.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/migration/ddl.go b/vendor/github.com/astaxie/beego/migration/ddl.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/migration/ddl.go rename to vendor/github.com/astaxie/beego/migration/ddl.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/migration/migration.go b/vendor/github.com/astaxie/beego/migration/migration.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/migration/migration.go rename to vendor/github.com/astaxie/beego/migration/migration.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/mime.go b/vendor/github.com/astaxie/beego/mime.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/mime.go rename to vendor/github.com/astaxie/beego/mime.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/namespace.go b/vendor/github.com/astaxie/beego/namespace.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/namespace.go rename to vendor/github.com/astaxie/beego/namespace.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/README.md b/vendor/github.com/astaxie/beego/orm/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/README.md rename to vendor/github.com/astaxie/beego/orm/README.md diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/cmd.go b/vendor/github.com/astaxie/beego/orm/cmd.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/cmd.go rename to vendor/github.com/astaxie/beego/orm/cmd.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/cmd_utils.go b/vendor/github.com/astaxie/beego/orm/cmd_utils.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/cmd_utils.go rename to vendor/github.com/astaxie/beego/orm/cmd_utils.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/db.go b/vendor/github.com/astaxie/beego/orm/db.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/db.go rename to vendor/github.com/astaxie/beego/orm/db.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/db_alias.go b/vendor/github.com/astaxie/beego/orm/db_alias.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/db_alias.go rename to vendor/github.com/astaxie/beego/orm/db_alias.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/db_mysql.go b/vendor/github.com/astaxie/beego/orm/db_mysql.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/db_mysql.go rename to vendor/github.com/astaxie/beego/orm/db_mysql.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/db_oracle.go b/vendor/github.com/astaxie/beego/orm/db_oracle.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/db_oracle.go rename to vendor/github.com/astaxie/beego/orm/db_oracle.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/db_postgres.go b/vendor/github.com/astaxie/beego/orm/db_postgres.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/db_postgres.go rename to vendor/github.com/astaxie/beego/orm/db_postgres.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/db_sqlite.go b/vendor/github.com/astaxie/beego/orm/db_sqlite.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/db_sqlite.go rename to vendor/github.com/astaxie/beego/orm/db_sqlite.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/db_tables.go b/vendor/github.com/astaxie/beego/orm/db_tables.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/db_tables.go rename to vendor/github.com/astaxie/beego/orm/db_tables.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/db_tidb.go b/vendor/github.com/astaxie/beego/orm/db_tidb.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/db_tidb.go rename to vendor/github.com/astaxie/beego/orm/db_tidb.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/db_utils.go b/vendor/github.com/astaxie/beego/orm/db_utils.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/db_utils.go rename to vendor/github.com/astaxie/beego/orm/db_utils.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/models.go b/vendor/github.com/astaxie/beego/orm/models.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/models.go rename to vendor/github.com/astaxie/beego/orm/models.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/models_boot.go b/vendor/github.com/astaxie/beego/orm/models_boot.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/models_boot.go rename to vendor/github.com/astaxie/beego/orm/models_boot.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/models_fields.go b/vendor/github.com/astaxie/beego/orm/models_fields.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/models_fields.go rename to vendor/github.com/astaxie/beego/orm/models_fields.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/models_info_f.go b/vendor/github.com/astaxie/beego/orm/models_info_f.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/models_info_f.go rename to vendor/github.com/astaxie/beego/orm/models_info_f.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/models_info_m.go b/vendor/github.com/astaxie/beego/orm/models_info_m.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/models_info_m.go rename to vendor/github.com/astaxie/beego/orm/models_info_m.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/models_utils.go b/vendor/github.com/astaxie/beego/orm/models_utils.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/models_utils.go rename to vendor/github.com/astaxie/beego/orm/models_utils.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/orm.go b/vendor/github.com/astaxie/beego/orm/orm.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/orm.go rename to vendor/github.com/astaxie/beego/orm/orm.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_conds.go b/vendor/github.com/astaxie/beego/orm/orm_conds.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_conds.go rename to vendor/github.com/astaxie/beego/orm/orm_conds.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_log.go b/vendor/github.com/astaxie/beego/orm/orm_log.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_log.go rename to vendor/github.com/astaxie/beego/orm/orm_log.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_object.go b/vendor/github.com/astaxie/beego/orm/orm_object.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_object.go rename to vendor/github.com/astaxie/beego/orm/orm_object.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_querym2m.go b/vendor/github.com/astaxie/beego/orm/orm_querym2m.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_querym2m.go rename to vendor/github.com/astaxie/beego/orm/orm_querym2m.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_queryset.go b/vendor/github.com/astaxie/beego/orm/orm_queryset.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_queryset.go rename to vendor/github.com/astaxie/beego/orm/orm_queryset.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_raw.go b/vendor/github.com/astaxie/beego/orm/orm_raw.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/orm_raw.go rename to vendor/github.com/astaxie/beego/orm/orm_raw.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/qb.go b/vendor/github.com/astaxie/beego/orm/qb.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/qb.go rename to vendor/github.com/astaxie/beego/orm/qb.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/qb_mysql.go b/vendor/github.com/astaxie/beego/orm/qb_mysql.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/qb_mysql.go rename to vendor/github.com/astaxie/beego/orm/qb_mysql.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/qb_tidb.go b/vendor/github.com/astaxie/beego/orm/qb_tidb.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/qb_tidb.go rename to vendor/github.com/astaxie/beego/orm/qb_tidb.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/types.go b/vendor/github.com/astaxie/beego/orm/types.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/types.go rename to vendor/github.com/astaxie/beego/orm/types.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/orm/utils.go b/vendor/github.com/astaxie/beego/orm/utils.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/orm/utils.go rename to vendor/github.com/astaxie/beego/orm/utils.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/parser.go b/vendor/github.com/astaxie/beego/parser.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/parser.go rename to vendor/github.com/astaxie/beego/parser.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/plugins/apiauth/apiauth.go b/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/plugins/apiauth/apiauth.go rename to vendor/github.com/astaxie/beego/plugins/apiauth/apiauth.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/plugins/auth/basic.go b/vendor/github.com/astaxie/beego/plugins/auth/basic.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/plugins/auth/basic.go rename to vendor/github.com/astaxie/beego/plugins/auth/basic.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/plugins/cors/cors.go b/vendor/github.com/astaxie/beego/plugins/cors/cors.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/plugins/cors/cors.go rename to vendor/github.com/astaxie/beego/plugins/cors/cors.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/router.go b/vendor/github.com/astaxie/beego/router.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/router.go rename to vendor/github.com/astaxie/beego/router.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/README.md b/vendor/github.com/astaxie/beego/session/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/README.md rename to vendor/github.com/astaxie/beego/session/README.md diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/couchbase/sess_couchbase.go b/vendor/github.com/astaxie/beego/session/couchbase/sess_couchbase.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/couchbase/sess_couchbase.go rename to vendor/github.com/astaxie/beego/session/couchbase/sess_couchbase.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/ledis/ledis_session.go b/vendor/github.com/astaxie/beego/session/ledis/ledis_session.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/ledis/ledis_session.go rename to vendor/github.com/astaxie/beego/session/ledis/ledis_session.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/memcache/sess_memcache.go b/vendor/github.com/astaxie/beego/session/memcache/sess_memcache.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/memcache/sess_memcache.go rename to vendor/github.com/astaxie/beego/session/memcache/sess_memcache.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/mysql/sess_mysql.go b/vendor/github.com/astaxie/beego/session/mysql/sess_mysql.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/mysql/sess_mysql.go rename to vendor/github.com/astaxie/beego/session/mysql/sess_mysql.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/postgres/sess_postgresql.go b/vendor/github.com/astaxie/beego/session/postgres/sess_postgresql.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/postgres/sess_postgresql.go rename to vendor/github.com/astaxie/beego/session/postgres/sess_postgresql.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/redis/sess_redis.go b/vendor/github.com/astaxie/beego/session/redis/sess_redis.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/redis/sess_redis.go rename to vendor/github.com/astaxie/beego/session/redis/sess_redis.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/sess_cookie.go b/vendor/github.com/astaxie/beego/session/sess_cookie.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/sess_cookie.go rename to vendor/github.com/astaxie/beego/session/sess_cookie.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/sess_file.go b/vendor/github.com/astaxie/beego/session/sess_file.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/sess_file.go rename to vendor/github.com/astaxie/beego/session/sess_file.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/sess_mem.go b/vendor/github.com/astaxie/beego/session/sess_mem.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/sess_mem.go rename to vendor/github.com/astaxie/beego/session/sess_mem.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/sess_utils.go b/vendor/github.com/astaxie/beego/session/sess_utils.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/sess_utils.go rename to vendor/github.com/astaxie/beego/session/sess_utils.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/session/session.go b/vendor/github.com/astaxie/beego/session/session.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/session/session.go rename to vendor/github.com/astaxie/beego/session/session.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/staticfile.go b/vendor/github.com/astaxie/beego/staticfile.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/staticfile.go rename to vendor/github.com/astaxie/beego/staticfile.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/swagger/docs_spec.go b/vendor/github.com/astaxie/beego/swagger/docs_spec.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/swagger/docs_spec.go rename to vendor/github.com/astaxie/beego/swagger/docs_spec.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/template.go b/vendor/github.com/astaxie/beego/template.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/template.go rename to vendor/github.com/astaxie/beego/template.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/templatefunc.go b/vendor/github.com/astaxie/beego/templatefunc.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/templatefunc.go rename to vendor/github.com/astaxie/beego/templatefunc.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/testing/assertions.go b/vendor/github.com/astaxie/beego/testing/assertions.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/testing/assertions.go rename to vendor/github.com/astaxie/beego/testing/assertions.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/testing/client.go b/vendor/github.com/astaxie/beego/testing/client.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/testing/client.go rename to vendor/github.com/astaxie/beego/testing/client.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/toolbox/healthcheck.go b/vendor/github.com/astaxie/beego/toolbox/healthcheck.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/toolbox/healthcheck.go rename to vendor/github.com/astaxie/beego/toolbox/healthcheck.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/toolbox/profile.go b/vendor/github.com/astaxie/beego/toolbox/profile.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/toolbox/profile.go rename to vendor/github.com/astaxie/beego/toolbox/profile.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/toolbox/statistics.go b/vendor/github.com/astaxie/beego/toolbox/statistics.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/toolbox/statistics.go rename to vendor/github.com/astaxie/beego/toolbox/statistics.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/toolbox/task.go b/vendor/github.com/astaxie/beego/toolbox/task.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/toolbox/task.go rename to vendor/github.com/astaxie/beego/toolbox/task.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/tree.go b/vendor/github.com/astaxie/beego/tree.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/tree.go rename to vendor/github.com/astaxie/beego/tree.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/caller.go b/vendor/github.com/astaxie/beego/utils/caller.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/caller.go rename to vendor/github.com/astaxie/beego/utils/caller.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/LICENSE b/vendor/github.com/astaxie/beego/utils/captcha/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/LICENSE rename to vendor/github.com/astaxie/beego/utils/captcha/LICENSE diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/README.md b/vendor/github.com/astaxie/beego/utils/captcha/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/README.md rename to vendor/github.com/astaxie/beego/utils/captcha/README.md diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/captcha.go b/vendor/github.com/astaxie/beego/utils/captcha/captcha.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/captcha.go rename to vendor/github.com/astaxie/beego/utils/captcha/captcha.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/image.go b/vendor/github.com/astaxie/beego/utils/captcha/image.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/image.go rename to vendor/github.com/astaxie/beego/utils/captcha/image.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/siprng.go b/vendor/github.com/astaxie/beego/utils/captcha/siprng.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/siprng.go rename to vendor/github.com/astaxie/beego/utils/captcha/siprng.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/debug.go b/vendor/github.com/astaxie/beego/utils/debug.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/debug.go rename to vendor/github.com/astaxie/beego/utils/debug.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/file.go b/vendor/github.com/astaxie/beego/utils/file.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/file.go rename to vendor/github.com/astaxie/beego/utils/file.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/mail.go b/vendor/github.com/astaxie/beego/utils/mail.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/mail.go rename to vendor/github.com/astaxie/beego/utils/mail.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/pagination/controller.go b/vendor/github.com/astaxie/beego/utils/pagination/controller.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/pagination/controller.go rename to vendor/github.com/astaxie/beego/utils/pagination/controller.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/pagination/doc.go b/vendor/github.com/astaxie/beego/utils/pagination/doc.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/pagination/doc.go rename to vendor/github.com/astaxie/beego/utils/pagination/doc.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/pagination/paginator.go b/vendor/github.com/astaxie/beego/utils/pagination/paginator.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/pagination/paginator.go rename to vendor/github.com/astaxie/beego/utils/pagination/paginator.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/pagination/utils.go b/vendor/github.com/astaxie/beego/utils/pagination/utils.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/pagination/utils.go rename to vendor/github.com/astaxie/beego/utils/pagination/utils.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/rand.go b/vendor/github.com/astaxie/beego/utils/rand.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/rand.go rename to vendor/github.com/astaxie/beego/utils/rand.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/safemap.go b/vendor/github.com/astaxie/beego/utils/safemap.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/safemap.go rename to vendor/github.com/astaxie/beego/utils/safemap.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/utils/slice.go b/vendor/github.com/astaxie/beego/utils/slice.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/utils/slice.go rename to vendor/github.com/astaxie/beego/utils/slice.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/validation/README.md b/vendor/github.com/astaxie/beego/validation/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/validation/README.md rename to vendor/github.com/astaxie/beego/validation/README.md diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/validation/util.go b/vendor/github.com/astaxie/beego/validation/util.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/validation/util.go rename to vendor/github.com/astaxie/beego/validation/util.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/validation/validation.go b/vendor/github.com/astaxie/beego/validation/validation.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/validation/validation.go rename to vendor/github.com/astaxie/beego/validation/validation.go diff --git a/Godeps/_workspace/src/github.com/astaxie/beego/validation/validators.go b/vendor/github.com/astaxie/beego/validation/validators.go similarity index 100% rename from Godeps/_workspace/src/github.com/astaxie/beego/validation/validators.go rename to vendor/github.com/astaxie/beego/validation/validators.go diff --git a/Godeps/_workspace/src/github.com/beego/i18n/.gitignore b/vendor/github.com/beego/i18n/.gitignore similarity index 100% rename from Godeps/_workspace/src/github.com/beego/i18n/.gitignore rename to vendor/github.com/beego/i18n/.gitignore diff --git a/Godeps/_workspace/src/github.com/beego/i18n/LICENSE b/vendor/github.com/beego/i18n/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/beego/i18n/LICENSE rename to vendor/github.com/beego/i18n/LICENSE diff --git a/Godeps/_workspace/src/github.com/beego/i18n/README.md b/vendor/github.com/beego/i18n/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/beego/i18n/README.md rename to vendor/github.com/beego/i18n/README.md diff --git a/Godeps/_workspace/src/github.com/beego/i18n/beei18n/beei18n.go b/vendor/github.com/beego/i18n/beei18n/beei18n.go similarity index 100% rename from Godeps/_workspace/src/github.com/beego/i18n/beei18n/beei18n.go rename to vendor/github.com/beego/i18n/beei18n/beei18n.go diff --git a/Godeps/_workspace/src/github.com/beego/i18n/beei18n/sync.go b/vendor/github.com/beego/i18n/beei18n/sync.go similarity index 100% rename from Godeps/_workspace/src/github.com/beego/i18n/beei18n/sync.go rename to vendor/github.com/beego/i18n/beei18n/sync.go diff --git a/Godeps/_workspace/src/github.com/beego/i18n/i18n.go b/vendor/github.com/beego/i18n/i18n.go similarity index 100% rename from Godeps/_workspace/src/github.com/beego/i18n/i18n.go rename to vendor/github.com/beego/i18n/i18n.go diff --git a/Godeps/_workspace/src/github.com/gorilla/context/.travis.yml b/vendor/github.com/gorilla/context/.travis.yml similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/context/.travis.yml rename to vendor/github.com/gorilla/context/.travis.yml diff --git a/Godeps/_workspace/src/github.com/gorilla/context/LICENSE b/vendor/github.com/gorilla/context/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/context/LICENSE rename to vendor/github.com/gorilla/context/LICENSE diff --git a/Godeps/_workspace/src/github.com/gorilla/context/README.md b/vendor/github.com/gorilla/context/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/context/README.md rename to vendor/github.com/gorilla/context/README.md diff --git a/Godeps/_workspace/src/github.com/gorilla/context/context.go b/vendor/github.com/gorilla/context/context.go similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/context/context.go rename to vendor/github.com/gorilla/context/context.go diff --git a/Godeps/_workspace/src/github.com/gorilla/context/context_test.go b/vendor/github.com/gorilla/context/context_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/context/context_test.go rename to vendor/github.com/gorilla/context/context_test.go diff --git a/Godeps/_workspace/src/github.com/gorilla/context/doc.go b/vendor/github.com/gorilla/context/doc.go similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/context/doc.go rename to vendor/github.com/gorilla/context/doc.go diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml b/vendor/github.com/gorilla/mux/.travis.yml similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml rename to vendor/github.com/gorilla/mux/.travis.yml diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/LICENSE b/vendor/github.com/gorilla/mux/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/mux/LICENSE rename to vendor/github.com/gorilla/mux/LICENSE diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/README.md b/vendor/github.com/gorilla/mux/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/mux/README.md rename to vendor/github.com/gorilla/mux/README.md diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/bench_test.go b/vendor/github.com/gorilla/mux/bench_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/mux/bench_test.go rename to vendor/github.com/gorilla/mux/bench_test.go diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/doc.go b/vendor/github.com/gorilla/mux/doc.go similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/mux/doc.go rename to vendor/github.com/gorilla/mux/doc.go diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/mux/mux.go rename to vendor/github.com/gorilla/mux/mux.go diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/mux_test.go b/vendor/github.com/gorilla/mux/mux_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/mux/mux_test.go rename to vendor/github.com/gorilla/mux/mux_test.go diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/old_test.go b/vendor/github.com/gorilla/mux/old_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/mux/old_test.go rename to vendor/github.com/gorilla/mux/old_test.go diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/mux/regexp.go rename to vendor/github.com/gorilla/mux/regexp.go diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go similarity index 100% rename from Godeps/_workspace/src/github.com/gorilla/mux/route.go rename to vendor/github.com/gorilla/mux/route.go diff --git a/Godeps/_workspace/src/github.com/mqu/openldap/LICENCE.txt b/vendor/github.com/mqu/openldap/LICENCE.txt similarity index 100% rename from Godeps/_workspace/src/github.com/mqu/openldap/LICENCE.txt rename to vendor/github.com/mqu/openldap/LICENCE.txt diff --git a/Godeps/_workspace/src/github.com/mqu/openldap/README.md b/vendor/github.com/mqu/openldap/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/mqu/openldap/README.md rename to vendor/github.com/mqu/openldap/README.md diff --git a/Godeps/_workspace/src/github.com/mqu/openldap/add-modify-delete.go b/vendor/github.com/mqu/openldap/add-modify-delete.go similarity index 100% rename from Godeps/_workspace/src/github.com/mqu/openldap/add-modify-delete.go rename to vendor/github.com/mqu/openldap/add-modify-delete.go diff --git a/Godeps/_workspace/src/github.com/mqu/openldap/defines.go b/vendor/github.com/mqu/openldap/defines.go similarity index 100% rename from Godeps/_workspace/src/github.com/mqu/openldap/defines.go rename to vendor/github.com/mqu/openldap/defines.go diff --git a/Godeps/_workspace/src/github.com/mqu/openldap/openldap.go b/vendor/github.com/mqu/openldap/openldap.go similarity index 100% rename from Godeps/_workspace/src/github.com/mqu/openldap/openldap.go rename to vendor/github.com/mqu/openldap/openldap.go diff --git a/Godeps/_workspace/src/github.com/mqu/openldap/options-errors.go b/vendor/github.com/mqu/openldap/options-errors.go similarity index 100% rename from Godeps/_workspace/src/github.com/mqu/openldap/options-errors.go rename to vendor/github.com/mqu/openldap/options-errors.go diff --git a/Godeps/_workspace/src/github.com/mqu/openldap/results.go b/vendor/github.com/mqu/openldap/results.go similarity index 100% rename from Godeps/_workspace/src/github.com/mqu/openldap/results.go rename to vendor/github.com/mqu/openldap/results.go diff --git a/Godeps/_workspace/src/github.com/mqu/openldap/types.go b/vendor/github.com/mqu/openldap/types.go similarity index 100% rename from Godeps/_workspace/src/github.com/mqu/openldap/types.go rename to vendor/github.com/mqu/openldap/types.go diff --git a/Godeps/_workspace/src/golang.org/x/blog/LICENSE b/vendor/golang.org/x/blog/LICENSE similarity index 100% rename from Godeps/_workspace/src/golang.org/x/blog/LICENSE rename to vendor/golang.org/x/blog/LICENSE diff --git a/Godeps/_workspace/src/golang.org/x/blog/PATENTS b/vendor/golang.org/x/blog/PATENTS similarity index 100% rename from Godeps/_workspace/src/golang.org/x/blog/PATENTS rename to vendor/golang.org/x/blog/PATENTS diff --git a/Godeps/_workspace/src/golang.org/x/blog/content/third-party-libraries-goprotobuf-and.article b/vendor/golang.org/x/blog/content/third-party-libraries-goprotobuf-and.article similarity index 100% rename from Godeps/_workspace/src/golang.org/x/blog/content/third-party-libraries-goprotobuf-and.article rename to vendor/golang.org/x/blog/content/third-party-libraries-goprotobuf-and.article diff --git a/Godeps/_workspace/src/golang.org/x/crypto/LICENSE b/vendor/golang.org/x/crypto/LICENSE similarity index 100% rename from Godeps/_workspace/src/golang.org/x/crypto/LICENSE rename to vendor/golang.org/x/crypto/LICENSE diff --git a/Godeps/_workspace/src/golang.org/x/crypto/PATENTS b/vendor/golang.org/x/crypto/PATENTS similarity index 100% rename from Godeps/_workspace/src/golang.org/x/crypto/PATENTS rename to vendor/golang.org/x/crypto/PATENTS diff --git a/Godeps/_workspace/src/golang.org/x/crypto/pbkdf2/pbkdf2.go b/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go similarity index 100% rename from Godeps/_workspace/src/golang.org/x/crypto/pbkdf2/pbkdf2.go rename to vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go diff --git a/Godeps/_workspace/src/golang.org/x/exp/LICENSE b/vendor/golang.org/x/exp/LICENSE similarity index 100% rename from Godeps/_workspace/src/golang.org/x/exp/LICENSE rename to vendor/golang.org/x/exp/LICENSE diff --git a/Godeps/_workspace/src/golang.org/x/exp/PATENTS b/vendor/golang.org/x/exp/PATENTS similarity index 100% rename from Godeps/_workspace/src/golang.org/x/exp/PATENTS rename to vendor/golang.org/x/exp/PATENTS diff --git a/Godeps/_workspace/src/golang.org/x/image/LICENSE b/vendor/golang.org/x/image/LICENSE similarity index 100% rename from Godeps/_workspace/src/golang.org/x/image/LICENSE rename to vendor/golang.org/x/image/LICENSE diff --git a/Godeps/_workspace/src/golang.org/x/image/PATENTS b/vendor/golang.org/x/image/PATENTS similarity index 100% rename from Godeps/_workspace/src/golang.org/x/image/PATENTS rename to vendor/golang.org/x/image/PATENTS diff --git a/Godeps/_workspace/src/golang.org/x/mobile/LICENSE b/vendor/golang.org/x/mobile/LICENSE similarity index 100% rename from Godeps/_workspace/src/golang.org/x/mobile/LICENSE rename to vendor/golang.org/x/mobile/LICENSE diff --git a/Godeps/_workspace/src/golang.org/x/mobile/PATENTS b/vendor/golang.org/x/mobile/PATENTS similarity index 100% rename from Godeps/_workspace/src/golang.org/x/mobile/PATENTS rename to vendor/golang.org/x/mobile/PATENTS diff --git a/Godeps/_workspace/src/golang.org/x/net/LICENSE b/vendor/golang.org/x/net/LICENSE similarity index 100% rename from Godeps/_workspace/src/golang.org/x/net/LICENSE rename to vendor/golang.org/x/net/LICENSE diff --git a/Godeps/_workspace/src/golang.org/x/net/PATENTS b/vendor/golang.org/x/net/PATENTS similarity index 100% rename from Godeps/_workspace/src/golang.org/x/net/PATENTS rename to vendor/golang.org/x/net/PATENTS diff --git a/Godeps/_workspace/src/golang.org/x/net/context/context.go b/vendor/golang.org/x/net/context/context.go similarity index 100% rename from Godeps/_workspace/src/golang.org/x/net/context/context.go rename to vendor/golang.org/x/net/context/context.go diff --git a/Godeps/_workspace/src/golang.org/x/sys/LICENSE b/vendor/golang.org/x/sys/LICENSE similarity index 100% rename from Godeps/_workspace/src/golang.org/x/sys/LICENSE rename to vendor/golang.org/x/sys/LICENSE diff --git a/Godeps/_workspace/src/golang.org/x/sys/PATENTS b/vendor/golang.org/x/sys/PATENTS similarity index 100% rename from Godeps/_workspace/src/golang.org/x/sys/PATENTS rename to vendor/golang.org/x/sys/PATENTS diff --git a/Godeps/_workspace/src/golang.org/x/talks/LICENSE b/vendor/golang.org/x/talks/LICENSE similarity index 100% rename from Godeps/_workspace/src/golang.org/x/talks/LICENSE rename to vendor/golang.org/x/talks/LICENSE diff --git a/Godeps/_workspace/src/golang.org/x/talks/PATENTS b/vendor/golang.org/x/talks/PATENTS similarity index 100% rename from Godeps/_workspace/src/golang.org/x/talks/PATENTS rename to vendor/golang.org/x/talks/PATENTS diff --git a/Godeps/_workspace/src/golang.org/x/text/LICENSE b/vendor/golang.org/x/text/LICENSE similarity index 100% rename from Godeps/_workspace/src/golang.org/x/text/LICENSE rename to vendor/golang.org/x/text/LICENSE diff --git a/Godeps/_workspace/src/golang.org/x/text/PATENTS b/vendor/golang.org/x/text/PATENTS similarity index 100% rename from Godeps/_workspace/src/golang.org/x/text/PATENTS rename to vendor/golang.org/x/text/PATENTS diff --git a/Godeps/_workspace/src/golang.org/x/tools/LICENSE b/vendor/golang.org/x/tools/LICENSE similarity index 100% rename from Godeps/_workspace/src/golang.org/x/tools/LICENSE rename to vendor/golang.org/x/tools/LICENSE diff --git a/Godeps/_workspace/src/golang.org/x/tools/PATENTS b/vendor/golang.org/x/tools/PATENTS similarity index 100% rename from Godeps/_workspace/src/golang.org/x/tools/PATENTS rename to vendor/golang.org/x/tools/PATENTS